代码如下:
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;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}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的值我没有多做研究,不过根据抛物线的数学模型,这个值的意义可以使抛物线的张口放大缩小,即可以让红包的值分散或者集中。
链接:https://www.php.cn/php-weizijiaocheng-393575.html