根据很多需求的使用场景,如发红包、砍价类需求,这两个功能都有一个同样的特点,如下:
红包
1.Total amount
2.Number of red envelopes
3.Min. Number of red envelopes
Bargaining
1. Total bargaining amount
2. How many people are needed to complete the bargaining (the number of people depends on the demand)
- Fixed Number of Bargaining Numbers
- Random Bargaining Number of Numbers
- Specified Random Bargaining Number of People
Point 2: The three rules all need to determine a number of people according to the rules
3. Minimum bargaining amount
Development ideas
Verification parameters
The minimum amount is not allowed to be less than 0
Total The amount is not allowed to be greater than the quantity multiplied by the minimum amount
Distribution amount
Get the average amount (total amount/remaining quantity)
Distribution amount Average amountless than or equal to
Directly allocate the minimum amount when the maximum amount is reached
Get the amountAmplitude ratio
The minimum value is not allowed to be less than-1
The maximum value is not allowed to be greater than1
Obtained Distribution amount amplitude calculation (average * (1amplitude ratio
))
Distribution amount judgmentDistribution amount
is less thanminimum amount
ordistribution amount
is greater thanThe maximum amount that can be claimed
((minimum amount remaining total amount) - (remaining quantity × minimum amount)) will redistribute the amount
If the last one is left, all the remaining amounts will be distributed
Development code
amount = $amount; $this->num = $num; $this->minAmount = $minAmount; } /** * 处理返回 * @return array * @throws Exception */ public function handle() { # 验证 if ($this->amount < $validAmount = $this->minAmount * $this->num) { throw new Exception('红包总金额必须≥'.$validAmount.'元'); } # 分配红包 $this->allot(); return $this->amountArr; } /** * 分配红包 */ protected function allot() { # 剩余可分配的红包个数 $num = $this->num; # 剩余可领取的红包金额 $amount = $this->amount; while ($num >= 1) { if ($num == 1) { # 剩余一个的时候,直接取剩余红包 $coupon_amount = $this->formattingAmount($amount); } else { # 平均金额 $avgAmount = $this->formattingAmount($amount / $num); # 分配金额 $countAllotAmount = $this->countAllotAmount($avgAmount, $amount, $num); # 剩余的红包的平均金额 $coupon_amount = $this->formattingAmount($countAllotAmount); } # 追加分配金额 $this->amountArr[] = $coupon_amount; # 计算剩余金额 $amount -= $coupon_amount; $num--; } # 随机打乱 // shuffle($this->amountArr); } /** * 计算分配的红包金额 * @param float $avgAmount 每次计算的平均金额 * @param float $amount 剩余可领取金额 * @param int $num 剩余可领取的红包个数 * @return float */ protected function countAllotAmount($avgAmount, $amount, $num) { # 如果平均金额小于等于最低金额,则直接返回最低金额 if ($avgAmount <= $this->minAmount) { return $this->minAmount; } # 浮动比率 $floatingRate = $this->floatingRate(); # 分配金额 $allotAmount = $avgAmount * (1 + $floatingRate); # 浮动计算 $coupon_amount = $this->formattingAmount($allotAmount); # 如果低于最低金额或超过可领取的最大金额,则重新获取 if ($coupon_amount < $this->minAmount || $coupon_amount > $this->canReceiveMaxAmount($amount, $num)) { return $this->countAllotAmount($avgAmount, $amount, $num); } return $coupon_amount; } /** * 计算分配的红包金额-可领取的最大金额 * @param $amount * @param $num * @return float|int */ protected function canReceiveMaxAmount($amount, $num) { return $this->minAmount + $amount - $num * $this->minAmount; } /** * 红包金额浮动比例 * @return float|int */ protected function floatingRate() { # 60%机率获取剩余平均值的大幅度红包(可能正数、可能负数) if (rand(1, 100) <= 60) { # 上下幅度70% return rand(-70, 70) / 100; } # 其他情况,上下浮动30%; return rand(-30, 30) / 100; } /** * 格式化金额,保留2位 * @param $amount * @return string */ protected function formattingAmount($amount) { return sprintf('%01.2f', round($amount, 2)); } }
- Total amount
$amount = 1;
- Allocation quantity
$num = 10;
- Minimum amount
$minAmount = 0.01; $red = new sandRed($amount, $num, $minAmount); $res = $red->handle(); print_r($res);
- Output result [0.10,0.04,0.08,0.04,0.16,0.14,0.11,0.13,0.11,0.09]
echo array_sum($res);
- Output result 1