­

抽獎活動 演算法設計

場景

  • 線上抽獎
  • 活動方設定了獎品數量,不允許超發

項目變數

  • 參與人數未知
  • 活動持續時間未知(活動方可能根據活動熱度增減時間,以月為單位)
  • 可預見的『老闆需求』:活動人數太多了,臨時增加1個大獎 & 無數個小獎。或者大獎太少,臨時增加出大獎概率刺激活動效果。

舉例:獎池設定

  • 一等獎: 2個
  • 二等獎:30個
  • 三等獎:500個
  • 四等獎:20000個

實現方案討論

  • rand 程式隨機? 可能會超發,據說有策劃團隊已經賠過錢了
  • 提前把獎品數量轉化為資料庫的記錄。用戶抽獎時,利用資料庫的隨機查詢 select * from 獎池表 where status = '未被抽中' order by rand() limit 0,1 。問題:記錄數太多,調整(增減)獎品數量不靈活,方案不優雅

最終方案

  • 每次開獎結果:rand(1, 當時的獎品總數) ,具體看程式碼
  • 優勢:隨時增減獎池數量,沒有關聯其它表(靈活、解耦),不會超發

程式碼

<?php
// 設置的獎品資訊
$priceInfo = [
    '102'  => 3, // 獎品id,獎品數量
    '203'  => 10,
    '3092' => 100,
];

// 抽獎函數
function getPrice(&$priceInfo)
{
    if (!array_sum($priceInfo)) {
        return 0; //獎池已空
    }
    $randId = rand(1, array_sum($priceInfo)); // 170

    $i = 0;
    foreach ($priceInfo as $id => $num) {
        $i += $num;
        if ($i >= $randId) {
            $priceInfo[$id]--;
            return $id;
        }
    }
}
// 模擬100次抽獎
for ($i = 0; $i < 130; $i++) {
    echo "\n 中獎ID: " . getPrice($priceInfo);
}

//剩餘的獎品
echo "\n 獎池狀態:\n";
print_r($priceInfo);
Tags: