zoukankan      html  css  js  c++  java
  • php 随机红包算法

    <?php
    
    /**
     * 红包分配算法
     *
     *      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
    {
        /**
         * 红包金额
         * @var float
         */
        protected $amount;
    
        /**
         * 红包个数
         * @var int
         */
        protected $num;
    
        /**
         * 领取的红包最小金额
         * @var float
         */
        protected $coupon_min;
    
        /**
         * 红包分配结果
         * @var array
         */
        protected $items = [];
    
        /**
         * 初始化
         * @param float $amount     红包金额(单位:元)最多保留2位小数
         * @param int   $num        红包个数
         * @param float $coupon_min 每个至少领取的红包金额
         */
        public function __construct($amount, $num = 1, $coupon_min = 0.01)
        {
            $this->amount = $amount;
            $this->num = $num;
            $this->coupon_min = $coupon_min;
        }
    
        /**
         * 处理返回
         * @return array
         */
        public function handle()
        {
            // A. 验证
            if ($this->amount < $validAmount = $this->coupon_min * $this->num) {
                throw new Exception('红包总金额必须≥'.$validAmount.'元');
            }
    
            // B. 分配红包
            $this->apportion();
    
            return [
                'items' => $this->items,
            ];
        }
    
        /**
         * 分配红包
         */
        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->decimal_number(
                        $this->calcCouponAmount($avg_amount, $amount, $num)
                    );
                }
                $this->items[] = $coupon_amount; // 追加分配
    
                $amount -= $coupon_amount;
                --$num;
            }
    
            shuffle($this->items);  //随机打乱
        }
    
        /**
         * 计算分配的红包金额
         *
         * @param float $avg_amount 每次计算的平均金额
         * @param float $amount     剩余可领取金额
         * @param int   $num        剩余可领取的红包个数
         * @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;
        }
    
        /**
         * 计算分配的红包金额-可领取的最大金额
         * @param float $amount
         * @param int   $num
         */
        protected function calcCouponAmountMax($amount, $num)
        {
            return $this->coupon_min + $amount - $num * $this->coupon_min;
        }
    
        /**
         * 红包金额浮动比例
         */
        protected function apportionRandRatio()
        {
            // 60%机率获取剩余平均值的大幅度红包(可能正数、可能负数)
            if (rand(1, 100) <= 60) {
                return rand(-70, 70) / 100; // 上下幅度70%
            }
    
            return rand(-30, 30) / 100; // 其他情况,上下浮动30%;
        }
    
        /**
         * 格式化金额,保留2位
         * @param float $amount
         * @return float
         */
        protected function decimal_number($amount)
        {
            return sprintf('%01.2f', round($amount, 2));
        }
    
    
    }
    
    
    
    // 例子
    $price_total = 0;
    $total = $surplus_total = 30;
    $max_num = $surplus_num = 8;
    echo "总共{$total}元,随机生成{$max_num}个红包".'</br>';
    
    for($i=0;$i<$max_num;$i++){
        $coupon = new Coupon($surplus_total, $surplus_num, 0.01);
        $res = $coupon->handle();
        $money = $res['items'][0];
        $price_total += $money;
        $surplus_total = bcsub($surplus_total, $money, 2);  //解决高精度问题
        echo '第'.($i+1).'个随机红包:'.$money.'还剩下'.($surplus_total).'元'.'<br>';
        $surplus_num--;
    }

    示例演示:

  • 相关阅读:
    敏捷开发之Scrum扫盲篇
    select选项框特效
    jquery 实现 点击按钮后倒计时效果,多用于实现发送手机验证码
    如何使用CSS实现小三角形效果
    轻松学习Ionic (四) 修改应用图标及添加启动画面(更新官方命令行工具自动生成)
    轻松学习Ionic (三) 安装sass并在webstorm中为scss添加watcher
    轻松学习Ionic (二) 为Android项目集成Crosswalk(更新官方命令行工具)
    轻松学习Ionic (一) 搭建开发环境,并创建工程
    cmake时选择的VS生成器
    mysql更改密码
  • 原文地址:https://www.cnblogs.com/-mrl/p/10782776.html
Copyright © 2011-2022 走看看