前言
前两天有人找我说能不能实现了一下红包随机分配。
想了一下写了一个简单的,不知道有没有bug,欢迎提出建议
需求
1 2 3 4 5 6 7 8 9 10 11
| 红包随机生成算法, 传入参数为:红包总金额,红包总个数,红包的最小金额(单位为元),红包的最大金额(单位为元),允许的小数点位数(0 1 2) 1 小数点允许 0 1 2 0 只发整数元的红包 1 最多到*.*元 2 允许到*.** 元 2 最小金额与最大金额是指需满足区间 3 随机出来的尽量满足正态分布 4 需要检查合理性 5 返回值为数组 测试是指用循环生成一百万组参数随机测
|
砍了一下需求:
1.最开始使用正态分布概率上面有点不好调控
2.没有最小金额和最大金额的限制
解决思路
因为提供了限制小数点的方式,在给的小数位数的情况下可以知道分配的最小单位。
例如:$point=2,表示两位小数,金额到分,所以最小单位就是0.01,分配数就是红包的总额度/最小单位
然后每次分的时候随机的区间就是[1,(剩余分配数/人数)*2]
,如果(剩余分配数/人数)
的值大于2才*2
这里的特例是如果有0.04元,2个人分,如果是0.02*2
的话第一个人有可能直接取走了所有的钱
代码
php代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
| <?php class RedPackage { public $rewardMoney; #红包金额、单位元 public $rewardNum; #红包数量 public $maxMoney; #最大红包金额 public $minMoney; #最小红包金额 public $rewardArray; #红包结果集 #判断输入是否合法 public function checklegal($rewardMoney,$rewardNum,$point) { if($point!=0 && $point!=1 && $point!=2) return false; if($rewardMoney==0 || $rewardNum==0) return false; $avg=$rewardMoney/$rewardNum; for($stand=1;$point>0;$point--){ $stand=$stand/10; } if($avg<$stand) return false; // echo $avg.' '.$stand; return true; } public function calUnit($point) { for($stand=1;$point>0;$point--){ $stand=$stand/10; } return $stand; } #执行红包生成算法 public function splitReward($rewardMoney,$rewardNum,$point=2,$scatter=100) { #传入红包金额和数量 $this->rewardMoney=$rewardMoney; $this->rewardNum=$rewardNum; $this->scatter=$scatter; $this->realscatter=$this->scatter/100; $this->rewardArray=array();
//首先判断输入是否合法 if(!$this->checklegal($rewardMoney,$rewardNum,$point)){ return false; } //计算最小单位和可分配数 $minUnit=$this->calUnit($point); $Num=$rewardMoney/$minUnit; // echo $minUnit.' '.$Num; //开始分配 $tNum=$Num; //剩余可分配数 $tpeople=$rewardNum; //需分配人数 $rewardArr=array(); //每人分配的数组 while($tpeople>1){ $canNum=$tNum/$tpeople; if($canNum>2) //如果2个人分0.04,不能到4 $canNum=round($canNum*2); else $canNum=round($canNum); $t=mt_rand(1,$canNum);//计算这个人能得到的分配数 array_push($rewardArr,$t); $tNum=$tNum-$t; $tpeople--; } //最后一个补全 array_push($rewardArr,$tNum); //然后换算成对应的金额 foreach($rewardArr as $k=>$value) { array_push($this->rewardArray,$value*$minUnit); } return $this->rewardArray;
} public function draw($rewardArr,$people,$money) { echo "<br />发放红包个数:{$people},红包总金额{$money}元。下方所有红包总额之和:".array_sum($rewardArr).'元。下方用图展示红包的分布'; echo '<hr>'; echo "<table style='font-size:12px;width:600px;border:1px solid #ccc;text-align:left;'><tr><td>红包金额</td><td>图示</td></tr>"; foreach($rewardArr as $val) { #线条长度计算 $width=intval($people*$val*300/$money); 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>"; } echo "</table>"; } }
$money=100; $people=20; $point=2; $redpackage=new RedPackage(); $result=$redpackage->splitReward($money,$people,$point); print_r($result); $redpackage->draw($result,$people,$money); // echo round(11/10); ?>
|
效果
基本效果如图
参考资料
笑喷!清华学霸发现微信抢到大红包的秘诀!
从14:33开始