红包算法
一、完全随机红包
(一)、方案一:(其实对于金钱应该用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=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 }
二、符合正态分布的随机红包
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 }