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

PHP 微信紅包隨機生成演算法:兩種實作風格對比與優化

     編輯:PHP 相關知識

PHP 微信紅包隨機生成演算法:兩種實作風格對比與優化

本文提供兩種風格截然不同的 PHP 紅包生成函數:循環遞減法與區間切分法,並分析其優缺點、適用場景,最後給出生產環境優化建議。

版本一:循環遞減法(直觀易懂)

<?php
/**
 * @param float $total 總金額(元)
 * @param int $count 紅包數
 * @param int $type 1=拼手氣, 0=固定平均
 * @return array 紅包金額陣列(元)
 */
function randBonus($total = 0, $count = 3, $type = 1) {
    $items = [];
    $balance = $total;
    $avg = number_format($total / $count, 2);

    for ($i = 0; $i < $count; $i++) {
        if ($i < $count - 1) {
            // 拼手氣:隨機 0.01 ~ (餘額-剩餘最小保障)
            // 固定:直接用平均值
            $rand = $type ? (rand(1, $balance * 100 - 1) / 100) : $avg;
            $items[] = $rand;
            $balance -= $rand;
        } else {
            // 最後一包承擔所有誤差
            $items[] = $balance;
        }
    }
    return $items;
}

// 測試
$bonus = randBonus(100, 3, 1);
var_dump($bonus);
var_dump(array_sum($bonus)); // 驗證總和
?>

版本二:區間切分法(數學巧思)

<?php
function sendRandBonus($total = 0, $count = 3, $type = 1) {
    $items = [];

    if ($type == 1) {
        // 將 [0.01, $total] 以 0.01 為步長離散化
        $points = range(0.01, $total, 0.01); // 陣列長度 = $total * 100
        // 隨機抽取 $count-1 個切分點
        $keys = array_rand($points, $count - 1);
        sort($keys);

        $last = 0;
        foreach ($keys as $k) {
            $items[] = $points[$k] - $last;
            $last = $points[$k];
        }
        $items[] = $total - array_sum($items);
    } else {
        $avg = number_format($total / $count, 2);
        for ($i = 0; $i < $count; $i++) {
            $items[] = ($i < $count - 1) ? $avg : ($total - array_sum($items));
        }
    }
    return $items;
}
?>

兩版本深度對比

維度循環遞減法區間切分法
時間複雜度O(N)O(M) 其中 M = 總金額(分),大額紅包極慢
記憶體O(1)O(M) 需建立巨大陣列
隨機分布前期偏大,後期極小理論上均勻分布於所有合法組合
浮點誤差需注意 number_format整數運算天然避免
適用場景金額小、紅包少、快速原型教學展示、極小金額(如 1 元以內)

生產環境必備優化(基於版本一改良)

<?php
function generateRedPackets(int $totalCents, int $count, int $minCents = 1): array {
    // 參數檢查
    if ($totalCents < $count * $minCents) throw new \InvalidArgumentException('總金額不足');

    $packets = [];
    $remaining = $totalCents;

    for ($i = 1; $i < $count; $i++) {
        // 動態上限:(餘額 - 剩餘最小保障) / 剩餘人數
        $max = ($remaining - ($count - $i) * $minCents) / ($count - $i);
        $max = min($max, $remaining - ($count - $i) * $minCents); // 安全邊界

        $val = mt_rand($minCents, (int)$max);
        $packets[] = $val;
        $remaining -= $val;
    }
    $packets[] = $remaining; // 最後一包

    shuffle($packets); // 打亂順序,消除順序偏差
    return $packets;
}

// 使用:200 分,8 包,最小 1 分
$packets = generateRedPackets(200, 8);
echo "總和:" . array_sum($packets) . " 分\n";
print_r($packets);
?>

關鍵優化點

  1. 全程整數運算(分):消除浮點誤差。
  2. 動態上限公式:(剩餘 - 剩餘人數×最小) / 剩餘人數,使分布集中於平均值。
  3. mt_rand 替代 rand:速度更快、隨機品質更好。
  4. shuffle 打亂:避免「手快有優勢」的順序公平性問題。
  5. 參數前置檢查:防止非法輸入導致邏輯錯誤。

延伸閱讀

更多 PHP 高併發紅包架構、Redis Lua 原子腳本、常態分布紅包演算法、金融級精度運算,請參考本站專題系列。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved