微信紅包核心邏輯:給定總金額、人數,隨機分配每個紅包金額,滿足每人≥0.01元、總和精確等於總額、金額有差異。本文提供標準算法、代碼實現、邊界處理及擴展建議。
遞歸/迭代思想:
第 i 個紅包(i < N)可分配範圍:
min = 0.01
max = (剩餘總額 - (剩餘人數 - 1) * 0.01) / 剩餘人數 * 2(乘 2 讓隨機波動更大,非必須)
最後一個紅包 = 剩餘總額。
function splitRedPacket(int $totalCents, int $num): array {
if ($num <= 0 || $totalCents < $num) {
throw new InvalidArgumentException("總金額(分)必須 ≥ 人數");
}
$packets = [];
$remaining = $totalCents;
for ($i = 1; $i < $num; $i++) {
$min = 1; // 0.01元 = 1分
$max = (int)(($remaining - ($num - $i) * $min) / ($num - $i) * 2);
if ($max < $min) $max = $min;
$packet = mt_rand($min, $max);
$packets[] = $packet;
$remaining -= $packet;
}
$packets[] = $remaining; // 最後一個拿走所有剩餘
return $packets;
}
// 使用示例
$total = 2000; // 20.00 元 = 2000 分
$num = 10;
$packets = splitRedPacket($total, $num);
echo "總金額: {$total} 分\n";
foreach ($packets as $i => $p) {
echo "第 " . ($i+1) . " 個: " . ($p/100) . " 元\n";
}
echo "驗證總和: " . array_sum($packets) . " 分\n";
總金額: 2000 分 第 1 個: 2.08 元 第 2 個: 1.81 元 第 3 個: 0.15 元 第 4 個: 1.61 元 第 5 個: 1.11 元 第 6 個: 1.51 元 第 7 個: 1.21 元 第 8 個: 2.58 元 第 9 個: 5.40 元 第 10 個: 2.54 元 驗證總和: 2000 分
| 問題 | 解決方案 |
|---|---|
| 浮點精度丟失 | 全部用分(整數)計算,輸出時除 100 |
| 極端分配(如前幾個拿走大部分) | 調整 max 公式:max = min + (remaining - min*left) * 0.5 等策略,或引入「二倍均值」上限 |
| 並發搶紅包 | 預先生成紅包陣列存入 Redis List,搶紅包用 LPOP 原子操作 |
| 超大紅包(如 100 萬人) | 分批生成、異步入隊、分片存儲 |
| 公平性/體驗優化 | 引入「手氣最佳」標記、最小金額隨機化、防連續極小值 |