How WeChat Red‑Packet Random Algorithms Work: Simple Random, Double‑Mean & Segment Split
This article examines the main algorithms behind WeChat's red‑packet distribution—plain random, double‑mean, and segment‑split (including an optimized version), explains their PHP implementations, validates array_rand's randomness, and compares their performance and fairness through code examples and timing tests.
Introduction
The original WeChat red‑packet design evolved from multi‑tiered proportional allocation to pure randomness because users found the former less fun. To avoid early claimers always receiving larger amounts, the random range is adjusted. This article presents three mainstream algorithms, their PHP implementations, and a performance comparison.
1. Plain Random Method
Also called "remaining‑value random", this method selects a random number from the remaining total for each user. It is simple but can produce highly uneven distributions, e.g., the first few users may receive most of the money.
// Remaining‑value random – simple logic, but the random interval shrinks each step, leading to unfairness.
$res = LeftMoneyRedbag($Moneys, $userNums, $isEveryHave);
function LeftMoneyRedbag($Moneys, $userNums, $isEveryHave = 1, $baseMoney = 1) {
if ($Moneys <= 0 || $userNums <= 0) {
return ['code'=>-3, 'msg'=>'Invalid amount or user count'];
}
if ($isEveryHave && $userNums > $Moneys) {
return ['code'=>-4, 'msg'=>'Insufficient amount'];
}
if ($isEveryHave) {
$Moneys = $Moneys - ($userNums * $baseMoney);
}
$userMoney = [];
$leftMoneys = $Moneys;
$leftUserNums = $userNums;
while ($leftUserNums > 1) {
$RandVal = 0;
if ($leftMoneys > 0) {
$RandVal = mt_rand(0, $leftMoneys);
$leftMoneys -= $RandVal;
}
$userMoney[] = $isEveryHave ? ($baseMoney + $RandVal) : $RandVal;
$leftUserNums--;
}
$userMoney[] = $isEveryHave ? ($baseMoney + $leftMoneys) : $leftMoneys;
return ['code'=>0, 'msg'=>'success', 'redbag'=>$userMoney];
}2. Double‑Mean Algorithm
This algorithm uses twice the average of the remaining amount as the upper bound for each random draw, producing a distribution closer to a normal curve. The multiplier can be increased (triple‑mean, etc.) for more randomness.
$Moneys = 99 * 10; // amount in cents
$userNums = 990;
$res = doubleMeanRedbag($Moneys, $userNums);
function doubleMeanRedbag($Moneys, $userNums, $isEveryHave = 1, $baseMoney = 1) {
if ($Moneys <= 0 || $userNums <= 0) return ['code'=>-3, 'msg'=>'Invalid'];
if ($isEveryHave && $userNums > $Moneys) return ['code'=>-4, 'msg'=>'Insufficient'];
if ($isEveryHave) $Moneys -= $userNums * $baseMoney;
$userMoney = [];
$leftMoneys = $Moneys;
$leftUserNums = $userNums;
while ($leftUserNums > 1) {
$RandVal = 0;
if ($leftMoneys > 0) {
$doubleMeans = ceil($leftMoneys / $leftUserNums * 2);
$RandVal = mt_rand(0, $doubleMeans);
$leftMoneys -= $RandVal;
}
$userMoney[] = $isEveryHave ? ($baseMoney + $RandVal) : $RandVal;
$leftUserNums--;
}
$userMoney[] = $isEveryHave ? ($baseMoney + $leftMoneys) : $leftMoneys;
return ['code'=>0, 'msg'=>'success', 'redbag'=>$userMoney];
}3. Segment‑Split Algorithm
The segment‑split method treats the total amount as a line segment and randomly selects N‑1 cut points to divide it into N parts. A naïve implementation may suffer from cut‑point collisions when the number of users approaches the total amount.
function lineSegmentRedbag($Moneys, $userNums, $isEveryHave = 1, $baseMoney = 1) {
if ($Moneys <= 0 || $userNums <= 0) return ['code'=>-3, 'msg'=>'Invalid'];
if ($isEveryHave && $userNums > $Moneys) return ['code'=>-4, 'msg'=>'Insufficient'];
$cutPoints = [];
$pointNums = $userNums - 1;
$userMoney = [];
while ($pointNums > 0) {
$randVal = $isEveryHave ? mt_rand(1, $Moneys-1) : mt_rand(0, $Moneys);
if (in_array($randVal, $cutPoints)) continue; // avoid collision
$cutPoints[] = $randVal;
$pointNums--;
}
sort($cutPoints);
$lastVal = 0;
foreach ($cutPoints as $randPoint) {
$userMoney[] = $randPoint - $lastVal;
$lastVal = $randPoint;
}
$userMoney[] = $Moneys - $lastVal;
return ['code'=>0, 'msg'=>'success', 'redbag'=>$userMoney];
}3.1 Optimized Segment‑Split Using array_rand
To eliminate collision handling, array_rand can pick multiple unique indices at once. The function below demonstrates this optimization.
function lineSegmentOptimize($Moneys, $userNums, $isEveryHave = 1) {
if ($Moneys <= 0 || $userNums <= 0) return ['code'=>-3, 'msg'=>'Invalid'];
if ($isEveryHave && $userNums > $Moneys) return ['code'=>-4, 'msg'=>'Insufficient'];
$MoneysArr = $isEveryHave ? array_fill(1, $Moneys-1, 0) : array_fill(0, $Moneys+1, 0);
if ($userNums == 1) return ['code'=>0, 'msg'=>'success', 'redbag'=>[$Moneys]];
$cutPoints = array_rand($MoneysArr, $userNums-1);
sort($cutPoints);
$lastVal = 0;
$userMoney = [];
foreach ($cutPoints as $randPoint) {
$userMoney[] = $randPoint - $lastVal;
$lastVal = $randPoint;
}
$userMoney[] = $Moneys - $lastVal;
return ['code'=>0, 'msg'=>'success', 'redbag'=>$userMoney];
}4. Verifying array_rand Randomness
Experiments show that array_rand($arr, k) returns k unique indices and the selection covers the full range, including both ends.
function checkRand($range, $num) {
$statiArr = array_fill(0, 100, 0);
$sourceArr = range(0, 99);
for ($i=0;$i<$num;$i++) {
$indexArr = array_rand($sourceArr, 4);
foreach ($indexArr as $index) {
$statiArr[$index]++;
}
}
return $statiArr;
}
$res = checkRand(10, 10000);
var_dump($res);5. Performance Comparison
A micro‑benchmark runs each algorithm 100 000 times and measures execution time. Results show the optimized segment‑split is faster than the double‑mean method and far quicker than the naïve segment‑split.
function microTime_float(){
list($usec,$sec)=explode(' ',microtime());
return ((float)$usec+(float)$sec);
}
$starTime = microTime_float();
for($i=0;$i<100000;$i++){
lineSegmentRedbag($Moneys,$userNums,$isEveryHave);
// lineSegmentOptimize($Moneys,$userNums,$isEveryHave);
// doubleMeanRedbag($Moneys,$userNums,$isEveryHave);
}
$endTime = microTime_float();
$diff = $endTime - $starTime;
echo "Line‑segment time: $diff
";Graphs (omitted here) illustrate that the naïve segment‑split suffers from high collision rates when the total amount and user count are close, while the optimized version achieves the best performance and a larger random interval than the double‑mean method.
Conclusion
Three red‑packet algorithms each have trade‑offs: plain random is simple but unfair, double‑mean offers a balanced distribution, and segment‑split provides a wide random range; the optimized segment‑split using array_rand combines fairness with high efficiency, making it a strong candidate for production use.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Tencent Cloud Developer
Official Tencent Cloud community account that brings together developers, shares practical tech insights, and fosters an influential tech exchange community.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
