微信红包.....真的是抢的越晚越好吗

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jnxxhzz/article/details/81700318

微信红包.....真的是抢的越晚越好吗


大家都喜欢抢红包这是必然的…虽然日常都是0.01….
在写这篇没有什么大意义的文章之前,先给出我代码跑了7000W次的结果…

腾讯作为一个有脑子的公司,那么多大佬在团队里,算法肯定要比我个人写的更加完整和完善…所以首先可以保证…抢到红包的均值是肯定一样的,因为期望值一定是 <总钱数>/<总人数>
那么存在的区别仅仅是根据个人的风险偏好的结构(risk preference structure)不同,风险回避(risk-averse)的抽取者应该尽量先抽,而风险偏爱(risk-seeking)的抽取者应该尽量后抽。

即喜欢刺激的人可以留到快抢完的时候抢,喜欢稳定的自然是第一时间抢,因为测试证明,在7000W次模拟中,越后抢的人有更大的几率抽到最大的红包,但同样也有更大的几率抽到均值以下的红包,即所谓的搏一搏单车变摩托

说了这么多,请注意一个问题,这一切的前提都是…..

你能抢的到红包,否则一切都是废话


1. 首先先给出一个有意思的小测试….

如果你发出n个红包总金额是(n+1)分,那么0.02这个手气王必然在最后两人之中,而有99.99%以上的概率是最后一个人…

2. 微信红包的发放算法

  1. 第一个问题必然是红包的发放方式,虽然没有意义,但是可以先小讨论一下...其实没啥好说的,要么就是每次有人抢红包的时候在剩余钱数中随机出金额,要么就是在发出红包之前就已经计算完每个人应该获得的钱,然后按顺序pop罢了,那我个人当然认为是第二种..因为我认为实时计算这种方式非常蠢...
  2. 第二个问题就是红包发放算法了,首先可以总结出几点:
  3. 1)所有人都能分到红包,也就是不会出现红包数值为0的情况。
  4. 2)所有人的红包数值加起来等于支付的金额。
  5. 3)红包波动范围比较大,约5%~8%的红包数值在平均值的两倍以上,同时数额0.01出现的概率比较高。
  6. 4)红包的数值是随机的,并且数值的分布近似于正态分布。
  7. 所以很多人会想到正态分布,比如分50元钱给25个人,那么我只要均匀随机出24个在0-50之间的数,这24个数可以将50元分成25分,然后按顺序给出去即可,那么这种发放方式的期望值虽然也是M/N,但是其分布是指数分布,也就是多会造成大多数人拿到的钱非常少,而极少数人会获得很多钱的情况,即方差非常大,这肯定不是大家想看到的或者是微信想看到的。
  8. 因为每个红包的最小值为0.01,所以在初始的时候为每个红包预留0.01元,那么剩余金额总数为M-0.01*k。
  9. 那么易得 < ai = rand(T - 0.01 * k - a0 - ... - ai-1) >
  10. 红包数额的分布并不完全符合正态分布,因为每个红包的数额都有上限和下限,所以准确地说应该是截尾正态分布,在这里红包金额范围为[0, Mi]。
  11. 剩下要做的就是确定sigma的数值,sigma的值会直接影响红包数额的分布曲线。根据正态分布的三个sigma定理, 生成的随机数值有95.449974%几率落在(mu-2*sigma,mu+2*sigma)内
  12. 为了使得mu-2*sigma = 0,sigma = mu/2。对于生成的随机数落在[0, Mi]以外区间的情况,采用截断处理,统一返回0或者Mi。也就是说,最后生成的随机数值分别有大约6%的几率为0或者大于2*(Mi/(n-i)),加上保留的0.01
  13. 所以代码在底部

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

猜你喜欢

转载自blog.csdn.net/jnxxhzz/article/details/81700318
今日推荐