前一阵公司业务有一个生成红包的需求,分为固定红包和随机红包两种,固定红包没什么好说的了,随机红包要求指定最小值,和最大值,必须至少有一个最大值,可以没有最小值,但任何红包不能小于最小值。
以前从来没做过这方面,有点懵B,于是去百度了一番,结果发现能找到的红包算法都有各种各样的 bug,要么会算出负值,要么超过最大值,所以决定自己撸一套出来。
基本思路
在随机数生成方面,我借鉴了这位博主 @悲惨的大爷 的思路:
原文:比如要把 1 个红包分给 N 个人,实际上就是相当于要得到 N 个百分比数据 条件是这 N 个百分比之和 = 100/100。这 N 个百分比的平均值是 1/N。 并且这 N 个百分比数据符合一种正态分布(多数值比较靠近平均值)。
解读:比如我有 1000 块钱,发 50 个红包,就先随机出 50 个数,然后算出这 50 个数的均值 avg,用 avg/(1/N),就得到了一个基数 mixrand ,然后用随机出的那 50 个数分别去除以 mixrand ,得到每个数相对基数的百分比 randVal ,然后用 randVal 乘以 1000 块钱,就可以得到每个红包的具体金额了。
还是不太清楚咋回事?没关系,我们一起撸代码!
算法实现
Talk is cheap, show me your code!
核心生成算法
|
细节考虑
下边这段代码用来控制具体的业务逻辑,按照具体的需求,留出固定的最大值、最小值红包的金额等;在代码中调用生成红包的方法时 splitReward($total, $num,$max - 0.01, $min),我传入的最大值减了 0.01,这样就保证了里面生成的红包最大值绝对不会超过我们设置的最大值。
|
实例测试
基础代码
先设置好各种初始值。
|
性能测试
因为 memory_limit 的限制,所以只测了 5 次的均值,结果都在 1.6s 左右。
for($i=0; $i<5; $i++) { |
运行结果:
数据检查
1) 数值是否有误
检测有没有负值,有没有最大值,最大值有多少个,有没有小于最小值的值。
$reward_arr = $create_reward->random_red($total, $num, $max, $min); |
运行结果:
2) 正态分布情况
注意,出图的时候,红包的数量不要给的太大,不然页面渲染不出来,会崩 。
$reward_arr = $create_reward->random_red($total, $num, $max, $min); |
运行结果:
PS:有朋友问我生成的数据有没有通过数学方法来验证其是否符合标准正态分布,因为我的数学不好,这个还真没算过,只是看着觉得像,就当他是了。既然遇到了这个问题,就一定要解决嘛,所以我就用 php 内置函数算了一下,算出来的结果在数据量小的时候还是比较接近正态分布的,但是数据量大起来的时候就不能看了,我整不太明白这个,大家感兴趣的可以找一下原因哟。 php 的四个函数:stats_standard_deviation(标准差),stats_variance(方差), stats_kurtosis((峰度),stats_skew(偏度)。使用上面的函数需要安装 stats 扩展。
总结
到这里,红包就算是写完啦,不知道能不能涨 50 块工资,但应该能解决燃眉之急了。
哦对,还落下了这个源码,打包下载 。