业务中经常会有返红包的需求,例如给一定的金额总数,发放红包的个数,每个红包的最大金额和最小金额,要随机生成指定个数的红包,符合上述条件并且红包总额不能超过金额总数。
思路:
平均红包金额数
平均红包金额上下浮动即为实际用户红包金额
上下浮动的计算 :需要红包上下边界 通过取-1 ~ 1之间的随机数获得
在不考虑正态分布的情况下可用下面代码实现:
/**
* 获取随机红包
* min<k<max
* min(n-1) <= money - k <= (n-1)max
* k <= money-(n-1)min
* k >= money-(n-1)max
*
* @param $money int 总金额, 单位分
* @param $num int 红包个数
* @param $min int 红包最小金额
* @param $max int 红包最大金额
* @return array
*/
function getRedPackage($money, $num, $min, $max)
{
$data = [];
if ($min * $num > $money) {
return [];
}
if ($max * $num < $money) {
return [];
}
while ($num >= 1) {
$num--;
# 计算真实金额边界,既要符合金额大小条件,又要考虑实际剩余金额数和红包数
$kmin = max($min, $money - $num * $max);
$kmax = min($max, $money - $num * $min);
# 平均红包金额数
$kAvg = $money / ($num + 1);
# 获取单个红包金额最大浮动;最大值和最小值的距离平均值之间的小的那个
$kDis = min($kAvg - $kmin, $kmax - $kAvg);
# 获取-1到1之间的随机数,与浮动区间相乘,这使得浮动区间不会超出范围
$r = ((float)(rand(-10000, 10000)/10000)) * $kDis;
$k = round($kAvg + $r);
$money -= $k;
$data[] = $k;
}
return $data;
}
$data = getRedPackage(100000, 1000, 10, 1000);
这个算法的原理其实就是根据剩余钱数不断变化的平均值去加减随机数做到不超过总额,但红包的分布就没那么平均。