前言
函数要实现的功能:设置N
个奖品,为每个奖品设置一个抽中概率,然后程序根据概率进行抽奖。
代码
<?php
function luckyDraw(array $prizePool)
{
$precision = 10000;
// 检查奖品池的概率总和是否等于1
$probabilitySum = 0;
foreach ($prizePool as $prize) {
if ($prize['weight'] > 1) {
throw new Exception("奖品的概率设置不能大于1:{$prize['weight']}");
}
if (!preg_match('/^[01](\.\d{0,4})?$/', strval($prize['weight']))) {
throw new Exception("概率数值不合法({$prize['weight']}):仅支持设置0-1之间的数值,小数精确度最多只能有4位");
}
$probabilitySum += $prize['weight'];
}
if ($probabilitySum != 1) {
throw new Exception("概率之和不为1:$probabilitySum");
}
// 开始抽奖
$result = null;
$rand = mt_rand(1, $precision);
$offset = 0;
foreach ($prizePool as $prize) {
$start = $offset + 1;
$end = intval($offset + ($prize['weight'] * $precision));
$offset = $end;
if ($rand >= $start && $rand <= $end) {
$result = $prize;
break;
}
}
if ($result === null) {
throw new Exception("抽奖失败!");
}
return $result;
}
$pool = [
[
'name' => '一等奖',
'weight' => 0.0001, // 抽中概率,可以设置0-1之间的任意数值,小数精确度最多支持4位,例如 0.0001 代表是 0.01%
],
[
'name' => '二等奖',
'weight' => 0.0099, // 0.99%
],
[
'name' => '三等奖',
'weight' => 0.05, // 5%
],
[
'name' => '谢谢惠顾',
'weight' => 0.94, // 94%
],
];
$result = luckyDraw($pool);
echo "您抽中的奖品是:" . json_encode($result, JSON_UNESCAPED_UNICODE) . "\n";
// TODO:如果奖品有库存限制,可以再加上判断库存的逻辑,如果抽中奖品的库存已经为0,则返回”谢谢惠顾“给用户
测试
抽奖10万次,看看结果符不符合我们设置的抽中概率,测试代码:
$stat = [];
for ($i = 0; $i < 100000; $i++) {
$result = luckyDraw($pool);
$stat[] = $result['name'];
}
var_dump(array_count_values($stat));
// 结果:
// 一等奖 8 次(概率 0.008%)
// 二等奖 1014 次(概率 1.014%)
// 三等奖 4943 次(概率 4.943%)
// 谢谢惠顾 94035 次(概率 94.035%)
从结果看是符合我们设置的抽中概率的:)