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));
    }
}

$cut_price_total = 0;
$total = 30;
$cut_price_max_num = 8;     //最大砍价次数
$surplus_num = 8;
// 例子
for($i=0;$i<$cut_price_max_num;$i++){
    $coupon = new Coupon($total, $surplus_num, 0.01);
    $res = $coupon->handle();
    $cut_price_total += $res[‘items‘][0];
    $total -= $res[‘items‘][0];
    echo $res[‘items‘][0].‘<br>‘;
    $surplus_num--;
}

原文地址:https://www.cnblogs.com/-mrl/p/10782776.html

时间: 2024-09-29 19:23:56

php 随机红包算法的相关文章

随机红包算法

PHP将一个数字划分成随机值,总和与数字相同 今天有个任务,非常奇怪,需要把一个数字值分为X个数,每个数的值是随机的,而已所随机出来的数相加必须=总数. 比如有一个数是100,我想把他分成5(或者N)份,每份的数是不一样的,但是加起来要是100,而且每份不能差距太大,这个用php如何实现? 这样我们就需要下面的PHP将一个数字划分成随机值,总和与数字相同的函数了 大致思路 1)取份数的平均值 2)根据允许的各份数间最大差值,动态调整随机数范围,形成各份的数量 3)最后一份的数量由前面已经分出的决

PHP随机红包算法

2017年1月14日 14:19:14 星期六 简便起见, 用整数, 假设每个红包里边有x个糖豆 一种: 防微信, 每个红包的最大金额是: (剩余金额/剩余红包数)*2 1 function randBean($total_bean, $total_packet) 2 { 3 $cp_total_bean = $total_bean; 4 $cp_total_packet = $total_packet; 5 6 $arr = []; 7 $min = 1; 8 while($total_pac

C# 随机红包算法

1 static void Main(string[] args) 2 { 3 double totalAmount = 20; 4 int num = 10; 5 double minAmount = 0.01; 6 Random r = new Random(); 7 for (int i = 1; i < num; i++) 8 { 9 double safeAmount = (totalAmount - (num - i) * minAmount) / (num - i); 10 //d

随机红包生成算法-python实现

抢红包那么开心,那你知道红包随机算法是怎么样的吗? 我模拟写了一个定额随机红包生成算法,如下. 输入: 红包总额,total 份数,num 调控参数(调控红包最平均差,默认为2) 约束: 每份最少有1分钱,即0.01 份数需为正整数 红包总额 <= 份数×0.01 输出 随机红包序列,序列长度等于红包份数 运气王,即红包数额最大的一份 # -*- coding: cp936 -*- # 思路:先随机出来m个数,然后平均分成m个数字只和的份数,然后将钱平均分给m个人# import random

关于随机红包抽奖算法

场景: 生成10个随机红包, 奖池总金额10000, 最小500, 最大1000,奖池全部分配完. 分析: 第一想法简单, 直接生成500-1000之间的随机数,直接生成10个, 直接上代码 /** * * @param lst 生成的奖项列表 * @param minAmount 红包允许的最小金额 * @param maxAmount 红包允许的最大金额 * @param totalAmount 总奖池金额 * @param count 生成红包数量 */public void genera

红包算法

今天看到一个红包算法,就使用了拿来主义 $total=10; //总额 $num=8; // 分成8个红包,支持8人随机领取 $min=0.10; //每个人最少能收到0.10元 for ($i=1;$i<$num;$i++) { $safe_total=($total-($num-$i)*$min)/($num-$i);//随机安全上限 ,这里算的是剩余的钱除以剩余的人的平均值 $money=mt_rand($min*100,$safe_total*100)/100; $total=$tota

随机抢红包算法实现

在这里你只需要给出一个红包个数,给出一个红包总金额,通过一下算法可以分配指定个数的两位小数金额. 首先,定义一个可以获取指定取值区间的随机数数组. /// <summary> /// 生成指定大小区间长度随机数组 /// </summary> /// <param name="length">数组长度</param> /// <param name="min">最小值</param> /// &

微信红包算法

微信红包有多种玩法,其中一种就是指定金额.人数(m),拆红包的人收到的金额是随机,收到的金额保留两位小数,至少有一分,所有人的红包加起来等于指定金额. 我想到一种做法就是:将指定金额放大100倍,也就是变成单位"分",这时金额就是整数了,设为n,从1到n这个整数区间随机抽取m(是人数)个整数,这样1到n的整数区间就分成了m或m+1(这种情况,最后的两个区间合成一个区间)个区间. 比如输入金额1.00元,人数m=3,n=100 * 1.从1到100之间随机选中的三个整数为15.42.88

微信红包算法TEST

1.基本算法 设定总金额为10元,有N个人随机领取:N=1 则红包金额=X元: N=2 为保证第二个红包可以正常发出,第一个红包金额=0.01至9.99之间的某个随机数 第二个红包=10-第一个红包金额: N=3 红包1=0.01至0.98之间的某个随机数 红包2=0.01至(10-红包1-0.01)的某个随机数 红包3=10-红包1-红包2 -- 2.java代码 1 package Demo_1.Test_1; 2 3 import java.math.BigDecimal; 4 impor