微信红包.....真的是抢的越晚越好吗
- 1. 首先先给出一个有意思的小测试….
- 2. 微信红包的发放算法
- 3. 最佳手气的金额对比
- 4. 不同位置抢红包的均值
- 5.是20元红包15个人抢1000次
- 代码
大家都喜欢抢红包这是必然的…虽然日常都是0.01….
在写这篇没有什么大意义的文章之前,先给出我代码跑了7000W次的结果…
腾讯作为一个有脑子的公司,那么多大佬在团队里,算法肯定要比我个人写的更加完整和完善…所以首先可以保证…抢到红包的均值是肯定一样的,因为期望值一定是 <总钱数>/<总人数>。
那么存在的区别仅仅是根据个人的风险偏好的结构(risk preference structure)不同,风险回避(risk-averse)的抽取者应该尽量先抽,而风险偏爱(risk-seeking)的抽取者应该尽量后抽。
即喜欢刺激的人可以留到快抢完的时候抢,喜欢稳定的自然是第一时间抢,因为测试证明,在7000W次模拟中,越后抢的人有更大的几率抽到最大的红包,但同样也有更大的几率抽到均值以下的红包,即所谓的搏一搏单车变摩托…
说了这么多,请注意一个问题,这一切的前提都是…..
你能抢的到红包,否则一切都是废话
1. 首先先给出一个有意思的小测试….
如果你发出n个红包总金额是(n+1)分,那么0.02这个手气王必然在最后两人之中,而有99.99%以上的概率是最后一个人…
2. 微信红包的发放算法
-
第一个问题必然是红包的发放方式,虽然没有意义,但是可以先小讨论一下...其实没啥好说的,要么就是每次有人抢红包的时候在剩余钱数中随机出金额,要么就是在发出红包之前就已经计算完每个人应该获得的钱,然后按顺序pop罢了,那我个人当然认为是第二种..因为我认为实时计算这种方式非常蠢...
-
第二个问题就是红包发放算法了,首先可以总结出几点:
-
1)所有人都能分到红包,也就是不会出现红包数值为0的情况。
-
2)所有人的红包数值加起来等于支付的金额。
-
3)红包波动范围比较大,约5%~8%的红包数值在平均值的两倍以上,同时数额0.01出现的概率比较高。
-
4)红包的数值是随机的,并且数值的分布近似于正态分布。
-
所以很多人会想到正态分布,比如分50元钱给25个人,那么我只要均匀随机出24个在0-50之间的数,这24个数可以将50元分成25分,然后按顺序给出去即可,那么这种发放方式的期望值虽然也是M/N,但是其分布是指数分布,也就是多会造成大多数人拿到的钱非常少,而极少数人会获得很多钱的情况,即方差非常大,这肯定不是大家想看到的或者是微信想看到的。
-
因为每个红包的最小值为0.01,所以在初始的时候为每个红包预留0.01元,那么剩余金额总数为M-0.01*k。
-
那么易得 < ai = rand(T - 0.01 * k - a0 - ... - ai-1) >
-
红包数额的分布并不完全符合正态分布,因为每个红包的数额都有上限和下限,所以准确地说应该是截尾正态分布,在这里红包金额范围为[0, Mi]。
-
剩下要做的就是确定sigma的数值,sigma的值会直接影响红包数额的分布曲线。根据正态分布的三个sigma定理, 生成的随机数值有95.449974%几率落在(mu-2*sigma,mu+2*sigma)内
-
为了使得mu-2*sigma = 0,sigma = mu/2。对于生成的随机数落在[0, Mi]以外区间的情况,采用截断处理,统一返回0或者Mi。也就是说,最后生成的随机数值分别有大约6%的几率为0或者大于2*(Mi/(n-i)),加上保留的0.01
-
所以代码在底部
3. 最佳手气的金额对比
经过测试可以发现,最佳手气金额在人数增加的时候,趋于稳定,并维持在平均值的两倍左右
4. 不同位置抢红包的均值
以下是20元红包15个人抢1000次的人均价值,可以发现,不管是第几个抢到的红包,抢到手的均值是不会变的,都是趋向于<总钱数>/<总人数>
5.是20元红包15个人抢1000次
以下是每个位置的人抢1000次20元红包的金额分布手动增加了快进功能….
在最后一张十五个人的拟合曲线中,我们可以看到越往后的位置获得的金额曲线更加像指数分布,也就是越靠近最基本的正态分布的方式,即最大金额越大,但是曲线更平坦,也就是开头说到的,越往后抢红包有几率获得更大的红包,但也会有更大的几率获得比一般人低的红包,也就是收获和风险并存?
然后就是我们的开头的结论了,若是你想要搏一搏…自然是留到后面抢比较好,若是你想求稳,那么自然是早点抢到手的好….
【代码】
#include <bits/stdc++.h>
using namespace std;
#define TWO_PI 6.2831853071795864769252866
double generateGaussianNoise(const double mu, const double sigma)
{
using namespace std;
static bool haveSpare = false;
static double rand1, rand2;
if(haveSpare)
{
haveSpare = false;
return (sigma * sqrt(rand1) * sin(rand2)) + mu;
}
haveSpare = true;
rand1 = rand() / ((double) RAND_MAX);
if(rand1 < 1e-100) rand1 = 1e-100;
rand1 = -2 * log(rand1);
rand2 = (rand() / ((double) RAND_MAX)) * TWO_PI;
return (sigma * sqrt(rand1) * cos(rand2)) + mu;
}
vector<double> generateMoneyVector(const double mon, const int pics)
{
vector<double> valueVec;
double moneyLeft = mon - pics * 0.01;
double mu, sigma;
double noiseValue;
for(int i = 0; i < pics - 1; i++)
{
mu = moneyLeft / (pics - i);
sigma = mu / 2;
noiseValue = generateGaussianNoise(mu, sigma);
if(noiseValue < 0) noiseValue = 0;
if(noiseValue > moneyLeft) noiseValue = moneyLeft;
noiseValue += 0.005;
noiseValue = ((int)(noiseValue * 100))/100.0;
valueVec.push_back(noiseValue + 0.01);
moneyLeft -= noiseValue;
}
valueVec.push_back(moneyLeft + 0.01);
return valueVec;
}
vector<double> f[10000];
int main()
{
freopen("1.txt","w",stdout);
double mon;
int pics;
for (int i=0;i<1000;i++)
{
mon=20;
pics=15;
vector<double> a=generateMoneyVector(mon,pics);
for (int i=0;i<a.size();i++) f[i].push_back(a[i]);
}
for (int j=0;j<15 ;j++)
{
double sum=0;
sort(f[j].begin(),f[j].end());
for (int i=0;i<f[j].size();i++)
{
sum+=f[j][i];
printf("%.2f\t",f[j][i]);
}
sum/=1000;
printf("%lf\t",sum);
puts("");
}
return 0;
}
标签:红包,晚越,double,0.01,mu,.....,微信,noiseValue,sigma From: https://blog.51cto.com/u_15782961/5984325