揭秘:微信是如何发红包的?纯小白揭秘,小白可阅。

510 阅读5分钟

大佬可直接翻到最后面

前言

不知道从何时起,开始流行用微信在家族群,工作群发红包这个活动了。每逢过节或者家有喜事,就必定少不了发红包和抢红包这个环节。前两天我还在一个外卖群里面抢了一个红包,不过就只抢到了0.01!气死我了!

是我的运气太差了吗?还是红包他就设定了我就是0.01?这让我不禁想深入探究一下,到底是如何实现随机金额发红包的!

如何实现红包金额随机

红包金额随机其实很简单,我们只要利用一个随机数就可以了。但是光设置个随机数可不行哦,不然的话,说不好我们发个红包还要多余的从我们钱包里面扣钱。我们在群里发红包的时候会提示我们红包个数和红包金额,这其实就是一个关键的参数!

我们该如何利用随机数以及这两个重要的参数设计这个发红包算法呢? 我先后想到了三种方法来设计这个

  • 我们都知道,我们可以利用一个Math.random()这个来生成一个0-1之间的随机数。按照我们之前做单个随机数的想法就是,只用将这个随机数乘以一个红包的总金额,这样就生成了一个红包总金额之内的随机数了。但是这样很明显是不行的, 因为这样很容易是的收到的红包金额大于发出去的红包总金额,那剩下少的钱谁来补呢?难道让微信自掏腰包给补上?这肯定不现实啊。

  • 第二种想法就是利用平均每个红包的金额来设计随机数,这样就不会有像上一个的想法的的问题了,微信再也不用自掏腰包了。但是随机而来的又是另外一个问题,这时候钱不会多了,但是钱少了。这种想法即使每次生成平均金额的随机数,也就刚刚好,而实际上并不会每次都生成一个平均的金额,所以到最后红包里面的钱又会剩余一些。显然这种想法还是不够实际的

  • 第三种想法就是只设置比红包个数少一个的随机数,最后一个红包金额就用红包的总金额减去前面几个随机数。这样就好啦,既解决了红包金额少又解决了红包金额多的问题。

既然解决了,我们就动手来验证一下。

const arr = [];
let total = 10;
let num = 10;
let count = total;
for(let i =0; i < num -1; i++) {
    let amount = parseFloat(Math.random() *  (total/num)).toFixed(2)
    arr.push(amount)
    count=count - amount
}
arr.push(count.toFixed(2))
console.log(arr);

具体的结果就是这样(第二张图总金额设置为500,个数为20)

image.pngimage.png

这样我们会发现,最后一个数很大,特别大。这就有问题了。通过分析代码得知,前面几个数会生成一些比较小的随机数,而通过最后一个数相减,只能说是硬性的符合了收发金额相等的这个要求。

大佬优化

为此,通过向大佬请教,知道了可以动态的获得随机数,就是通过引入restAmountrestNum变量,实时跟踪剩余需要分配的金额和人数。这意味着每一次循环时,生成随机数的范围是基于当前剩余的总金额和人数。这样或许就能比前面的更好一些。我们可以实验一下:

 const arr = [];
    // 余额
    let restAmount = total;
    // 未领取人数
    let restNum = num;
    // num-1  
    for(let i =0; i < num -1; i++) {
        let amount = parseFloat(Math.random() *  (restAmount/restNum)).toFixed(2)
        restAmount -= amount;
        restNum--;
        arr.push(amount)
    }
    // 最后一人的
    arr.push(restAmount.toFixed(2));

结果是:

image.png

emmmm,这结果貌似和上面的相差不大啊?仔细查看一下代码逻辑发现还是一样的问题,前面生成的随机数都是很小的一部分,我们要使值变得更均匀的话可以扩大前面几个的值,在后面乘以一个2试试

image.png

这时候得到的结果就均匀许多了。其实这就是我们常说的二倍均值法。

到这里其实这个公平又公正的红包随机分配就已经完成了。但是这只是发红包的其中一部分,我们这里只涉及到简单的红包分配,还有其他的红包发放,红包领取这些过程,这需要我们和我们的伙伴们一起完成,那么上面的这段代码肯定就增加了团队合作的难度。所以我们可以修改一下我们的代码,让以后我们的伙伴拿到就知道是什么意思,就可以直接用,而不需要做过多的修改。

下面是最终版本:

/**
 * @func 红包算法
 * @param {Number} total 总金额
 * @param {Number} num 人数
 */

function hongbao(total,num){
    // 发红包的那一刻就已经决定了
    // 宕机
    const arr = [];
    // 余额
    let restAmount = total;
    // 未领取人数
    let restNum = num;
    // num-1  
    for(let i =0; i < num -1; i++) {
        let amount = parseFloat(Math.random() *  (restAmount/restNum)).toFixed(2)
        restAmount -= amount;
        restNum--;
        arr.push(amount)
    }
    // 最后一人的
    arr.push(restAmount.toFixed(2));
    return arr;
    
}

小结

写到这里,我想说的是,很多事情其实做出来很简单,但是要做到完美就很难了。我在写这篇文章的时候是以我现在的水平写的,慢慢的得出最好的结果。在学习到这里的时候,大佬们直接就给出最后一步,我这种菜鸟属实是很难一下就领悟到这个层次,所以我就在这文章里一步一步的来。生活中很多事情其实也是这样,只有一步一步的慢慢试错我们才能最后少犯错误,达到大佬那种出手即最佳的水平。加油啦,每天都要进步一点哟。