首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 抽獎程序概率算法:加權隨機、獎品配置與前後端交互實現

     編輯:PHP 綜合知識

核心算法:加權隨機選擇(O(N) 時間、O(1) 空間)

/**


 * 根據權重數組返回中獎索引
 * @param array $weights 索引=>權重值(整數),如 [100, 200, 300]
 * @return int 中獎項的鍵名
 */
function getRand(array $weights) {
    $total = array_sum($weights); // 權重總和
    foreach ($weights as $key => $weight) {
        $rand = mt_rand(1, $total); // 1 到總權重
        if ($rand <= $weight) {
            return $key; // 命中
        }
        $total -= $weight; // 未命中,縮小範圍
    }
    return array_key_last($weights); // 理論不可達,兜底返回最後一項
}

原理解析:將權重視為數軸區間長度。如權重 [10, 20, 30] 總和 60。隨機數落在 1-10 選第 0 項,11-30 選第 1 項,31-60 選第 2 項。每次未命中扣除當前權重,等價於在剩餘區間重新隨機。

獎品配置結構

$prizes = [
    ['id' => 1, 'name' => '平板電腦',      'weight' => 1,   'limit' => 10],   // 總量限制
    ['id' => 2, 'name' => '數碼相機',      'weight' => 5,   'limit' => 50],
    ['id' => 3, 'name' => '音箱設備',      'weight' => 10,  'limit' => 200],
    ['id' => 4, 'name' => '4G 優盤',       'weight' => 12,  'limit' => 500],
    ['id' => 5, 'name' => '10 Q幣',        'weight' => 22,  'limit' => 1000],
    ['id' => 6, 'name' => '下次沒準就能中', 'weight' => 50,  'limit' => 0],    // 0=無限
];

完整後端邏輯 (ajax_lottery.php)

 0 ? ($p['limit'] - getAwardedCount($p['id'])) : INF;
    if ($remaining > 0) {
        $weights[$p['id']] = $p['weight'];
        $validPrizes[$p['id']] = $p;
    }
}

// 3. 執行抽獎
if (empty($weights)) {
    echo json_encode(['code' => 400, 'msg' => '獎品已發完']);
    exit;
}

$wonId = getRand($weights);
$wonPrize = $validPrizes[$wonId];

// 4. 記錄中獎(資料庫事務)
$awarded = recordAward($wonPrize['id'], $_SESSION['user_id']);
if (!$awarded) {
    echo json_encode(['code' => 500, 'msg' => '系統繁忙']);
    exit;
}

// 5. 構建返回:中獎項 + 未中獎項(供前端展示)
unset($validPrizes[$wonId]);
$otherPrizes = array_values($validPrizes);
shuffle($otherPrizes); // 打亂順序

echo json_encode([
    'code' => 200,
    'win' => ['id' => $wonPrize['id'], 'name' => $wonPrize['name']],
    'others' => array_map(fn($p) => ['id' => $p['id'], 'name' => $p['name']], $otherPrizes)
]);

前端交互流程

// 用戶點擊「開始抽獎」
$('#lottery-btn').click(function() {
    $(this).prop('disabled', true).text('抽獎中...');
    
    $.post('ajax_lottery.php', {}, function(res) {
        if (res.code !== 200) {
            alert(res.msg);
            $('#lottery-btn').prop('disabled', false).text('開始抽獎');
            return;
        }
        
        // 動畫展示(簡化:直接顯示結果)
        showResult(res.win, res.others);
        $('#lottery-btn').prop('disabled', false).text('再來一次');
    }, 'json');
});

function showResult(win, others) {
    // 翻轉動畫、彈窗等 UI 邏輯
    console.log('恭喜獲得:', win.name);
    console.log('其他獎品:', others.map(o => o.name).join(', '));
}

進階:陣列展開法(簡單、適合固定總量)

function getRewardSimple($total = 10000) {
    $pool = [];
    // 按權重比例填充池
    $pool = array_merge($pool, array_fill(0, 12, '平板電腦'));     // 0.12%
    $pool = array_merge($pool, array_fill(0, 300, '數碼相機'));    // 3%
    $pool = array_merge($pool, array_fill(0, 1200, '音箱設備'));   // 12%
    $pool = array_merge($pool, array_fill(0, 8488, '謝謝惠顧'));   // 84.88%
    
    shuffle($pool); // 打亂
    return $pool[array_rand($pool)];
}

優點:代碼極簡、概率精確可控。
缺點:記憶體佔用大(百萬獎池需百萬元素數組)、動態調整權重需重建數組。

防作弊與公平性保障

風險對策
前端篡改請求參數服務端驗證用戶身份、IP 頻率限制、Token 一次性驗證
內部人員操控權重權重配置上鏈/存儲不可變日誌、多簽審批修改
機器人批量刷獎驗證碼、設備指紋、行為分析、賬號等級門檻
中獎結果可預測使用 CSPRNG (random_int) 替代 mt_rand、服務端種子加密

總結

  • 核心算法 getRand() 高效、記憶體恆定、易擴展動態權重。
  • 獎品配置分離,支援庫存限制、動態上下架。
  • 前後端分離:後端只負責概率計算與狀態持久化,前端負責動畫展示。
  • 生產環境必須加上防刷、審計日志、數據一致性(事務/樂觀鎖)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved