红包算法

一、完全随机红包

给定总金额,每个红包的金额范围和红包的个数,随机生成符合条件的红包金额。
转成数学语言: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('\t');
 9             }
10             sw.Close();
11             fs.Close();
12         }

 

posted @ 2017-06-28 17:35  晴心  阅读(5979)  评论(2编辑  收藏  举报