红包算法之如何助你抢到大红包 | 青训营笔记

1,474 阅读4分钟

红包算法


这是我参与「第四届青训营 」笔记创作活动的的第5天。

前几天,在跟着月影学JavaScript课程中,讲了四个小算法,已经发 (shui) 了前面三个,有兴趣的可以去看一下Leftpad快速幂与位运算 | 青训营笔记 - 掘金 (juejin.cn)洗牌算法 | 青训营笔记 - 掘金 (juejin.cn)

而今天这个算法,了解了可以帮助你更大概率抢到大红包!

红包是随机的吗?

或许你可能会说,当然,这完全靠运气的东西啊,100块有人能抢60,有人只能精准1块钱。

IMG_20220729_004329.jpg

不知道你有没有这种感觉:

  • 好像后抢的金额更大?

  • 好像抢的金额跟人数有关?

n 个人抢 n + 1 分钱

这里我们假设有 n 个人抢红包,红包金额为 n + 1 分钱。

毫无疑问的是,肯定有 n - 1 个人抢的是 1 分钱,而有一个抢的是 2 分钱。

按理说那个 2 分钱应该是所有人获得的概率是一样的。

但是,现实情况是肯定是最后一个人抢到 2 分钱!!

这里斥 ‘巨资’ 做的实验:

  • 2 个人抢 3 分
IMG_20220729_005538.jpg IMG_20220729_005619.jpg
  • 3个人抢 4 分
IMG_20220729_005643.jpg IMG_20220729_005711.jpg IMG_20220729_005740.jpg

可以看抢光耗时,这几个都是不一样,暂时就做了这几个实验,我每次都是最后抢的,每次都是 2 分钱(别问,问就是回血),按照随机的说法,我要是想每次都抢到 2 分,概率为:1/2 * 1/2 * 1/3 * 1/3 * 1/3 = 1/108,这么低的概率,那我们是不是可以说 n 个人抢 n + 1分钱,肯定是最后一个人抢 2 分呢。

  • 如果各位有兴趣的话,可以试试更多人,是不是也符合这样的定律。

那么是不是对于金额大的,一定最后的人抢的就一定多呢。这也不一定,到这里就得看微信红包实现算法了。

微信红包实现

二倍均值法

在网上查阅了很多,大部分都是说的二倍均值法实现的微信红包。

  • 所谓二倍均值法,就是说保证每个人随机到的数为 1分 ~ 两倍平均值-1分,平均值为 当前剩余钱 / 当前剩余未抢的人数

核心代码如下:

    for(int i = 0; i < num - 1; i ++){
        int ave = money / (num - i);
        int temp = 1 + rand() % (2 * ave - 1);
        cout << temp << endl;
        money -= temp; 
    }
    cout << money;      // 最后一个人将剩下的都拿走
    

这种算法,也刚好满足我们上面的 n 个人抢 n + 1 分钱 的事实。

那么这样一个算法,是否有什么能帮助我们更大概率抢到更大的红包呢。答案是有的。

如何抢到大红包

从上面我们得知,假如100元的红包,5个人抢,第一个人只可能抢到0.01 ~ 39.99,第二个人 0.01 ~ 49.99 ,第三个人 0.01 ~ 66.65 ,第四个人 0.01 ~ 99.95,第五个人 0.01 ~ 99.96,可以看到,只有在后面的,才有可能抢到超级大红包。

这里我们模拟一下,设置四个数组,分别是每个人运气王次数,平均金额数和最大最小金额数,我们来看一下模拟10000次结果如何:

  • 3个人抢15
2.jpg
  • 5个人抢20
1.jpg
  • 10个人抢50

3.jpg

  • 20个人抢100
4.jpg

从上面,我们基本可以得出以下几点:

  • 如果是希望抢到超级大红包,最后两个概率更大

  • 人数比较少的时候,先抢更容易是运气王

  • 人数比较多的时候,最好开始就抢或者最后抢,中间运气王的概率是最低的,最后几个抢运气王概率是最大的

  • 不管怎么样抢,均值都差不多,也可以说是比较 ‘公平’

  • 不要因为等后面几个而红包被抢光...

证明过程

如下是这种规则 先抢后抢均值相等,而后抢的方差更大 的证明:

QQ图片20220729112954.jpg

图来自:微信红包先抢和后抢差距居然这么大!

也可以看看他发的视频:我给自己发了2亿个红包,才发现先抢和后抢差距这么大!_哔哩哔哩_bilibili

附代码:

#include <iostream>
#include <algorithm>
#include <ctime>
#include <cstring>

using namespace std;

const int N = 25;		// 最大人数 
double money;
int num, cent, loop = 10000;
int nums[N], maxM[N], minM[N], sum[N];			// 运气王次数, 最大红包金额,最小红包金额,金额总和 

void fightLuck(){
	// 保证每个人随机到的数为 1分 ~ 两倍平均值-1分
	int n = num, m = cent, maxNum = 0, index; 
	for(int i = 0; i < n - 1; i ++){
		int ave = m / (n - i), temp = 1 + rand() % (2 * ave - 1);
		m -= temp, sum[i] += temp;
		maxM[i] = max(maxM[i], temp), minM[i] = min(minM[i], temp);
		if(temp > maxNum)	maxNum = temp, index = i;
	}
	sum[n - 1] += m, maxM[n - 1] = max(maxM[n - 1], m), minM[n - 1] = min(minM[n - 1], m); 
	if(m > maxNum)	index = n - 1;
	nums[index] ++;			//运气王 
}

void print(){
	cout << endl << " 第  个人 : ";			 
	for(int i = 0; i < num; i ++)	printf(" %d \t", i + 1);
	cout << endl << "运气王次数: ";
	for(int i = 0; i < num; i ++) 	printf("%d\t", nums[i]);
	cout << endl << "平均金额数: "; 
	for(int i = 0; i < num; i ++) 	printf("%.2lf\t", (double)sum[i] / 1000000);
	cout << endl << "最大金额数: "; 
	for(int i = 0; i < num; i ++) 	printf("%.2lf\t", maxM[i] / 100.0);
	cout << endl << "最小金额数: "; 
	for(int i = 0; i < num; i ++) 	printf("%.2lf\t", minM[i] / 100.0); 
}

int main(){
	memset(minM, 0x3f, sizeof minM); 
	srand((unsigned)time(NULL)); 
	cout << "请输入金额和总人数:" << endl;
	cin >> money >> num;
	cent = money * 100;		// 转成 分 钱 
	if(cent < num)	cout << "金额不足" << endl;
	else{
		while(loop --)	fightLuck();
		print();
	}	
	 
	return 0;
}


最后

可能现在微信红包具体实现算法与上有所出入,但是就我目前抢的来看得话好像大差不差,如果对你 (抢红包) 有所帮助的话,点个赞再走吧哈哈哈。

如有不当之处,欢迎指出~