嘿,老板在群里发了个10000的红包,大家快去抢呀!!!!!
来自灵魂深处的拷问:第一个抢的就一定得到的钱多吗?这个还真的要看红包算法是如何实现的呢。说不定,有个程序员用了一个神奇的算法呢。下面介绍两种常用的红包算法:
假设红包总金额m元,红包可抢人数n。保证每个人都有份,且单个红包金额最小0.01。
(为了方便计算,代码中我们将金额转换为分制)
1、二倍均值法
先求出当前余额和可抢人数的一个平均值,根据正态分布的思维,尽量保证结果靠近我们的平均值,则可得每次抢到的金额取值范围为 (0, (m / n) * 2)
假设m=100,n=10;
第一个人抢到金额 (0, (100 / 10) * 2) = (0, 20),假设抢到了10元;
第二个人抢到金额 (0, (90 / 9) * 2) = (0, 20),假设抢到了10元;
......
根据正态分布来看,第一个人抢到金额的均值稳定在10附近,此类类推。代码参考:
<?php
$num = 10;
$sum = 100;
//转换为分
$sum - $sum * 100;
//计算所有的随机结果
$ret = [];
while ($num > 1) {
$rand = mt_rand(1, ($sum/$num)*2);
$ret[] = $rand;
$sum -= $rand;
$num--;
}
//计算最后一个人的
$ret[] = $sum;
print_r($ret);
exit;通过分析可得:假如第一个人抢到了19元,那么第二个人抢到金额的范围为(0, 18),假如第二个抢到了17元,那么第三个人抢到金额的范围为(0, 16),可以发现,前面的人抢到的金额越多,对后面的人越不利。所以在抢红包这件事上,先下手为强有时候也是有利的哦~~~
2、线段切割法
即将一条长度为 m 的线段切割成 n 份。需要切割 n-1 次,每一次切割的长度随机为 (0, m)。
需要注意一个问题:如果两次随机的值一样,为了确保每个人都能领取到,需要重新处理。
代码参考:
<?php
$num = 5;
$sum = 100;
//转换为分
$sum = $sum * 100;
//计算所有的随机结果
$rand = [];
$i = 1;
while ($i < $num) {
$rand_detail = mt_rand(1, $sum-1);
if(!in_array($rand_detail, $rand, true)){
$rand[] = $rand_detail;
$i ++;
}
}
//对随机正序排
sort($rand);
//即按照每个人抢红包的顺序依次分配
$ret = [];
foreach ($rand as $key => $value) {
if($key == 0){
$ret[] = $value;
}else{
$ret[] = $value - $rand[$key-1];
}
}
//计算最后一个人的金额
$ret[] = $sum - $rand[$num-2];
print_r($ret);代码中为何要进行所有随机值的排序?画图更容易理解一些。如图,我们通过 5 次的随机值结果正序减法,即将 100 元红包拆分为了 10 份。
