php实现抽奖 概率
PHP实现抽奖概率的方法
方法一:使用数组和随机数
创建一个包含奖项和对应概率的数组,使用随机数进行抽奖判断。

$prizes = [
['name' => '一等奖', 'probability' => 0.01],
['name' => '二等奖', 'probability' => 0.05],
['name' => '三等奖', 'probability' => 0.1],
['name' => '谢谢参与', 'probability' => 0.84]
];
function drawLottery($prizes) {
$rand = mt_rand(1, 10000) / 10000;
$currentProbability = 0;
foreach ($prizes as $prize) {
$currentProbability += $prize['probability'];
if ($rand <= $currentProbability) {
return $prize['name'];
}
}
return end($prizes)['name'];
}
echo drawLottery($prizes);
方法二:使用权重分配
为每个奖项分配权重值,计算总权重后随机选择。

$prizes = [
['name' => 'iPhone', 'weight' => 1],
['name' => 'iPad', 'weight' => 5],
['name' => '优惠券', 'weight' => 20],
['name' => '谢谢参与', 'weight' => 74]
];
function weightedDraw($prizes) {
$totalWeight = array_sum(array_column($prizes, 'weight'));
$rand = mt_rand(1, $totalWeight);
$currentWeight = 0;
foreach ($prizes as $prize) {
$currentWeight += $prize['weight'];
if ($rand <= $currentWeight) {
return $prize['name'];
}
}
}
echo weightedDraw($prizes);
方法三:概率递减算法
适用于需要保证某些奖项必中或限制次数的场景。
$prizes = [
['name' => '特等奖', 'initial_prob' => 0.01, 'remaining' => 1],
['name' => '一等奖', 'initial_prob' => 0.05, 'remaining' => 5],
['name' => '二等奖', 'initial_prob' => 0.1, 'remaining' => 10],
['name' => '参与奖', 'initial_prob' => 1, 'remaining' => 1000]
];
function decreasingProbabilityDraw($prizes) {
$totalRemaining = array_sum(array_column($prizes, 'remaining'));
if ($totalRemaining <= 0) return '奖品已发完';
$adjustedProbs = [];
foreach ($prizes as $prize) {
if ($prize['remaining'] > 0) {
$adjustedProbs[] = [
'name' => $prize['name'],
'prob' => $prize['initial_prob'] * ($prize['remaining'] / $totalRemaining)
];
}
}
$rand = mt_rand(1, 10000) / 10000;
$currentProb = 0;
foreach ($adjustedProbs as $prize) {
$currentProb += $prize['prob'];
if ($rand <= $currentProb) {
return $prize['name'];
}
}
return '谢谢参与';
}
echo decreasingProbabilityDraw($prizes);
方法四:使用概率别名算法
对于大量奖品的高效抽样方法,适合复杂概率分布场景。
class AliasMethod {
private $n;
private $prob;
private $alias;
public function __construct($weights) {
$this->n = count($weights);
$this->prob = array_fill(0, $this->n, 0);
$this->alias = array_fill(0, $this->n, 0);
$sum = array_sum($weights);
$scaled = array_map(function($w) use ($sum) {
return $w * $this->n / $sum;
}, $weights);
$small = [];
$large = [];
foreach ($scaled as $i => $w) {
if ($w < 1) {
$small[] = $i;
} else {
$large[] = $i;
}
}
while (!empty($small) && !empty($large)) {
$l = array_pop($small);
$g = array_pop($large);
$this->prob[$l] = $scaled[$l];
$this->alias[$l] = $g;
$scaled[$g] = ($scaled[$g] + $scaled[$l]) - 1;
if ($scaled[$g] < 1) {
$small[] = $g;
} else {
$large[] = $g;
}
}
foreach ($large as $i) {
$this->prob[$i] = 1;
}
foreach ($small as $i) {
$this->prob[$i] = 1;
}
}
public function draw() {
$i = mt_rand(0, $this->n - 1);
if (mt_rand() / mt_getrandmax() <= $this->prob[$i]) {
return $i;
} else {
return $this->alias[$i];
}
}
}
$weights = [1, 2, 3, 4]; // 各奖项权重
$sampler = new AliasMethod($weights);
$result = $sampler->draw(); // 返回抽中的索引
注意事项
- 随机数生成建议使用
mt_rand()而非rand(),前者具有更好的随机性和性能 - 浮点数比较时考虑精度问题,可使用整数运算避免
- 高并发场景下需考虑原子操作或使用数据库事务
- 实际应用中建议记录中奖日志,便于统计和审计
这些方法可根据实际需求组合使用,复杂抽奖系统可能需要结合数据库记录奖品库存和用户抽奖次数限制。






