微信紅包的分配算法,在知乎上已經有人討論過了,詳見《微信紅包的隨機算法是怎樣實現的?》。基本的原則是:紅包分配的錢數知足截尾正態隨機數分佈。程序員
大體爲在截尾正態分佈中取隨機數,並用其求和數除以總價值,得到修正因子,再用修正因子乘上全部的隨機數,獲得紅包價值。算法
這種分佈意味着:低於平均值的紅包多,可是離平均值不遠;高於平均值的紅包少,可是遠大於平均值的紅包偏多。c#
關於正態分佈,可參考百科:安全
正態分佈(Normal distribution)又名高斯分佈(Gaussian distribution),是一個在數學、物理及工程等領域都很是重要的機率分佈,在統計學的許多方面有着重大的影響力。若隨機變量X服從一個數學指望爲μ、方差爲σ^2的高斯分佈,記爲N(μ,σ^2)。其機率密度函數爲正態分佈的指望值μ決定了其位置,其標準差σ決定了分佈的幅度。因其曲線呈鐘形,所以人們又常常稱之爲鐘形曲線。咱們一般所說的標準正態分佈是μ = 0,σ = 1的正態分佈。微信
例如100元總額,分紅20份的隨機紅包,能夠這樣設置:指望值μ=100/20=5,標準差σ=100,這樣紅包總體分佈大概會是這樣(負數能夠取絕對值):dom
紅包分佈示意圖生成器是參考《c# 隨機生成1000個知足正態分佈的點》寫的,核心代碼以下:函數
/// <summary> /// 返回正態分佈的值 /// </summary> /// <param name="u1">第一個均勻分佈值</param> /// <param name="u2">第二個均勻分佈值</param> /// <param name="e">正態指望</param> /// <param name="d">正態方差</param> /// <returns>分佈值或者null</returns> private static double? GetZTFB(double u1, double u2, double e, double d) { double? result = null; try { result = e + Math.Sqrt(d) * Math.Sqrt((-2) * (Math.Log(u1) / Math.Log(Math.E))) * Math.Sin(2 * Math.PI * u2); } catch (Exception ex) { result = null; } return result; }
讓咱們加大金額10000和份數2000:能夠看到紅包分佈老是在必定區間以內,符合紅包分配的大部分特色,你們拿到的錢都會差很少,但顯然這樣不是最佳手氣拿到的金額,也不夠刺激。this
實際上基於正態分佈的原理,真正的紅包要這樣處理,參考《別人在搶紅包,程序員在研究紅包算法》:url
this.Cursor = Cursors.WaitCursor; this.chart1.Series[0].Points.Clear(); this.listBox1.Items.Clear(); double t_max = 0,t_min = 0; Random rand = new Random(); double total = (double)this.num_Total.Value; ;//紅包總額 int num = (int)this.num_Num.Value; ;// 紅包份數 double min = 0.01;//每一個人最少能收到0.01元 double safe_total, money; for (int i = 1; i < num; i++) { safe_total = (total - (num - i) * min) / (num - i);//隨機安全上限 double ztfb_u = total / num;//正態分佈指望 double ztfb_fc = ztfb_u;//正態分佈方差 money = rand.Next((int)(min * 100), (int)(safe_total * 100)) / 100.0d; this.chart1.Series[0].Points.Add(money); this.listBox1.Items.Add(i + ":" + money.ToString("0.00")); if (money > t_max) t_max = money; if (i == 1) t_min = t_max; if (money < t_min) t_min = money; total = total - money; } this.label3.Text = string.Format("Max:{0} Min:{1}", t_max.ToString("0.00"), t_min.ToString("0.00")); this.Cursor = Cursors.Default;
一樣是10000元,分2000份,大部分人都在20元如下,少數在20~80元,幸運者會超過100元,看起來這樣的紅包分配比較合理了。spa