Ruby Ruby - 微信红包分配算法

michael_roshen · 2016年02月16日 · 最后由 qwlong 回复于 2016年02月16日 · 5008 次阅读

今年和去年一样,央视的春晚再次被广大网友吐槽,即便全民呼吁“六小龄童上春晚”,齐天大圣仍然没有走上央视舞台。知名时评人石述思转了这样一条微博,今年的春晚导演为了大家抢红包也是拼了,连一分钟好看的节目都没安排。目前全国人民都听牌了,单吊敬业福!

稍微吐槽一下还是回到今天的主题,微信红包金额分配算:知乎上的一篇文章讨论的比较深入,个人觉得下面的这个分析比较靠谱。

红包领了不少,据观察红包主要有以下几个限制条件: 1.所有人都能分到红包,也就是不会出现红包数值为 0 的情况。 2.所有人的红包数值加起来等于支付的金额。 3.红包波动范围比较大,约 5%~8% 的红包数值在平均值的两倍以上,同时数额 0.01 出现的概率比较高。 4.红包的数值是随机的,并且数值的分布近似于正态分布。

基本思路: 1.因为要保证每个人都至少抢到一分钱,那么首先给每个人分配 1 分钱 2.剩下的钱再随机分给每个人,Ruby 的随机函数是平均分布的,因此如果需要使红包金额近似正态分布,需要对平均分布进行 Box–Muller 转换操作 3.由于精度问题,最后一个红包需要用总的红包金额-已分配的金额

  1. 得到服从正态分布的随机数的基本思想是先得到服从均匀分布的随机数,再将服从均匀分布的随机数转变为服从正态分布

Box-Muller Box-Muller 是产生随机数的一种方法。Box-Muller 算法隐含的原理非常深奥,但结果却是相当简单。 如果在(0,1] 值域内有两个一致的随机数字 U1 和 U2, 可以使用以下两个等式中的任一个算出一个正态分布的随机数字 Z: Z=R*cos(θ) 或 Z=R*sin(θ) 其中,R=sqrt(-2*ln(U2));θ=2*π*U1 正态值 Z 有一个等于 0 的平均值和一个等于 1 的标准偏差,可使用以下等式将 Z 映射到一个平均值为 m、标准偏差为 sd 的统计量 X: X=m+(Z*sd)

mu 和 sigma 值的确定: mu 的值: 为当前红包的均值,令分配第 i 个红包时所剩总金额为 Ti,所以:Ti = T - 0.01 * k - a0 - … - ai-1 得到:mu = Ti / (k - i)

sigma 的值: sigma 的值会直接影响红包数额的分布曲线,根据正态分布的三个 sigma 定理,生成的随机数值有 95.449974% 几率落在 (mu-2*sigma,mu+2*sigma) 内,为了使得 mu-2*sigma = 0,sigma = mu/2。对于生成的随机数落在[0, Ti] 以外区间的情况,采用截断处理,统一返回 0 或者 Ti。也就是说,最后生成的随机数值分别有大约 6% 的几率为 0 或者大于 2*mu,加上保留的 0.01 rg = RandomGaussian.new(mu,sigma) noiseValue = rg.rand.round(2)

测试: generateMoneyVector(1, 3) [0.42, 0.38, 0.2] [0.49, 0.06, 0.45] [0.18, 0.54, 0.28] generateMoneyVector(1, 10) [0.13, 0.11, 0.08, 0.13, 0.01, 0.11, 0.04, 0.15, 0.16, 0.08] [0.07, 0.12, 0.11, 0.15, 0.08, 0.05, 0.14, 0.18, 0.03, 0.07] [0.12, 0.08, 0.15, 0.10, 0.11, 0.04, 0.04, 0.10, 0.05, 0.21]

github: https://github.com/MichaelRoshen/RedPaket

分配红包到微信那个规模更重要的是分配算法的效率吧~ 公平公正是次要的

#1 楼 @jasl 这是根据微信红包的实际情况猜测的,主要分享的是利用高斯分布来模拟微信红包金额分配。 有效率更高的代码,欢迎分享~

看着很高大上的样子

需要 登录 后方可回复, 如果你还没有账号请 注册新账号