代码如下:
1 <?php
2 /*
3 * 红包生成随机算法
4 */
5 header("Content-type:text/html;charset=utf-8");
6 date_default_timezone_set('PRC');
7
8 #红包生成的算法程序
9 class reward
10 {
11 public $rewardMoney; #红包金额、单位元
12 public $rewardNum; #红包数量
13 public $scatter; #分散度值1-10000
14 public $rewardArray; #红包结果集
15
16 #初始化红包类
17 public function __construct()
18 {
19 $this->rewardArray = array();
20 }
21
22 #执行红包生成算法
23 public function splitReward($rewardMoney, $rewardNum, $scatter = 100)
24 {
25 #传入红包金额和数量
26 $this->rewardMoney = $rewardMoney;
27 $this->rewardNum = $rewardNum;
28 $this->scatter = $scatter;
29 $this->realscatter = $this->scatter / 100;
30 /*
31 *前言:今天我突然这样一想,比如要把1个红包分给N个人,实际上就是相当于要得到N个百分比数据
32 * 条件是这N个百分比之和=100/100。这N个百分比的平均值是1/N。
33 * 并且这N个百分比数据符合一种正态分布(多数值比较靠近平均值)
34 *观点:微信红包里很多0.01的红包,我觉得这是微信程序里的人为控制,目的是为了防止总红包数超过总额,先分了几个0.01的红包。
35 * 不然不管是以随机概率还是正态分布都很难会出现非常多的0.01元红包。
36 */
37 #我的思路:正如上面说的,比如:1个红包发给5个人,我要得出5个小数,它们的和是1,他们的平均值是1/5
38 #计算出发出红包的平均概率值、精确到小数4位。即上面的1/N值。
39 $avgRand = round(1 / $this->rewardNum, 4);
40 #红包的向平均数集中的分布正像数学上的抛物线。抛物线y=ax2,|a|越大则抛物线的开口就越小,|a|越小则抛物线的开口就越大,a>0时开口向上,我们这都是正数,就以a>0来考虑吧。
41 #程序里的$scatter值即为上方的a,此值除以100,当做100为基准,
42 #通过开方(数学里的抛物线模型,开方可缩小变化值)得出一个小数字较多(小数字多即小红包多)的随机分布,据此生成随机数
43 $randArr = array();
44 while (count($randArr) < $rewardNum) {
45 $t = round(sqrt(mt_rand(1, 10000) / $this->realscatter));
46 $randArr[] = $t;
47 }
48 #计算当前生成的随机数的平均值,保留4位小数
49 $randAll = round(array_sum($randArr) / count($randArr), 4);
50 #为将生成的随机数的平均值变成我们要的1/N,计算一下生成的每个随机数都需要除以的值。我们可以在最后一个红包进行单独处理,所以此处可约等于处理。
51 $mixrand = round($randAll / $avgRand, 4);
52 #对每一个随机数进行处理,并剩以总金额数来得出这个红包的金额。
53 $rewardArr = array();
54 foreach ($randArr as $key => $randVal) {
55 $randVal = round($randVal / $mixrand, 4);
56 $rewardArr[] = round($this->rewardMoney * $randVal, 2);
57 }
58 #对比红包总数的差异、修正最后一个大红包
59 sort($rewardArr);
60 $rewardAll = array_sum($rewardArr);
61 $rewardArr[$this->rewardNum - 1] = $this->rewardMoney - ($rewardAll - $rewardArr[$this->rewardNum - 1]);
62 rsort($rewardArr);
63 #对红包进行排序一下以方便在前台图示展示
64 foreach ($rewardArr as $k => $value) {
65 $t = $k % 2;
66 if ($t) array_push($this->rewardArray, $value);
67 else array_unshift($this->rewardArray, $value);
68 }
69 $rewardArr = NULL;
70 return $this->rewardArray;
71 }
72 }
73
74 $money = 1000; #总共要发的红包数;
75 $people = 50; #总共要发的人数
76 $scatter = 100; #分散度
77 $reward = new reward();
78 $rewardArr = $reward->splitReward($money, $people, $scatter);
79 echo "发放红包个数:{$people},红包总金额{$money}元。下方所有红包总额之和:" . array_sum($reward->rewardArray) . '元。下方用图展示红包的分布';
80 echo '<hr>';
81 echo "<table style='font-size:12px;width:600px;border:1px solid #ccc;text-align:left;'><tr><td>红包金额</td><td>图示</td></tr>";
82 foreach ($rewardArr as $val) {
83 #线条长度计算
84 $width = intval($people * $val * 300 / $money);
85 echo "<tr><td>{$val}</td><td width='500px;text-align:left;'><hr style='width:{$width}px;height:3px;border:none;border-top:3px double red;margin:0 auto 0 0px;'></td></tr>";
86 }
87 echo "</table>";
88 ?>

在上传的文件里需要改一下:
$t=round(sqrt(mt_rand(1,10000)/$this->realscatter));,要控制值不为能0,我改成了1,没有测试,可能需要改大点,因为开方后的数值会缩小。
也可以对这行的值直接进行ceil处理, 就不会出现红包为0的数了。
对于scatter的值我没有多做研究,不过根据抛物线的数学模型,这个值的意义可以使抛物线的张口放大缩小,即可以让红包的值分散或者集中。
















