PHP Implementation of WeChat Red Packet Splitting Algorithm
This article demonstrates a PHP class that implements the WeChat red packet splitting algorithm, explaining the constraints, providing a complete source code example, and showing how to instantiate the class to distribute a total amount into a specified number of packets with a minimum value.
The article introduces the problem of splitting a WeChat red packet (hongbao) into multiple smaller packets using PHP, describing the two main constraints: each packet amount is limited to two decimal places and a minimum amount can be set for each packet.
It then presents a complete PHP class named Coupon that encapsulates the algorithm. The class stores the total amount, the number of packets, the minimum per‑packet amount, and the resulting items array. It provides a constructor for initialization, a handle() method that validates inputs and performs the allocation, and several protected helper methods ( apportion() , calcCouponAmount() , calcCouponAmountMax() , apportionRandRatio() , and decimal_number() ) that implement the random distribution logic while respecting the constraints.
<?php
class Coupon {
protected $amount;
protected $num;
protected $coupon_min;
protected $items = [];
/**
* Initialize the object
* @param float $amount Total amount (max 2 decimal places)
* @param int $num Number of packets
* @param float $coupon_min Minimum amount per packet
*/
public function __construct($amount, $num = 1, $coupon_min = 0.01) {
$this->amount = $amount;
$this->num = $num;
$this->coupon_min = $coupon_min;
}
/**
* Process and return the allocation result
* @return array
*/
public function handle() {
// A. Validation
if ($this->amount < $validAmount = $this->coupon_min * $this->num) {
throw new Exception('红包总金额必须≥' . $validAmount . '元');
}
// B. Allocate red packets
$this->apportion();
return ['items' => $this->items];
}
/**
* Allocate red packets
*/
protected function apportion() {
$num = $this->num;
$amount = $this->amount;
while ($num >= 1) {
if ($num == 1) {
$coupon_amount = $this->decimal_number($amount);
} else {
$avg_amount = $this->decimal_number($amount / $num);
$coupon_amount = $this->calcCouponAmount($avg_amount, $amount, $num);
}
$this->items[] = $coupon_amount;
$amount -= $coupon_amount;
$num--;
}
shuffle($this->items);
}
/**
* Calculate the amount for a single packet
* @param float $avg_amount Average amount for remaining packets
* @param float $amount Remaining total amount
* @param int $num Remaining packet count
* @return float
*/
protected function calcCouponAmount($avg_amount, $amount, $num) {
if ($avg_amount <= $this->coupon_min) {
return $this->coupon_min;
}
$coupon_amount = $this->decimal_number($avg_amount * (1 + $this->apportionRandRatio()));
if ($coupon_amount < $this->coupon_min || $coupon_amount > $this->calcCouponAmountMax($amount, $num)) {
return $this->calcCouponAmount($avg_amount, $amount, $num);
}
return $coupon_amount;
}
/**
* Maximum possible amount for a packet
* @param float $amount
* @param int $num
*/
protected function calcCouponAmountMax($amount, $num) {
return $this->coupon_min + $amount - $num * $this->coupon_min;
}
/**
* Random fluctuation ratio (60% chance for ±70%, otherwise ±30%)
*/
protected function apportionRandRatio() {
if (rand(1, 100) <= 60) {
return rand(-70, 70) / 100;
}
return rand(-30, 30) / 100;
}
/**
* Format a number to two decimal places
* @param float $amount
* @return float
*/
protected function decimal_number($amount) {
return sprintf('%01.2f', round($amount, 2));
}
}
?>An example usage creates a Coupon instance with a total of 200 CNY, 10 packets, and a minimum of 10 CNY per packet, calls handle() , and prints the resulting array of allocated amounts.
<?php
$coupon = new Coupon(200, 10, 10);
$res = $coupon->handle();
print_r($res);
?>The printed result shows an array of ten amounts that sum to 200 CNY, each respecting the minimum value constraint, demonstrating that the algorithm works as intended.
Laravel Tech Community
Specializing in Laravel development, we continuously publish fresh content and grow alongside the elegant, stable Laravel framework.
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.