紅包分配算法


微信紅包的分配算法,在知乎上已經有人討論過了,詳見《微信紅包的隨機算法是怎樣實現的?》。基本的原則是:紅包分配的錢數滿足截尾正態隨機數分布。

大致為在截尾正態分布中取隨機數,並用其求和數除以總價值,獲得修正因子,再用修正因子乘上所有的隨機數,得到紅包價值。

這種分布意味着:低於平均值的紅包多,但是離平均值不遠;高於平均值的紅包少,但是遠大於平均值的紅包偏多。

關於正態分布,可參考百科:

正態分布(Normal distribution)又名高斯分布(Gaussian distribution),是一個在數學、物理及工程等領域都非常重要的概率分布,在統計學的許多方面有着重大的影響力。若隨機變量X服從一個數學期望為μ、方差為σ^2的高斯分布,記為N(μ,σ^2)。其概率密度函數為正態分布的期望值μ決定了其位置,其標准差σ決定了分布的幅度。因其曲線呈鍾形,因此人們又經常稱之為鍾形曲線。我們通常所說的標准正態分布是μ = 0,σ = 1的正態分布。

例如100元總額,分成20份的隨機紅包,可以這樣設置:期望值μ=100/20=5,標准差σ=100,這樣紅包整體分布大概會是這樣(負數可以取絕對值):

紅包分布示意圖生成器是參考《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.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元,看起來這樣的紅包分配比較合理了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM