微信的抢红包算法PHP版

397 阅读1分钟

在抢红包这个场景中,大都使用的是修正后的随机算法,从统计学意义上来讲要达到先抢后抢得到的红包是差不多的。

微信红包算法的核心是:每次随机的基础是剩余金额除以剩余个数得到的平均值的两倍。

class Test
{
    public $remainSize = 20;
    public $remainMoney = '10.00';
    public $min = '0.01';
    public $scale = 2;

    public function getRandomMoney(): string
    {
        if ($this->remainSize <= 0 || bccomp($this->remainMoney, "0") < 1) {
            return "0.00";
        }
        bcscale($this->scale);
        if ($this->remainSize == 1) {
            $this->remainSize = 0;
            return $this->remainMoney;
        }
        
        // 核心算法 start
        $max = bcmul(bcdiv($this->remainMoney, strval($this->remainSize)), "2");
        $money = bcmul(rand(0, 1), $max);
        // 核心算法 end
        
        if (bccomp($money, $this->min) < 1) {
            $money = $this->min;
        }

        $this->remainSize--;
        $this->remainMoney = bcsub($this->remainMoney, $money);
        return $money;
    }
}

测试代码:

function run()
{
    $o = new Test();
    for ($i = 0; $i < 30; $i++) {
        echo $o->getRandomMoney(), PHP_EOL;
    }
}

运行两次后得到的结果为:

在这里插入图片描述
在这里插入图片描述
可见,其波动性还是挺大的,并且也没有证据表示先抢还是后抢会占优势。

分别运行 2000 次和 3000 次看看统计规律

function run2()
{
    $sum = [];
    bcscale(2);
    for ($k = 0; $k < 2000; $k++) {
        $o = new Test();
        for ($i = 0; $i < 30; $i++) {
            $t = $o->getRandomMoney();
            if (isset($sum[$i])) {
                $sum[$i] = bcadd($sum[$i], $t);
            } else {
                $sum[$i] = $t;
            }
        }
    }

    foreach ($sum as $v) {
        echo $v, PHP_EOL;
    }
}

2000 次
在这里插入图片描述
3000次
在这里插入图片描述
可见次数越多越趋近于平均值 1000 元。而这个值正好是 2000 * 10 / 20

如果将 2 改成 1.5 ,将会是前低后高的趋势
在这里插入图片描述
如果将 2 改成 3 ,将会是前高后低的趋势

在这里插入图片描述