PHP实现简单发红包(随机分配,平均分配)
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
PHP实现简单发红包(随机分配,平均分配)
最近碰到⼀些情况,把思路重新整理了⼀下,敲出代码。
记下来,以后可以借鉴,进⼀步优化等。
⼤致的思路:红包主要分两种,⼀种是平均分配,⼀种是随机分配。
1、平均分配
平均分配相对好理解,只要把钱平均分给每⼀个⼈就可以了
这⾥有⼀个情况,就是钱的总额是固定的,但是分配的⼈数,不⼀定可以整除余0,那么剩下的如何分配呢?
这⾥,剩余的钱(极少),多分到的⼈,也就是多分1分钱(在计算处理时,单位是“分”)
所以,使⽤的处理办法是,从前到后(谁⼿快,谁多分,蚊⼦再⼩也是⾁),逐⼀分这剩余的钱,每⼈1分钱,直到钱没为⽌
2、随机分配
我这个随机分配,⽐较简单,只⽐平均分配多了⼀个步骤(此步骤根据需要,可以循环多次使⽤)。
先是随机分配的两个特殊情况:
a.总⾦额不够所有⼈分。
例如,最⼩的钱是1分钱,分给10个⼈。
是不可以的
b.总⾦额正好只能每⼈平均分1分钱。
例如,0.1元,分给10个⼈,任何⼀个⼈多1分钱,就会有⼈没钱分
这两个特例单独处理
接下来的情况就是,正常的随机分钱,为了尽量让每个⼈分钱的概率差不多,⽤了下边的⽅法
a.先将钱按当前分钱的⼈数计算平均值
b.随机的钱数的取值范围是(1,平均值)
c.可以分配的总钱数减去⽣成的随机钱数,得到下⼀次分配时的可分配总钱数
d.重复a~c步骤,最终完成随机分配
按照以上的⽅法随机分完之后,消耗的总⾦额是⼀定⼩于等于输⼊的总⾦额的,那么,在处理完随机分配之后,还要对剩余的⾦额处理 这⾥实现的,就是将剩余的⾦额,再⽤平均分配的⽅式,分散到每⼀个⼈的⼿⾥
以上就是实现发红吧的⼤致思路,下边代码,就是根据这个思路整理⽽成
⼀、rand_money⽅法,完成⼀次随机分配
1/*
2 * 随机分钱
3 * 参数:$money,参与分钱的⾦额
4 * $num,参与分钱的⼈数
5*/
6function rand_money($money, $num)
7 {
8$arr = [];//结果数组
9$money = $money * 100;// 将元转成分(⼩数计算有误差,随机数也都是整数)
10$rest_money = $money;// 初始化,剩余钱的变量
11$average = $rest_money / $num;// 求出均分情况下,每⼈的红包值
12
13if ($average < 1) {// 钱不够所有⼈分
14return false;
15 }elseif($average == 1){// 所有⼈平均分这笔钱(钱数只够这么分的)
16for ($i=0; $i<$num; $i++) {
17$arr[] = $average;
18 }
19 }else{// 每个⼈随机分配
20for ($i=0; $i<$num; $i++) {
21$range_money = round(($rest_money / ($num - $i)));
22$rand_money = mt_rand(1, $range_money);
23$arr[] = $rand_money;
24$rest_money = $rest_money - $rand_money;// 获取剩下的钱
25 }
26 }
27return$arr;
28 }
⼆、average_money⽅法,既可以⾃⼰完成平均分配,⼜可以协助随机分配,完成剩余⾦额的分配
1function average_money($money, $num, $arr=[], $conversion_val=1)
2 {
3$money = $money * 100;
4$arr_sum = 0;//保存数组和
5if (count($arr) > 0) {// 随机分配,会调⽤此⽅法将剩余的钱分掉,此数组为随机分配后的结果
6foreach ($arr as$k=>$v) {
7$arr[$k] = $v * 100 / $conversion_val;// 如果单位有变化这调整⼀下,⼀直以分为单位处理数据
8 }
9$arr_sum = array_sum($arr);// 统计随机分配已经分配了总钱数
10 } else {
11for ($i=0; $i<$num; $i++) {
12$arr[] = 0;// 初始化每个⼈的数组,兼容下边循环处理部分
13 }
14 }
15$add_money = $money - $arr_sum;
16// 如果总钱数和之前随机分配的数组的总和差值为0,就说明随机分配已经将钱全部分出去了,就不需要再平均分配处理了17if ($add_money == 0) {
18return$arr;
19 }
20// 先把剩余的能均分的部分均分⼀下,然后若再有剩余,则从前到后,注意分配
21for ($i = 0; $i < $num; $i++) {
22$arr[$i] = $arr[$i] + floor($add_money / $num);// 如果之前有随机分配,则是将剩余的钱平均追加⼊随机分配的值⾥
23 }
24$arr_sum = array_sum($arr);// 分配后,求和,⽤于修正最后剩余的零钱
25 // 如果还有剩余,这部分说明每⼈⼀分都不够,就从头开始没⼈⼀分的分下去,直到分完为⽌
26$odd_money = bcsub($money, $arr_sum, 0);// 针对钱的计算,建议使⽤bc函数,普通的计算⽅法有误差
27$i = 0;
28while ($odd_money >= 1) {
29$arr[$i] = $arr[$i] + 1;// 每⼈加1分钱
30$odd_money = $odd_money - 1;// 剩余的⾦额,每分掉⼀个⼈,就减1分钱
31$i++;
32 }
33return$arr;
34 }
三、红包调⽤⽅法,根据不同类型,返回不同红包的结果
1/*
2 * 红包⽅法
3 * 参数:$money,参与分钱的⾦额
4 * $num,参与分钱的⼈数
5 * $type,红包类型,0平均分配,1随机分配
6*/
7function get_red ($money, $num, $type=0) {
8if ($type) { // ⾮0,随机红包
9$arr_rand = rand_money($money, $num);// 先随机分配
10$arr = average_money($money, $num, $arr_rand, 100);// 再平均分配
11 } else { // 平均分配红包
12$arr = average_money($money, $num);
13 }
14return$arr;
15 }
四、实例代码测试
1$a = get_red(66.61, 11, 0);
2//将最终结果,转换成元为单位
3foreach ($a as$k=>$val) {
4$a[$k] = $val / 100;
5 }
6print_r($a);
7echo '<br />'.array_sum($a);
8
9$r = get_red(66.61, 11, 1);
10//将最终结果,转换成元为单位
11foreach ($r as$k=>$val) {
12$r[$k] = $val / 100;
13 }
14echo '<br />';
15print_r($r);
16echo '<br />'.array_sum($r);
以上的代码,就完成了红包的操作。
这代码只是,简单的实现。
这其中还有特殊情况,⽐如,每次随机的数都是最⼩的数,虽然概率很低。
那么这种情况,只做⼀次随机分配,貌似效果并不好。
毕竟后边就是平均分配了,这样每⼀个⼈的终值⾮常接近平均值。
所以,可以考虑,在⼀次随机分配之后,计算已分配总钱数,根据该总钱数判断是否需要再次进⾏随机分配,然后将两次或者多次随机分配的值同key合并。
最后再把剩余的⾦额“平均分配”后,同key加到⼀起。
这样的结果效果更好。
注意:
1、⽇常⼈们习惯⾦钱的单位都是“元”,但这⾥,尽量转成“分”;⼩数计算误差⼤,随机数⽣成也都是整数
2、如果可以,⾦钱在计算时,尽量使⽤bc⾼精度函数。
如:bcadd(加),bcsub(减),bcmul(乘),bcdiv(除)等。