业务流程
以微信红包为例,分为如下流程:
- 发红包(包红包);
- 抢红包;
- 拆红包(开红包)。
发红包
设置金额和份数,完成支付,红包详情入库,为红包生成一个唯一ID。
红包拆分
拆分时机
红包金额拆分分为实时拆分和提前拆分,实时拆分对系统性能和拆分算法要求较高,在拆分时既要保证线程安全,又要保证并发效率,开发难度较大。而且拆分过程中要一直保证后续待拆分红包的金额不能为0,不容易做到拆分的红包金额服从正态分布规律。
大型红包系统一般选择提前拆分,从而降低系统复杂度。
拆分算法
随机数
我们假设有一个100元的红包。第一个人可以在0.01元到100元之间,随机地分配到一定金额。如果我们把第一个人抽到的所有可能的金额都计算在内,并取个平均值,那么他平均能获得50元。这50元在数学上还有个形象的名字,叫作数学期望。既然是“期望”,就总会有落空的时候,也不排除会有意外的惊喜。因此,第一个人抽到的金额可能不足50元,也可能大于50元。
第二个人就只能在0.01到剩下的金额之间随机了,显然,第二个人平均能获得的金额比第一个人小,而且越到后面的人,这个平均金额越小。
也就是说,越在前面抢的人越有优势,因此,这个算法非常不公平。
二倍均值法
二倍均值法拆分的金额比较平均,属于“雨露均沾”类型。
假设红包总金额是a,红包个数为p,每个红包的最低金额是0.01元。
那么每次抢到的红包金额的范围在 [0.01, (a / p) * 2] 之间。
算法代码如下:
public class DoubleAVG {
/**
* 拆分金额
* @param amount 总金额,单位:元
* @param part 份数
* @return
*/
public static float[] compute(int amount, int part) {
// 红包最低金额0.01元,乘以100以后就可以全部用整型计算,不用担心精度丢失的问题
int amt = amount