郎头疼 發表於 2019-8-28 11:41:00

PHP 实现微信红包拆分算法

<div class="cnblogs_Highlighter">
<pre class="brush:php;gutter:true;">&lt;?php
/**
* 红包分配算法
*
* example
*      $coupon = new Coupon(200, 5);
*      $res = $coupon-&gt;handle();
*      print_r($res);
*
* @author Flc &lt;2018-04-06 20:09:53&gt;
* @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-&gt;amount = $amount;
      $this-&gt;num = $num;
      $this-&gt;coupon_min = $coupon_min;
    }

    /**
   * 处理返回
   *
   * @return array
   */
    public function handle()
    {
      // A. 验证
      if ($this-&gt;amount &lt; $validAmount = $this-&gt;coupon_min * $this-&gt;num) {
            throw new Exception('红包总金额必须≥'.$validAmount.'元');
      }

      // B. 分配红包
      $this-&gt;apportion();

      return [
            'items' =&gt; $this-&gt;items,
      ];
    }

    /**
   * 分配红包
   */
    protected function apportion()
    {
      $num = $this-&gt;num;// 剩余可分配的红包个数
      $amount = $this-&gt;amount;//剩余可领取的红包金额

      while ($num &gt;= 1) {
            // 剩余一个的时候,直接取剩余红包
            if ($num == 1) {
                $coupon_amount = $this-&gt;decimal_number($amount);
            } else {
                $avg_amount = $this-&gt;decimal_number($amount / $num);// 剩余的红包的平均金额

                $coupon_amount = $this-&gt;decimal_number(
                  $this-&gt;calcCouponAmount($avg_amount, $amount, $num)
                );
            }

            $this-&gt;items[] = $coupon_amount; // 追加分配

            $amount -= $coupon_amount;
            --$num;
      }

      shuffle($this-&gt;items);//随机打乱
    }

    /**
   * 计算分配的红包金额
   *
   * @param float $avg_amount 每次计算的平均金额
   * @param float $amount   剩余可领取金额
   * @param int   $num      剩余可领取的红包个数
   *
   * @return float
   */
    protected function calcCouponAmount($avg_amount, $amount, $num)
    {
      // 如果平均金额小于等于最低金额,则直接返回最低金额
      if ($avg_amount &lt;= $this-&gt;coupon_min) {
            return $this-&gt;coupon_min;
      }

      // 浮动计算
      $coupon_amount = $this-&gt;decimal_number($avg_amount * (1 + $this-&gt;apportionRandRatio()));

      // 如果低于最低金额或超过可领取的最大金额,则重新获取
      if ($coupon_amount &lt; $this-&gt;coupon_min
            || $coupon_amount &gt; $this-&gt;calcCouponAmountMax($amount, $num)
      ) {
            return $this-&gt;calcCouponAmount($avg_amount, $amount, $num);
      }

      return $coupon_amount;
    }

    /**
   * 计算分配的红包金额-可领取的最大金额
   *
   * @param float $amount
   * @param int   $num
   */
    protected function calcCouponAmountMax($amount, $num)
    {
      return $this-&gt;coupon_min + $amount - $num * $this-&gt;coupon_min;
    }

    /**
   * 红包金额浮动比例
   */
    protected function apportionRandRatio()
    {
      // 60%机率获取剩余平均值的大幅度红包(可能正数、可能负数)
      if (rand(1, 100) &lt;= 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));
    }
}

// 例子
$coupon = new Coupon(200, 5, 30);
$res = $coupon-&gt;handle();
print_r($res);
</pre>
</div>
<p>  </p><br><br>
来源:https://www.cnblogs.com/lhm166/p/11423122.html
頁: [1]
查看完整版本: PHP 实现微信红包拆分算法