概率演算法在抽獎系統、遊戲掉寶、廣告加權展示等場景極為常見。本文匯總三種經典 PHP 實現,從面向對象封裝到純函數式寫法,滿足不同業務需求。
封裝 Probability 類,支援動態添加獎項與權重,內部維護總權重池,getOne() 方法基於隨機數落區間原理返回結果。
<?php
class Probability {
private $data = [];
private $chance_count = 0;
public function __construct(array $initdata = []) {
if (!empty($initdata)) {
$this->data = $initdata;
foreach ($initdata as $d) {
$this->chance_count += $d['num'];
}
}
}
public function addData(string $name, int $chance): void {
$this->data[] = ['name' => $name, 'num' => $chance];
$this->chance_count += $chance;
}
public function getOne(): string {
$index = rand(0, $this->chance_count);
foreach ($this->data as $d) {
$index -= $d['num'];
if ($index <= 0) return $d['name'];
}
return '';
}
}
// 使用示例
$pro = new Probability();
$pro->addData('iPhone', 10);
$pro->addData('Watch', 30);
$pro->addData('$18', 50);
$pro->addData('Thank you', 10);
$pro->addData('Super big', 1);
for ($i = 0; $i < 100; $i++) {
echo $pro->getOne() . "\n";
}
?>
核心邏輯:計算總權重,遍歷獎項陣列,每輪生成 1~剩餘總權重的隨機數,若落在當前獎項權重內則命中,否則扣除當前權重進入下一輪。時間複雜度 O(n),極適合大數據量場景。
<?php
function get_rand(array $proArr): string {
$result = '';
$proSum = array_sum($proArr);
foreach ($proArr as $key => $proCur) {
$randNum = mt_rand(1, $proSum);
if ($randNum <= $proCur) {
$result = $key;
break;
} else {
$proSum -= $proCur;
}
}
return $result;
}
// 測試:a:20%, b:30%, c:50%
$prize_arr = ['a' => 20, 'b' => 30, 'c' => 50];
for ($i = 0; $i < 10; $i++) {
echo get_rand($prize_arr) . " ";
}
?>
預先計算每個獎項的累積權重臨界值,生成單一隨機數,判斷落在哪個區間。邏輯直觀,適合權重固定、頻繁抽獎的場景。
<?php
function get_rand_v2(array $arr): string {
$pro_sum = array_sum($arr);
$rand_num = mt_rand(1, $pro_sum);
$tmp_num = 0;
foreach ($arr as $k => $val) {
if ($rand_num <= $val + $tmp_num) return $k;
$tmp_num += $val;
}
return '';
}
?>
<?php
$prize_arr = [
['id'=>1, 'prize'=>'平板電腦', 'v'=>1],
['id'=>2, 'prize'=>'數碼相機', 'v'=>5],
['id'=>3, 'prize'=>'音箱設備', 'v'=>10],
['id'=>4, 'prize'=>'4G優盤', 'v'=>12],
['id'=>5, 'prize'=>'10Q幣', 'v'=>22],
['id'=>6, 'prize'=>'下次沒準就能中哦', 'v'=>50],
];
// 構建權重陣列
foreach ($prize_arr as $val) {
$arr[$val['id']] = $val['v'];
}
$rid = get_rand($arr); // 獲取中獎 ID
$res['yes'] = $prize_arr[$rid-1]['prize']; // 中獎獎品
// 剔除已中獎項,打亂剩餘順序
unset($prize_arr[$rid-1]);
shuffle($prize_arr);
$res['no'] = array_column($prize_arr, 'prize');
header('Content-Type: application/json');
echo json_encode($res);
?>