求1到2之间的随机数。请大神赐教

迷失在代码里 2016-01-18 05:19:22
需求是这样的,比如 5000块钱,分给6000个人,但是每个人的金额上下限要求是 m>=1 && m<=2的随机数。

我现在的写法不能控制最后一个数的大小。

请问这个算法该如何实现。

求大神帮忙,不胜感激涕零。
...全文
2145 49 打赏 收藏 转发到动态 举报
写回复
用AI写文章
49 条回复
切换为时间正序
请发表友善的回复…
发表回复
迷失在代码里 2016-01-22
  • 打赏
  • 举报
回复
结贴了,首先感谢各位大神的踊跃讨论,提供不少好思路。 虽然暂时性的解决了,但还不是最好的方式。 贴如下代码,可以保证每个人拿的钱都在最小值 最大值之间,而且钱能刚好分完。 弊端就是分配的金额都在平均值周围徘徊,正如大神们所说的 必须要人为的去干涉,不然金额都太平均。

  
              //总人数
              int totalNum=5000;
              //总金额
             decimal totalAmount=6000;
              //最小值
             decimal minMoney=1;
             //最大值
             decimal maxMoney=2;
            //平均点范围
            var avgMoney = (maxMoney - minMoney) / 4;
            //分配金额
            decimal sendMoney = 0;

             int i = 0;
            while (i < totalNum)
            {
                //当前数量
                var iNum = totalNum - i;
                //平均值
                var avgAmount = totalAmount / iNum;


                //当最小值与当前数量的乘积等于当前总金额时,分配金额取最小值
                if (minMoney * iNum == totalAmount)
                {
                    sendMoney = minMoney;
                }
                //当最大值与当前数量的乘积等于当前总金额时,分配金额取最大值
                else if (maxMoney * (totalNum - i) == totalAmount)
                {
                    sendMoney = maxMoney;
                }
                else {
                    //最小值
                    var min = Math.Max(minMoney, avgAmount - avgMoney);
                    //最大值
                    var max = Math.Min(maxMoney, avgAmount + avgMoney);
                    //随机提取分配金额
                    var seed = Guid.NewGuid().GetHashCode() + i;
                    Random r = new Random(seed);
                    sendMoney = Convert.ToDecimal(r.Next(Convert.ToInt32(min * 100), Convert.ToInt32(max * 100))) / 100;
                }
                //减去当前分配金额后的总金额
                var minusAmount = totalAmount - sendMoney;

                if ((iNum - 1) > 0 && (minMoney * (iNum - 1)) <= minusAmount)
                {
                    if ((maxMoney * (iNum - 1)) < minusAmount)
                    {
                        //如果剩下的总金额大于实际最大分配金额,则将当前分配金额加上取余的数
                        sendMoney = sendMoney + minusAmount % (maxMoney * (iNum - 1));
                    }
                    //总金额减掉当前分配金额
                    totalAmount -= sendMoney;
                    i++;
                }
                //最后分配剩下的所有钱
                else if ((iNum - 1) == 0)
                {
                    sendMoney = totalAmount;
                    i++;
                }
            }
feiyun0112 2016-01-21
  • 打赏
  • 举报
回复
引用 44 楼 zixingcheng_ 的回复:
[quote=引用 43 楼 bjgzxx 的回复:] [quote=引用 35 楼 feiyun0112 的回复:] 6000块钱,5000个人 把1000块拿出来,每个人分0-1块,分完为止 剩下5000,每人一块
这个方法好剩下的1000元随机分配就行了[/quote] 一样的问题,假设4500人分配为0,就变成剩余500人1000块了,随机数嘛!所以必须人工干预,任何纯的随机数都应该没办法解决的。[/quote] 这个很好解决啊,没分完从头再来一遍,不会那么倒霉吧,5000人分不了1000块
cxdragoon 2016-01-21
  • 打赏
  • 举报
回复
前面那个会让只分到基数钱的人集中在后面,下面是对while循环的改进

while (totalMoney > 0)
            {
                int i = rnd.Next(0, counter);
                double money = rnd.Next(minValue, maxValue) / 100.0;
                money = totalMoney - money < 0 ? totalMoney : money;//如果余额不足,则仅分余额
                if (perMoney[i] + money <= maxMoney)//不能超过上限
                {
                    perMoney[i] += money;
                    totalMoney -= money;
                }
            }
i的值随机获得,不再需要初始值!如果希望分得再均匀一些可以将maxValue的值再调小一点!
cxdragoon 2016-01-21
  • 打赏
  • 举报
回复
有个思路,这个思路可能会有很多人只能分到基数的1元钱

static void Main(string[] args)
        {
            double totalMoney = 6000;//钱的总数
            int counter = 5000;//人数
            double baseMoney = 1.0;//每个人分到钱的最小值
            double maxMoney = 2.0;//每个人能分到钱的最大值
            System.Random rnd = new System.Random();
            double[] perMoney = new double[counter];
            //给每个人分配基数值的钱,同时调整钱的总数
            for(int j = 0; j < counter; j++)
            {
                perMoney[j] = baseMoney;
                totalMoney -= baseMoney;
            }
            //因为Random.Next产生的是整数,所以放大100倍
            int minValue = 0, maxValue = (int)((maxMoney - baseMoney) * 100);
            int i = 0;
            //分剩下的钱
            while (totalMoney > 0)
            {
                double money = rnd.Next(minValue, maxValue) / 100.0;
                money = totalMoney - money < 0 ? totalMoney : money;//如果余额不足,则仅分余额
                perMoney[i] += money;
                i++;
                totalMoney -= money;
                if(i>=counter && totalMoney>0) //如果每个人都分了还有剩余,则从头再把剩下的钱分了,也可以随机选一个人开始分
                {
                    minValue = 0;
                    maxValue = 100;
                    i = 0;
                }
            }

            foreach(double m in perMoney)
            {
                Console.Write(m.ToString());
                Console.Write("\t");
            }
摇撼大地 2016-01-21
  • 打赏
  • 举报
回复
首先不知道楼主要的结果对分布有没有要求。 第一种分法:(平均分布,基本都是平均值。分布不均匀,方差很小) 1.每人1块。 2.1000块每次分一分。随机给。但是每次给完都要计算是不是超过2(虽然基本上都是否定的。可以把这个运算放到最后。但是不能确定运算次数了)。这样的结果。基本上每个人都差不多。分布基本就是平均值。这样计算差不多1000*100次和1000*100次判断。 下面是平均分布(但是比较第一个方法均匀了很多,方差比较大) 其实题目要求的就是1000元分给5000个人。0-1元。再简化一下题目。就是1元分给5个人。均匀分布。 然后考虑1元怎么分给5个人。我们可以分200次这样。这样问题就简单了不少. 我的思路是。随机1-0.随机4次。如果超过1.就重新运算。小于1.最后一个数字减去前面4个的数字。加起来5个就是1元了。这样分布就很均匀了。但是并不是完全随机。他是5个人为一组。不过这个应该能满足楼主要求。而且运算次数一组应该也就20次左右。总体运算20*200。速度也挺快的。
zixingcheng_ 2016-01-20
  • 打赏
  • 举报
回复
引用 43 楼 bjgzxx 的回复:
[quote=引用 35 楼 feiyun0112 的回复:] 6000块钱,5000个人 把1000块拿出来,每个人分0-1块,分完为止 剩下5000,每人一块
这个方法好剩下的1000元随机分配就行了[/quote] 一样的问题,假设4500人分配为0,就变成剩余500人1000块了,随机数嘛!所以必须人工干预,任何纯的随机数都应该没办法解决的。
早起晚睡 2016-01-20
  • 打赏
  • 举报
回复
引用 35 楼 feiyun0112 的回复:
6000块钱,5000个人 把1000块拿出来,每个人分0-1块,分完为止 剩下5000,每人一块
这个方法好剩下的1000元随机分配就行了
瑞卡哥哥 2016-01-20
  • 打赏
  • 举报
回复

//上面的函数有bug   其实这个也有bug  主要是限制了最大值  就什么都不好说了
    private static int GetRedBag(ref int bag, ref int p)
        {
            var min = 100;
            var mid = bag/p;
            var max = 201;
            var max1 = mid*2 - min;
            max = max1 > max ? max : max1;
            if (bag / p == min && bag % p == 0)
            {
                bag -= (max - 1);
                p--;
                return min;
            }
            if (bag / p == max - 1 && bag % p==0)
            {
                bag -= (max - 1);
                p--;
                return max-1;
            }
            Random rn = new Random(Convert.ToInt32(Guid.NewGuid().ToString("N").Substring(0,5),16));
            var result = rn.Next(min, max);
            while (((bag - result) / p < min) || (bag - result) / p > max)
            {
                result = rn.Next(min, max);
            }
            bag -= result;
            p--;
            return result;
        }


瑞卡哥哥 2016-01-20
  • 打赏
  • 举报
回复


 static void Main(string[] args)
        {
     var bag = 600000;
            var p = 5000;
            for (int i = 0; i < p; i++)
            {
                var redbag = GetRedBag(ref bag, ref p);
                Console.Write(redbag+" ");
            }

            Console.ReadKey();
        }
//这样单纯随机数 会导致最后的红包分配不合理  下面在给出一种更合理的方式
 private static int GetRedBag(ref int bag, ref int p)
        {
            var min = 100;
            var max = 201;
            if (bag/p == min)
            {
                return min;
            }
            if (bag / p == max-1)
            {
                return max;
            }
            Random rn = new Random(Convert.ToInt32(Guid.NewGuid().ToString("N").Substring(0,5),16));
            var result = rn.Next(min, max);
            while ((bag - result) / p < min || (bag - result) / p > max)
            {
                result = rn.Next(min, max);
            }
            bag -= result;
            p--;
            return result;
        }

瑞卡哥哥 2016-01-20
  • 打赏
  • 举报
回复
5000块钱,分给6000个人 不可能分的每人大于1块钱的. 你说反了吧.. 6000块分给5000个人吧
zixingcheng_ 2016-01-20
  • 打赏
  • 举报
回复
这个问题的核心就是后面的人可能分配的大于2,导致分配失败。 所以可以从这个地方解决,随机数保证在1-2这个不变,但分配后需要验证剩余钱能否正常分配。 比如还有1001人,剩余金额2001,当前随机数0.5,则剩余可分配2000人,剩余金额2000.5,即平均分配没人均超过2元,分配必然失败,所以验证结果就要重新分配,保证此轮分配值必须大于1. 本轮分配的最小结果怎么算已经很清楚了。 主要每轮的分配能满足其最小分配值则无需重新分配。 如上例如果分配正好1元,则剩余分配必然满足均为2元。好吧,这几率也不是不可能的啦(数值范围太小)。 当然这样的方式不是最好的,合理的应该使用线性的方式,干扰分配结果,以使得随机数据的分散的更平滑。
本拉灯 2016-01-19
  • 打赏
  • 举报
回复
double money = 6000; int pick = 5000; double rndMoney = money - pick * 1; //所有人最低值,剩下的额度用来随机 if (rndMoney < 0) throw new Exception("额度不足"); while (true) { double reMoney = 1; double r = 0; if (pick > 1 && rndMoney > 0) { double max = rndMoney; r = rnd.Next(0, 101)/100.0; rndMoney -= r; } else { r = rndMoney; rndMoney = 0; } reMoney = reMoney + r; pick--; Debug.WriteLine("I:{0},R:{1}", pick, reMoney); if (pick == 0) { break; } }
zbdzjx 2016-01-19
  • 打赏
  • 举报
回复
比如 6000块钱,分给5000个人。 先用1~2的随机数生成5000个人的数值,再将这些数值加到一起,结果要么多于6000,要么少于6000,如果等于,就直接OK了。 如果多于6000,先算出多了多少,例如多出123.45,再从这5000个人中随机抽取一个人,看他的数字比1多多少,例如是1.23,多了0.23,再用多出的数字,来生成随机数,例如从0~0.23生成随机数,例如是0.1,这个人的数字减去这个随机数,就是1.23-0.1=1.13,变成这个人的数字,总差异也去减这个数字,就是123.45-0.1=123.35。就这样一直减下去,直到差异为0。 如果是少于6000,就随机抽取一个人,类似上面的算法,往上加,直到差异为0。
shown_l 2016-01-19
  • 打赏
  • 举报
回复
能用贪心算法可以吗,第一轮每人发1元,剩下1000元平均成2毛钱或者其他的金额,用随机数判断是否分发,搞个几轮直到发完为止
Heart09 2016-01-19
  • 打赏
  • 举报
回复
M元钱分给N个人, 分配金额区间[S,X] 前提:S*N <= M <= X*N 每人分配金额基数为 S,N个人,就是 S*N, 剩余的金额为 L = M - S*N 剩余金额L再分配给N个人,每个人分配金额RL < MRL = X - S 设 RNM = L / MRL, 向上取整 设 RN = N / RNM, 则每次拿出MRL钱分给RN个人,都必须分完。 ------------------------------ 代入假设数据 6000元,5000人 ------------------------------ M 6000元钱分给N 5000个人, 分配金额区间[S 1,X 2] 前提:S 1*N 5000 <= M 6000 <= X 1*N 5000 每人分配金额基数为 S 1,N 5000个人,就是 S 1*N 5000, 剩余的金额为 L 1000 = M 6000 - S 1*N 5000 剩余金额L 1000再分配给N 5000个人,每个人分配金额RL < MRL 1 = X 2 - S 1 设 RNM 1000 = L 1000 / MRL 1, 向上取整 设 RN 5 = N 5000 / RNM 1000, 则每次拿出MRL 1钱分给RN 5个人,都必须分完。即没5个人必须领够1块钱 ----------------------- 这个方法的思想,将剩余的钱按最大增量分组,将人分相等的组去领取每堆的钱 这样可以保证每个人领到的钱+基数不会超过最大值 这种思想,领到的钱,每个分组的钱都是平均的。但每个人的钱不一样。 给它起个名字,就叫:一碗水尽量端平法。 ---------------------------------------------- 还有一种思想,动态分析,但容易产生连续的极值。 设随机数ER, 当前剩余人数为CN(初值为N),当前剩余钱为CL(初值为 L = M - S*N) 则每次得到随机数,必须满足(下面判断可以做优化) ER < CL S + ER <= X CL - ER <= (CN - 1)*MRL 如不满足,则重新计算随机数ER 如果产生CL为零,则后面再分的人,就都是基数S 如果产生CL等于CN*MRL,则后面再分的人,就都是最大值X 每次分配之后,重新计算 CL = CL - ER CN = CN - 1 这种思想容易产生连续极值,只是概率问题而已 给它起个名字,就叫:先来后到随机法
Poopaye 2016-01-19
  • 打赏
  • 举报
回复
引用 24 楼 leooyou 的回复:
[quote=引用 16 楼 shingoscar 的回复:] 综上所述,每次获取随机值范围为 max(1, ma - 2pa + 2) <= x <= min(2, ma - pa + 1)
最后的公式有点问题,ma - 2pa + 2 有可能为负数啊[/quote] 是max(1, ma - 2pa + 2) 如果后面一项是负数,那就取1
本拉灯 2016-01-19
  • 打赏
  • 举报
回复
你这个问题不能用随机数据整。 1.你应把这6000事件先配成4000个1元1千成2元(那这个就好办了事先把这4000个1与1千成2存入List)然后 value=list[rnd.Next(0,list.Count)] list.Remove(value); 2.或者你要有小数的,每个人分配的最大不能超过1.2元,要不然肯定有一个人会拿 到小于1元的。你的条件就不成立了,因为你要求最小1元,
迷失在代码里 2016-01-19
  • 打赏
  • 举报
回复
引用 16 楼 shingoscar 的回复:
设某一时刻 未分配钱为 ma,未分配人为 pa,本轮应当分配金额为 x 其中 1 <= x <= 2 再设本轮分配完成后 未分配钱为 mb,未分配人为 pb 下列不等式应当成立 pa <= ma <= 2pa pb <= mb <= 2pb 因为如果未分配钱超过这个范围,后面无论怎么分配,为了满足上面 x 的限制,剩余钱不可能为0 又 pb = pa - 1 mb = ma - x 得 pa - 1 <= ma - x <= 2pa - 2 化简: ma - 2pa + 2 <= x <= ma - pa + 1 综上所述,每次获取随机值范围为 max(1, ma - 2pa + 2) <= x <= min(2, ma - pa + 1)
最后的公式有点问题,ma - 2pa + 2 有可能为负数啊
本拉灯 2016-01-19
  • 打赏
  • 举报
回复
你要统计已取的个数与余额 int rndMax=取余额 if(统计数>2) { if(rndMax>2) rndMax=3; rndValue=rnd.Next(1,rndMax); } else { rndValue=取额 }
迷失在代码里 2016-01-19
  • 打赏
  • 举报
回复
引用 20 楼 l397870376 的回复:
这是抢红包的原理吗
是的,跟抢红包一个原理
加载更多回复(28)

111,125

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Creator Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧