<?php /** * Red envelope allocation algorithm * * example * $coupon = new Coupon(200, 5); * $res = $coupon->handle(); * print_r($res); * * @author Flc <2018-04-06 20:09:53> * @see http://flc.ren | http://flc.io | https://github.com/flc1125 */ class Coupon { /** * The amount of red envelopes * Float * @var */ protected $amount; /** * The number of red envelopes * * @was not */ protected $num; /** * Receive a minimum amount of red envelopes * Float * @var */ protected $coupon_min; /** * Red envelope allocation results * * @Var array */ protected $items = []; /** * Initialization * * @Param float $ amount envelopes Amount (Unit: RMB) retain up to two decimal places * $ @Param int num the number of red envelopes * @Param float $ coupon_min each receive at least the amount of red envelopes */ public function __construct($amount, $num = 1, $coupon_min = 0.01) { $this->amount = $amount; $this->num = $num; $this->coupon_min = $coupon_min; } /** * Processing returns * * @return array */ public function handle() { // A. Verify IF ( $ the this -> AMOUNT < $ validAmount = $ the this -> coupon_min * $ the this -> NUM) { the throw new new Exception ( 'red total amount must be ≥'. $ ValidAmount 'dollars' ); } // B. allocated envelopes $ the this -> apportion (); return [ 'items' => $this->items, ]; } /** * Red envelope distribution */ protected function apportion() { NUM $ = $ the this -> NUM; // remaining number assigned red $ AMOUNT = $ the this -> AMOUNT; // red can receive the remaining amount of the while ( $ NUM > =. 1 ) { // remaining a time, direct access to the remaining red IF ( $ NUM ==. 1 ) { $ of COUPON_AMOUNT = $ the this -> decimal_number ( $ AMOUNT ); } The else { $ avg_amount = $ the this -> decimal_number ( $ AMOUNT / $ NUM ); // the average amount of remaining red $coupon_amount = $this->decimal_number( $this->calcCouponAmount($avg_amount, $amount, $num) ); } the this $ -> items [] = $ of COUPON_AMOUNT ; // additional distribution $amount -= $coupon_amount; --$num; } shuffle ( $ the this -> items); // random disrupted } /** * Calculated assigned amount of red envelopes * * @Param float $ avg_amount average amount of each calculation * @Param float $ amount remaining can receive money * @Param int num $ number can receive the remaining red * * @return float */ protected function calcCouponAmount($avg_amount, $amount, $num) { // If the average amount is less than equal to the minimum amount, the minimum amount returned directly IF ( $ avg_amount <= $ the this -> coupon_min) { return $ the this -> coupon_min; } // sliding scale $ of COUPON_AMOUNT = $ the this -> decimal_number ( $ avg_amount * (. 1 + $ the this -> apportionRandRatio ())); // If the amount is below the minimum or maximum amount that can receive over, then re-acquired IF ( $ of COUPON_AMOUNT < $ the this -> coupon_min || $ of COUPON_AMOUNT > $ the this -> calcCouponAmountMax ( $ AMOUNT , $ NUM ) ) { return $this->calcCouponAmount($avg_amount, $amount, $num); } return $coupon_amount; } /** * Red envelope calculation amount allocated - can receive the maximum amount * * @param float $amount * @Param int $ whether */ protected function calcCouponAmountMax($amount, $num) { return $this->coupon_min + $amount - $num * $this->coupon_min; } /** * Amount proportion of floating red envelopes * / Protected function apportionRandRatio () { // 60% chance of obtaining a substantial average remaining red (may be positive, negative possible) IF ( RAND (. 1, 100) <= 60 ) { return RAND (-70, 70) / 100; // vertical width 70 % } return RAND (-30, 30) / 100; // other cases, plus or minus 30%; } /** * Formatting amount reserved 2 * * @param float $amount * * @return float */ protected function decimal_number($amount) { return sprintf('%01.2f', round($amount, 2)); } } // 例子 $coupon = new Coupon(200, 5, 30); $res = $coupon->handle(); print_r($res);