紅包算法


一、完全隨機紅包

給定總金額,每個紅包的金額范圍和紅包的個數,隨機生成符合條件的紅包金額。
轉成數學語言: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         }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM