随机红包金额分配(php)

前言

前两天有人找我说能不能实现了一下红包随机分配。
想了一下写了一个简单的,不知道有没有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);
?>

效果

基本效果如图
RedPackage.png


参考资料

笑喷!清华学霸发现微信抢到大红包的秘诀!
从14:33开始

如果对您有帮助,请我喝杯咖啡?