今年和去年一样,央视的春晚再次被广大网友吐槽, 即便全民呼吁“六小龄童上春晚”,齐天大圣仍然没有走上央视舞台。知名时评人石述思转了这样一条微博,今年的春晚导演为了大家抢红包也是拼了,连一分钟好看的节目都没安排。目前全国人民都听牌了,单吊敬业福!
稍微吐槽一下还是回到今天的主题,今天讲一个ruby实战教程——微信红包金额分配算:知乎上的一篇文章讨论的比较深入,个人觉得下面的这个分析比较靠谱。
红包领了不少,据观察红包主要有以下几个限制条件:
1.所有人都能分到红包,也就是不会出现红包数值为0的情况。
2.所有人的红包数值加起来等于支付的金额。
3.红包波动范围比较大,约5%~8%的红包数值在平均值的两倍以上,同时数额0.01出现的概率比较高。
4.红包的数值是随机的,并且数值的分布近似于正态分布。
基本思路:
1.因为要保证每个人都至少抢到一分钱,那么首先给每个人分配1分钱
2.剩下的钱再随机分给每个人,Ruby的随机函数是平均分布的,因此如果需要使红包金额近似正态分布,需要对平均分布进行Box–Muller转换操作
3.由于精度问题,最后一个红包需要用总的红包金额-已分配的金额
4. 得到服从正态分布的随机数的基本思想是先得到服从均匀分布的随机数,再将服从均匀分布的随机数转变为服从正态分布
Box-Muller
Box-Muller是产生随机数的一种方法。Box-Muller算法隐含的原理非常深奥,但结果却是相当简单。
如果在(值域内有两个一致的随机数字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+(Zsd)
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]