zoukankan      html  css  js  c++  java
  • 红包算法

    一、完全随机红包

    给定总金额,每个红包的金额范围和红包的个数,随机生成符合条件的红包金额。
    转成数学语言:n个随机数,总和为sum,每个随机数的范围为[min,max]。求随机方案。
    这里先声明一下,什么样的算法是合理的呢,基本需要具备以下两个特点:
    1、随机。这里认为生成红包金额一定要随机,即不能出现大量红包金额落在同一值;
    2、分布。范围内金额都有得到分配的机会。

    (一)、方案一:(其实对于金钱应该用decimal,不用double,这里先忽略这个问题只讨论算法的合理性)

      1 /* n个随机数,总和为sum,每个随机数的范围为[min,max]。
      2     前n-1个用rand函数随机产生,第n个数设为val=sum-(前n-1数之和)。
      3 (1)若val属于[min,max],则可以直接用;
      4 (2)若val>=max,则用max。val-max的处理:在1~n-1中随机出一个序号m来,将多出来的val-max补到这第m个数上,如果将其补为200仍然还有剩余,则继续做这样的操作,直到多余部分被分配完为止。
      5 (3)若val<=min,则用min。min-val的处理:在1~n-1中随机出一个序号m来,将多出来的min-val部分从这第m个数上扣除,如果将其削为min仍然没有扣完,则继续这个操作,直到配平为止。*/
      6 
      7     public class RandomGenerator
      8     {
      9         /// <summary>
     10         /// 生成指定个数的限定金额范围的随机红包,且总额为指定值
     11         /// </summary>
     12         /// <param name="totalValue">指定总额</param>
     13         /// <param name="min">最小边界</param>
     14         /// <param name="max">最大边界</param>
     15         /// <param name="num">数量</param>
     16         /// <returns>以列表的形式返回生成的数据</returns>
     17         public List<double> RandomData(double totalValue, double min, double max, int num, out string message)
     18         {
     19             if (min > max)
     20             {
     21                 message = "min > max,参数错误";
     22             }
     23           
     24             List<double> list = new List<double>();
     25 
     26             //判断参数合理性
     27             if (min * num == totalValue)
     28             {
     29                 for (int i = 0; i < num ; i++)
     30                 {
     31                     list.Add(min);
     32                 }
     33                 message = "fixedValue";
     34                 return list;
     35             }
     36             if (min * num > totalValue)
     37             {
     38                 message = "min * num > totalValue,参数不合理";
     39                 return null;
     40             }
     41             if (max * num == totalValue)
     42             {
     43                 for (int i = 0; i < num; i++)
     44                 {
     45                     list.Add(max);
     46                 }
     47                 message = "fixedValue";
     48                 return list;
     49             }
     50             if (max * num < totalValue)
     51             {
     52                 message = "max * num < totalValue,参数不合理";
     53                 return null; 
     54             }
     55             
     56             double sum = 0.0;
     57             Random random = new Random();
     58             double diffValue = max - min;
     59            
     60             double temp;
     61             //前num-1个数据随机生成
     62             for (int i = 0; i < num-1; i++)
     63             {
     64                 temp = Math.Round(random.NextDouble() * diffValue + min, 2);
     65                 sum += temp;
     66                 list.Add(temp);
     67             }
     68             //为了保证总额为指定值,须对最后一个数据的生成做处理
     69             double gap = totalValue-sum;
     70             if (gap >= min && gap <= max)
     71             {
     72                 list.Add(gap);
     73                 message = "success";
     74                 return list;
     75             }
     76             else if (gap > max)
     77             {//剩余值大于max
     78                 list.Add(max);
     79                 gap = gap - max;
     80                 int index;
     81                 double value;
     82                 while (gap > 0)
     83                 {
     84                     index = random.Next(num - 1);  //生成0到num-1之间的随机整数(包括0,不包括num-1)
     85                     value = list[index];
     86                     double margin = max - value;
     87                     if (margin>0)
     88                     {
     89                         if (gap >= margin)
     90                         {
     91                             list[index] = max;
     92                             gap = gap - margin;
     93                         }
     94                         else 
     95                         {
     96                             list[index] = list[index] + gap;
     97                             message = "success";
     98                             return list;
     99                         }
    100                     }
    101                 }
    102             }
    103             else //剩余值小于min
    104             {
    105                 list.Add(min);
    106                 double need = min-gap;
    107                 int index;
    108                 double value;
    109                 double buffer;
    110                 while (need > 0)
    111                 {
    112                     index = random.Next(num - 1);  //生成0到num-1之间的随机整数(包括0,不包括num-1)
    113                     value = list[index];
    114                     buffer = value - min;
    115                     if (buffer >= need)
    116                     {
    117                         list[index] = list[index] - need;
    118                         message = "success";
    119                         return list;
    120                     }
    121                     else
    122                     {
    123                         list[index] = min;
    124                         need = need - buffer;
    125                     }
    126                 }
    127             }
    128             message = "success";
    129             return list;
    130         }
    131 
    132     }

    试验结果:

    第一组:min=20;max=150;n=10;sum=1000 
                

    第二组:min=50,max=200,n=20000,sum=1200000(部分数据截图如下)

    总结:从试验结果来看,这种算法生成的数据并不理想,明显大量的数据落在了范围的分界点上。因此不适用。

    结果分析:当平均值远大于最大最小值中位数(中位数代表随机值围绕上线抖动范围)时,会导致随机分配完之后剩余金额较多,而算法采用随机选人进行累加,达到上限后继续随机人员,从而导致大部分人处在上限边界;

    反之,平均值小于中位数时,大部分人处于下限边界。

    算法尝试改进:

    尝试改进一

    这里对每个值先在范围内随机赋值,剩余值进行平均加到每个人上,保证原有值的随机性;但是,加入剩余平均值为r_avg,这样计算之后,每个人的金额范围会变为[min+r_avg,  max+r_avg]。

    max+r_avg问题会导致某些人加r_avg之后大于最大值,可以尝试把value(i)+r_avg>=max的人排除;

    min+r_avg会导致min~min+r_avg之间无人数,尝试计算min~min+r_avg之间应有人数min_n = (max-min)/n  *  r_avg = (max-min)/n  *  r_sum/n = (max-min)*r_sum/n*n,排除这些人。但是这样又会导致[min+avg, min+avg+avg]之间断层。这个问题可以通过随机选n/2来变相解决,但是有点不合理,目前来看没有好的解决方案。

    尝试改进二

    1)这里对每个值先在范围内随机赋值;

    2)遍历每个值,在范围[0,  max-value(i)]内计算个随机值;即random(i);

    3)判断剩余金额r_sum-random(i)的值是否变号。如原r_sum为5,随机金额为7,5-7=-2,即变号;

    4)如果变号,取value(i) = value(i) + r_sum,程序终止;

    5)如果未变号,value(i) = value(i) + random(i)。继续遍历下个值重复2。

    另外,如果剩余值为负,同样道理,只是随机数范围是[0, value(i)-min],而且遍历每个值要减去这个随机数。

    ( 二)、方案二:

     1  //每个红包先分配min元,其余的再随机分(随机位置在符合金额范围内随机加钱,分完为止)。
     2     public class RandomGeneratorNew
     3     {        
     4         public List<double> RandomData(double totalValue, double min, double max, int num, out string message)
     5         {
     6             if (min > max)
     7             {
     8                 message = "min > max,参数错误";
     9             }
    10 
    11             List<double> list = new List<double>();
    12 
    13             //判断参数合理性
    14             if (min * num == totalValue)
    15             {
    16                 for (int i = 0; i < num; i++)
    17                 {
    18                     list.Add(min);
    19                 }
    20                 message = "fixedValue";
    21                 return list;
    22             }
    23             if (min * num > totalValue)
    24             {
    25                 message = "min * num > totalValue,参数不合理";
    26                 return null;
    27             }
    28             if (max * num == totalValue)
    29             {
    30                 for (int i = 0; i < num; i++)
    31                 {
    32                     list.Add(max);
    33                 }
    34                 message = "fixedValue";
    35                 return list;
    36             }
    37             if (max * num < totalValue)
    38             {
    39                 message = "max * num < totalValue,参数不合理";
    40                 return null;
    41             }
    42 
    43             double left;
    44             Random random = new Random();
    45             double diffValue = max - min;
    46 
    47             double tempAdd;
    48             //每个红包先分配min元
    49             for (int i = 0; i < num; i++)
    50             {
    51                 list.Add(min);
    52             }
    53             int index;
    54             double value;
    55             left=totalValue-min*num;//剩余未分配金额
    56             while (Math.Round(left,2) > 0)
    57             { 
    58                 index = random.Next(num - 1);  //生成0到num-1之间的随机整数(包括0,不包括num-1)
    59                 value = list[index];
    60                 double maxMargin = NumMIN(max, left,max-value);
    61                 if (Math.Round(maxMargin, 2) == 0)
    62                     break;
    63                 tempAdd = Math.Round(random.NextDouble() * maxMargin, 2);
    64                 list[index] = value + tempAdd;
    65                 left -= tempAdd;
    66             }
    67             message = "success";
    68             return list;
    69         }
    70 
    71         private double NumMIN(double p1, double p2, double p3)
    72         {
    73             double temp =  p1 < p2 ? p1 : p2;
    74             return temp < p3 ? temp : p3;
    75         }
    76     
    77     }

    试验结果:

    min=1,max=200,n=30000,sum=100000。结果如下(部分数据截图):

    总结:从试验结果来看,这种算法生成的数据并不理想,大部分数据都集中在了最小值上。因此这种算法不能接受。

    结果分析:这里之所以出现这种情况,是因为在最小值比较小,中位数(100)远远大于平均值(3)的情况下,再进行剩余金额分配时,刚分配少量几个位置余额就分配完了。

    (三)、方案三:

    随机撒开,0.01撒开,再撒。即按照0.01随机选择位置进行累加,直至剩余金额为0。
     1 //随机撒开,0.01撒开,再撒
     2     public class RandomTest
     3     {
     4         /// <summary>
     5         /// 生成指定个数的限定金额范围的随机红包,且总额为指定值
     6         /// </summary>
     7         /// <param name="totalValue">指定总额</param>
     8         /// <param name="min">最小边界</param>
     9         /// <param name="max">最大边界</param>
    10         /// <param name="num">数量</param>
    11         /// <returns>以列表的形式返回生成的数据</returns>
    12         public List<decimal> RandomData(decimal totalValue, decimal min, decimal max, int num, out string message)
    13         {
    14             if (min > max)
    15             {
    16                 message = "min > max,参数错误";
    17             }
    18 
    19             List<decimal> list = new List<decimal>();
    20 
    21             //判断参数合理性
    22             if (min * num == totalValue)
    23             {
    24                 for (int i = 0; i < num; i++)
    25                 {
    26                     list.Add(min);
    27                 }
    28                 message = "fixedValue";
    29                 return list;
    30             }
    31             if (min * num > totalValue)
    32             {
    33                 message = "min * num > totalValue,参数不合理";
    34                 return null;
    35             }
    36             if (max * num == totalValue)
    37             {
    38                 for (int i = 0; i < num; i++)
    39                 {
    40                     list.Add(max);
    41                 }
    42                 message = "fixedValue";
    43                 return list;
    44             }
    45             if (max * num < totalValue)
    46             {
    47                 message = "max * num < totalValue,参数不合理";
    48                 return null;
    49             }
    50             Random random = new Random();
    51             decimal remainder = totalValue;
    52             int index;       
    53             //每个红包初始化赋值,先保证min
    54             for (int i = 0; i < num; i++)
    55             {
    56                 list.Add(min);
    57             }
    58             while (remainder!=0)
    59             {
    60                 index = random.Next(num);  //生成0到num之间的随机整数(包括0,不包括num)
    61                 if (list[index] + (decimal)0.01 <= max)
    62                 {
    63                     list[index] += (decimal)0.01;
    64                     remainder -= (decimal)0.01;
    65                 }
    66             }
    67             message = "success";
    68             return list;
    69         }
    70 
    71       
    72     }

    试验结果:

    min=1,max=200,n=30000,sum=100000。结果如下(部分数据截图):

    总结:从试验结果来看,这种算法生成的数据并不理想,数据比较接近,没有达到在范围内随机的目的。

    结果分析:这里因为按照0.01最细粒度进行随机撒开,当金额数量比较大时,随机撒的次数比较多,那么从统计学上来说每个位置被选中的次数比较接近,所以会造成金额比较相近的结果。但是也不能按照粗粒度进行随机撒,这样会造成所有值都是粗粒度的倍数的情况出现。

    (四)、方案四:(最终采用的方案)

      1 public List<decimal> RandomData(decimal totalValue, decimal min, decimal max, int num, out string message)
      2         {
      3             decimal mu = totalValue / num;
      4             List<decimal> data;
      5 
      6             if (mu - min <= max - mu)
      7             {
      8                 data = RandomDataLeft(totalValue, min, max, num, out message);
      9             }
     10             else
     11             {
     12                 data = RandomDataRight(totalValue, min, max, num, out message);
     13             }
     14             return data;
     15         }
     16         public decimal AdjustData(decimal temp, decimal min, decimal max)//修正数据
     17         {
     18             if (temp < min)
     19             {
     20                 temp = min;
     21             }
     22             if (temp > max)
     23             {
     24                 temp = max;
     25             }
     26             return temp;
     27         }
     28         public List<decimal> RandomDataLeft(decimal totalValue, decimal min, decimal max, int num, out string message)
     29         {  //这个方法适用于max-mu>=mu-min的时候
     30             List<decimal> list = new List<decimal>();
     31 
     32             if (max / (20 + min) >= 2)
     33                 return RandomDataLeftAdjust(totalValue, min, max, num, out message);
     34 
     35 
     36 
     37             decimal maxMargin = 0;
     38             //decimal mu = totalValue / num;
     39             //decimal mu = Math.Round(totalValue / num, 2);
     40             decimal mu = Math.Round(totalValue / num, 2);
     41             Random random = new Random();
     42             decimal diffValue = max - min;
     43             int count = num;
     44 
     45             decimal factor = mu - min;
     46             decimal temp;
     47 
     48             //最多可以几个数拼凑
     49             int maxIndex;
     50             decimal remainder = (max - min) % factor;
     51             if (remainder == 0)
     52                 maxIndex = (int)((max - min) / factor);
     53             else
     54                 maxIndex = (int)((max - min) / factor) + 1;
     55 
     56             decimal a;
     57             decimal b;
     58             int index = 2;
     59 
     60 
     61             while (count > 1)
     62             {
     63 
     64                 if (count > maxIndex)
     65                 {
     66                     //正常流程
     67                     temp = Math.Round((decimal)(random.NextDouble()) * diffValue + min, 2);
     68                 }
     69                 else
     70                 {
     71                     temp = Math.Round((decimal)(random.NextDouble()) * (count * factor) + min, 2);//当count大于2时,范围边界 min,min+count*factor,最后一个区间:min+(count-1)*factor,min+count*factor;;;当count=2时也通用
     72                 }
     73                 temp = AdjustData(temp, min, max);
     74                 list.Add(temp);
     75                 a = min;
     76                 b = min + 2 * factor;
     77                 while (!(temp >= a && temp <= b))
     78                 {
     79                     a = b;
     80                     b += factor;
     81                     if (b >= max)
     82                         b = max;
     83                     index++;
     84                 }
     85                 //此时index的值为多少(n),就表示需要多少个数,总和为mu*index
     86                 if (index == 2)
     87                 {
     88                     count -= 2;
     89                     //list.Add(mu * 2 - temp);
     90                     if (count == 0)
     91                         list.Add(totalValue - list.Sum());
     92                     else
     93                         list.Add(AdjustData(Math.Round(mu * 2 - temp, 2),min,max));
     94                     continue;
     95                 }
     96                 decimal partialSum = mu * index;
     97                 count -= index;
     98                 while (index != 2)
     99                 {
    100                     partialSum = partialSum - temp;
    101                     maxMargin = partialSum - (index - 2) * min;
    102                     temp = Math.Round((decimal)(random.NextDouble()) * (maxMargin - min) + min, 2);
    103                     temp = AdjustData(temp, min, max);
    104                     list.Add(temp);
    105                     index--;
    106                 }
    107                 partialSum -= temp;
    108                 if (count == 0)
    109                 {
    110                     decimal tempSum = list.Sum();
    111                     list.Add(totalValue - list.Sum());
    112                 }
    113                 else
    114                     list.Add(AdjustData(Math.Round(partialSum, 2),min,max));
    115             }
    116             if (count == 1)
    117             {
    118                 //list.Add(Math.Round(mu,2));
    119                 decimal tempSum = list.Sum();
    120                 list.Add(totalValue - list.Sum());
    121             }
    122 
    123             //检验校正
    124 
    125             message = "success";
    126 
    127             list = checkAndAdjust(list,min,max);//看最后一个数据是否符合要求,如果不符合需处理
    128 
    129             //在返回数据之前再打乱一下顺序
    130             Random RND = new Random(DateTime.Now.Millisecond);
    131             list = list.OrderBy(x => RND.Next()).ToList();
    132             return list;
    133         }
    134 
    135         public List<decimal> checkAndAdjust(List<decimal> list,decimal min, decimal max)
    136         {
    137             decimal lastNum = list[list.Count - 1];
    138             decimal diff;
    139             Random random = new Random();
    140             if (lastNum < min)
    141             {
    142                 list[list.Count - 1] = min;
    143                 diff = min - lastNum;//多算了diff,需从其他元素中减去                               
    144                
    145                 while(diff!=0)
    146                 {
    147                     int index = random.Next(list.Count - 1);
    148                     if (list[index] > min)
    149                     {
    150                         if (list[index] - min > diff)
    151                         {
    152                             list[index] -= diff;
    153                             diff = 0;
    154                         }
    155                         else if (list[index] - min < diff)
    156                         {
    157                             diff = diff - (list[index] - min);//还剩diff需从其他元素中减去
    158                             list[index] = min;
    159                         }
    160                         else { //=
    161                             list[index] = min;
    162                             diff = 0;
    163                         }
    164                     }
    165                 }
    166                 return list;
    167             }
    168             else if (lastNum > max)
    169             {
    170                 list[list.Count - 1] = max;
    171                 diff = lastNum - max;//少算了diff,需加入其他元素中去
    172                 while (diff != 0)
    173                 { 
    174                     int index = random.Next(list.Count - 1);
    175                     if (list[index] < max)
    176                     {
    177                         if (max - list[index] > diff)
    178                         {
    179                             list[index] += diff;
    180                             diff = 0;
    181                         }
    182                         else if (max - list[index] < diff)
    183                         {
    184                             list[index] = max;
    185                             diff = diff - (max - list[index]);//还剩diff需要加进其他元素中
    186                         }
    187                         else
    188                         {
    189                             list[index] = max;
    190                             diff = 0;
    191                         }
    192                     }
    193                 }
    194                 return list;
    195             }
    196             else //最后一个元素刚好也符合条件,则不需处理
    197             {
    198                 return list;
    199             }
    200         }
    201 
    202         public List<decimal> RandomDataRight(decimal totalValue, decimal min, decimal max, int num, out string message)
    203         {//这个方法适用于max-mu<mu-min的时候
    204 
    205             List<decimal> list = new List<decimal>();
    206 
    207             decimal minMargin = 0;
    208             //decimal mu = totalValue / num;
    209             //decimal mu = Math.Round(totalValue / num, 2);
    210             decimal mu = Math.Round(totalValue / num, 2);
    211             Random random = new Random();
    212             decimal diffValue = max - min;
    213             int count = num;
    214 
    215             decimal factor = max - mu;
    216             decimal temp;
    217             decimal lastNum;
    218             decimal left;
    219 
    220             //最多可以几个数拼凑
    221             int maxIndex;
    222             decimal remainder = (max - min) % factor;
    223             if (remainder == 0)
    224                 maxIndex = (int)((max - min) / factor);
    225             else
    226                 maxIndex = (int)((max - min) / factor) + 1;
    227 
    228             decimal a;
    229             decimal b;
    230             int index = 2;
    231 
    232 
    233             while (count > 1)
    234             {
    235 
    236                 if (count > maxIndex)
    237                 {
    238                     //正常流程
    239                     temp = Math.Round((decimal)(random.NextDouble()) * diffValue + min, 2);
    240                 }
    241                 else
    242                 {
    243                     temp = Math.Round((decimal)(random.NextDouble()) * (count * factor) + max - count * factor, 2);//范围:(max-count*factor,max)
    244                 }
    245                 list.Add(temp);
    246                 a = max - 2 * factor;
    247                 b = max;
    248                 while (!(temp >= a && temp <= b))
    249                 {
    250                     b = a;
    251                     a -= factor;
    252                     if (a < min)
    253                         a = min;
    254                     index++;
    255                 }
    256                 //此时index的值为多少(n),就表示需要多少个数,总和为mu*index
    257                 if (index == 2)
    258                 {
    259                     count -= 2;
    260                     if (count == 0)
    261                     {
    262                         lastNum = totalValue - list.Sum();
    263                         if (lastNum <= max)
    264                             list.Add(lastNum);
    265                         else
    266                         {
    267                             list.Add(max);
    268                             left = lastNum - max;
    269                             int pos;
    270                             while (true)
    271                             {
    272                                 pos = random.Next(num);
    273                                 if (max - list[pos] >= left)
    274                                 {
    275                                     list[pos] += left;
    276                                     break;
    277                                 }
    278                             }
    279                         }
    280 
    281                     }
    282                     else
    283                         list.Add(Math.Round(mu * 2 - temp, 2));
    284                     continue;
    285                 }
    286                 decimal partialSum = mu * index;
    287                 count -= index;
    288                 while (index != 2)
    289                 {
    290                     partialSum = partialSum - temp;
    291                     minMargin = partialSum - (index - 2) * max;
    292                     temp = Math.Round((decimal)(random.NextDouble()) * (max - minMargin) + minMargin, 2);
    293                     list.Add(temp);
    294                     index--;
    295                 }
    296                 partialSum -= temp;
    297                 if (count == 0)
    298                 {
    299                     lastNum = totalValue - list.Sum();
    300                     if (lastNum <= max)
    301                         list.Add(lastNum);
    302                     else
    303                     {
    304                         list.Add(max);
    305                         left = lastNum - max;
    306                         int pos;
    307                         while (true)
    308                         {
    309                             pos = random.Next(num);
    310                             if (max - list[pos] >= left)
    311                             {
    312                                 list[pos] += left;
    313                                 break;
    314                             }
    315                         }
    316                     }
    317                 }
    318                 else
    319                     list.Add(Math.Round(partialSum, 2));
    320             }
    321             if (count == 1)
    322             {
    323                 lastNum = totalValue - list.Sum();
    324                 if (lastNum <= max)
    325                     list.Add(lastNum);
    326                 else
    327                 {
    328                     list.Add(max);
    329                     left = lastNum - max;
    330                     int pos;
    331                     while (true)
    332                     {
    333                         pos = random.Next(num);
    334                         if (max - list[pos] >= left)
    335                         {
    336                             list[pos] += left;
    337                             break;
    338                         }
    339                     }
    340                 }
    341             }
    342 
    343             //检验校正
    344 
    345             message = "success";
    346             list = checkAndAdjust(list, min, max);//看最后一个数据是否符合要求,如果不符合需处理
    347             //在返回数据之前再打乱一下顺序
    348             Random RND = new Random(DateTime.Now.Millisecond);
    349             list = list.OrderBy(x => RND.Next()).ToList();
    350             return list;
    351         }
    352 
    353         private List<decimal> RandomDataLeftAdjust(decimal totalValue, decimal min, decimal max, int num, out string message)
    354         {
    355 
    356             List<decimal> list = new List<decimal>();
    357 
    358             int ratio;//概率比例调整倍数
    359             int maxReduce;//最大值缩小倍数
    360 
    361             int tempMaxReduce = (int)(max % (20 + min));
    362             if (tempMaxReduce == 0)
    363                 maxReduce = (int)(max / (20 + min));
    364             else
    365             {
    366                 maxReduce = (int)(max / (20 + min)) + 1;
    367                 if (maxReduce >= max / min)
    368                     maxReduce--;
    369             }
    370 
    371             int tempRatio = num / 5;
    372             ratio = tempRatio > 200 ? 200 : tempRatio;
    373 
    374             decimal maxMargin = 0;
    375             //decimal mu = totalValue / num;
    376             //decimal mu = Math.Round(totalValue / num, 2);
    377             decimal mu = Math.Round(totalValue / num, 2);
    378             Random random = new Random();
    379             decimal diffValue = max - min;
    380             int count = num;
    381 
    382             decimal factor = mu - min;
    383             decimal temp;
    384 
    385             //最多可以几个数拼凑
    386             int maxIndex;
    387             decimal remainder = (max - min) % factor;
    388             if (remainder == 0)
    389                 maxIndex = (int)((max - min) / factor);
    390             else
    391                 maxIndex = (int)((max - min) / factor) + 1;
    392 
    393             decimal a;
    394             decimal b;
    395             int index = 2;
    396 
    397             int flag = 1;
    398             int mode;
    399             decimal modediffValue = max / maxReduce - min;  //缩小多少倍,这个参数10可根据实际情况来调节     !!小心会不会出现max/10<min的情况???
    400 
    401             while (count > 1)
    402             {
    403                 if (flag % ratio == 0)   //这个比例的控制也可以根据实际情况来调节
    404                     mode = 1;
    405                 else
    406                     mode = 0;
    407                 flag++;
    408                 if (count > maxIndex)
    409                 {
    410                     //正常流程
    411 
    412                     if (mode == 1)
    413                         temp = Math.Round((decimal)(random.NextDouble()) * diffValue + min, 2);
    414                     else
    415                         temp = Math.Round((decimal)(random.NextDouble()) * modediffValue + min, 2);
    416                 }
    417                 else
    418                 {
    419                     if (mode == 1)
    420                         temp = Math.Round((decimal)(random.NextDouble()) * (count * factor) + min, 2);//当count大于2时,范围边界 min,min+count*factor,最后一个区间:min+(count-1)*factor,min+count*factor
    421                     else
    422                     {
    423                         decimal tempMargin = (count * factor) < modediffValue ? (count * factor) : modediffValue;
    424                         temp = Math.Round((decimal)(random.NextDouble()) * tempMargin + min, 2);
    425                     }
    426                 }
    427                 temp = AdjustData(temp, min, max);
    428                 list.Add(temp);
    429                 a = min;
    430                 b = min + 2 * factor;
    431                 while (!(temp >= a && temp <= b))
    432                 {
    433                     a = b;
    434                     b += factor;
    435                     if (b >= max)
    436                         b = max;
    437                     index++;
    438                 }
    439                 //此时index的值为多少(n),就表示需要多少个数,总和为mu*index
    440                 if (index == 2)
    441                 {
    442                     count -= 2;
    443                     //list.Add(mu * 2 - temp);
    444                     if (count == 0)
    445                         list.Add(totalValue - list.Sum());
    446                     else
    447                         list.Add(AdjustData(Math.Round(mu * 2 - temp, 2),min,max));
    448                     continue;
    449                 }
    450                 decimal partialSum = mu * index;
    451                 count -= index;
    452                 while (index != 2)
    453                 {
    454                     partialSum = partialSum - temp;
    455                     maxMargin = partialSum - (index - 2) * min;
    456                     temp = Math.Round((decimal)(random.NextDouble()) * (maxMargin - min) + min, 2);
    457                     temp = AdjustData(temp, min, max);
    458                     list.Add(temp);
    459                     index--;
    460                 }
    461                 partialSum -= temp;
    462                 if (count == 0)
    463                 {
    464                     decimal tempSum = list.Sum();
    465                     list.Add(totalValue - list.Sum());
    466                 }
    467                 else
    468                     list.Add(AdjustData(Math.Round(partialSum, 2),min,max));
    469             }
    470             if (count == 1)
    471             {
    472                 //list.Add(Math.Round(mu,2));
    473                 decimal tempSum = list.Sum();
    474                 list.Add(totalValue - list.Sum());
    475             }
    476 
    477             //检验校正
    478 
    479             message = "success";
    480             //在返回数据之前再打乱一下顺序
    481             Random RND = new Random(DateTime.Now.Millisecond);
    482             list = list.OrderBy(x => RND.Next()).ToList();
    483             return list;
    484         }

    二、符合正态分布的随机红包

    给定总金额,每个红包的金额范围和红包的个数,随机生成符合正态分布的红包金额。
    转成数学语言:n个随机数,总和为sum,每个随机数的范围为[min,max],要求数据符合正态分布。求随机方案。
      1 /// <summary>
      2         ///  生成符合正态分布的随机数据
      3         /// </summary>
      4         /// <param name="r"></param>
      5         /// <param name = "mu">Mean of the distribution</param>
      6         /// <param name = "sigma">Standard deviation</param>
      7         /// <returns></returns>
      8         private decimal NextGaussian(Random r, decimal mu = 0, decimal sigma = 1)
      9         {
     10             var u1 = r.NextDouble();
     11             var u2 = r.NextDouble();
     12             var rand_std_normal = (decimal)(Math.Sqrt(-2.0 * Math.Log(u1)) *
     13                                 Math.Sin(2.0 * Math.PI * u2));
     14 
     15             var rand_normal = mu + sigma * rand_std_normal;
     16 
     17             return rand_normal;
     18         }
     19         public List<decimal> NorDist(decimal totalValue, decimal min, decimal max, int num, out string message)
     20         {
     21             try
     22             {
     23                 decimal mu = (max + min) / 2;
     24                 decimal sigma = (max - mu) / 3;
     25                 decimal tempSum = 0;
     26 
     27                 var r = new Random();
     28                 List<decimal> list = new List<decimal>();
     29                 decimal randomData;
     30                 for (int i = 0; i < num - 1; i++)  //先生成前num-1个数据
     31                 {
     32                     randomData = NextGaussian(r, mu, sigma);
     33                     for (; randomData < min || randomData > max; )
     34                         randomData = NextGaussian(r, mu, sigma);
     35                     randomData = Math.Round(randomData, 2);
     36                     tempSum += randomData;
     37                     list.Add(randomData);
     38                 }
     39                 decimal diff = totalValue - tempSum;
     40                 if (diff >= min && diff <= max)
     41                 {
     42                     tempSum += diff;
     43                     list.Add(diff);
     44                     message = "success";
     45                     return list;
     46                 }
     47                 else
     48                 {
     49                     decimal miniUnit = 0.1M;
     50                     randomData = NextGaussian(r, mu, sigma);
     51                     for (; randomData < min || randomData > max; )
     52                         randomData = NextGaussian(r, mu, sigma);
     53                     randomData = Math.Round(randomData, 2);
     54                     tempSum += randomData;
     55                     list.Add(randomData);
     56                     diff = totalValue - tempSum;
     57                     int k = -1;
     58                     decimal n;
     59 
     60                     if (diff > 0)
     61                     {  //即预定资金没用完
     62                         for (n = diff; n >= miniUnit; n = n - miniUnit)
     63                         {
     64                             k = (k + 1) % num;
     65                             while (list[k] > max - miniUnit)
     66                             {
     67                                 k = (k + 1) % num;
     68                             }
     69                             list[k] += miniUnit;
     70                         }
     71                         if (n != 0)
     72                         {
     73                             while (list[k] + n > max)
     74                             {
     75                                 if (k > 0)
     76                                     k = k - 1;
     77                                 else
     78                                 {
     79                                     message = "正态分布分配失败,请重试";
     80                                     return null;  //分配失败
     81                                 }
     82                             }
     83                             list[k] += n;
     84                         }
     85                     }
     86                     else //即diff<0,不可能出现=0的情况   ,此时分配资金超出预定,需要回收
     87                     {
     88                         diff = -diff;
     89                         for (n = diff; n >= miniUnit; n = n - miniUnit)
     90                         {
     91                             k = (k + 1) % num;
     92                             while (list[k] < min + miniUnit)
     93                             {
     94                                 k = (k + 1) % num;
     95                             }
     96                             list[k] -= miniUnit;
     97                         }
     98                         if (n != 0)
     99                         {
    100                             while (list[k] - n < min)
    101                             {
    102                                 if (k > 0)
    103                                     k = k - 1;
    104                                 else
    105                                 {
    106                                     message = "正态分布分配失败,请重试";
    107                                     return null;  //分配失败
    108                                 }
    109                             }
    110                             list[k] -= n;
    111                         }
    112                     }
    113                 }
    114 
    115                 message = "success";
    116                 return list;
    117             }
    118             catch (Exception ex)
    119             {
    120                 Logger.Error("HRPortal -> NorDist方法异常", ex);
    121                 message = "分配金额失败,请重试";
    122                 return null;
    123             }
    124         }

     三、网上找到的例子:(生成随机红包一)

    (1)https://www.zhihu.com/question/22625187

    (2)

     1 /**
     2  * 计算随机值
     3  * @input:  min  最小金额(默认为1, 0.01元)
     4  *          max 最大金额(默认为20000, 200元)
     5  *          total 剩余总金额
     6  *          num 剩余总人数
     7  * @return: 本次随机金额
     8  */
     9 LONG HbReceive::calcRandomValue(LONG min, LONG max, LONG total, LONG num) 
    10   throw (CException)
    11 {
    12     if(num == 1)
    13     {
    14         return total;
    15     }
    16  
    17     // 更新随机种子
    18     srand(time(NULL));
    19  
    20     // 确定本次随机范围
    21     LONG low = (total - (num-1)*max) < min ? min : total - (num-1)*max;
    22  
    23     LONG high = (total - (num-1)*min) > max ? max : (total - (num-1)*min);
    24  
    25     LONG ave = total / num > 1 ? total / num : 1;
    26  
    27     // 调整上限
    28     if(high > 2 * ave)   high = 2 * ave;
    29  
    30     // 生成随机值
    31     LONG ram = random() % high;
    32  
    33     // 防止溢出
    34     if(ram < low) ram = low;
    35  
    36     if(ram > high) ram = high;
    37  
    38     return ram;
    39 }

    输出到文件中代码:

     1  public void outputToFile(List<decimal> list)
     2         {
     3             FileStream fs = new FileStream("D:\RandomTest.txt", FileMode.Append);
     4             StreamWriter sw = new StreamWriter(fs, Encoding.Default);
     5             foreach (var item in list)
     6             {
     7                 //sw.WriteLine(item);
     8                 sw.Write(item); sw.Write('	');
     9             }
    10             sw.Close();
    11             fs.Close();
    12         }
  • 相关阅读:
    十九:数字排重
    十八:十六进制转八进制
    Dubbo Notes
    Concurrent Package
    MySQL(8.0)官方文档阅读笔记
    MyBatis笔记
    分布式事务
    RabbitMQ笔记
    Kafka官方文档阅读笔记
    Cassandra Note
  • 原文地址:https://www.cnblogs.com/qingxinblog/p/7090834.html
Copyright © 2011-2022 走看看