逢年过节抢红包乐此不疲的你,作为IT人的你是否有想过微信红包到底是怎么回事,怎么生成的,有什么规则,怎么抢手气最佳的概率更高?
刚开始我对这个问题也比较困惑,看似一个简单的随机算法,参考了国内几篇文章但都不太满意,后来还是得益于stackoverflow上一位大神的启发[https://stackoverflow.com/questions/2640053/getting-n-random-numbers-that-the-sum-is-m]。
先说下思路,有了思路,就只是如何把丑的、复杂的、性能较低的代码写的更加强大了。
以微信红包作为参考,通常红包要满足的规则有:
最小金额为0.01
最大金额为200
红包金额可动态输入
红包个数可动态输入
生成的所有红包金额总和为输入红包金额
生成的每个红包金额是随机的
了解了红包规则之后说一下我这个生成随机红包算法的基本思路和要考虑的问题点:
首先对输入的红包总金额和红包个数进行校验
特殊情况无需随机,直接返回
生成随机数组,用于计算红包金额占比
遍历随机数组,计算其各个随机数对应的红包金额
预先判断金额是否太大或太小,并作特殊处理
校验生成的红包是否满足条件
最后,需要将生成的红包打乱
另外,考虑到基本数据类型比保证数据类型占用内存更少,其计算更快,所以能用基本数据类型的地方不用包装类型,但是涉及到除法的计算还是BigDecimal更好一些。
备注:下面的源码对应的java版本是JAVA8及以上。
下面是该算法对应的github源码URL:
https://github.com/stathry/jdkdeep/blob/master/src/org/stathry/jdkdeep/concurrent/luckymoney/RandomGiftUtils.java
测试用例URL:
https://github.com/stathry/jdkdeep/blob/master/src/org/stathry/jdkdeep/concurrent/luckymoney/RandomGiftTest.java
该算法的主要代码贴一下(如果真的感兴趣,还是建议去github看比较直观):
/**
* 生成随机红包(最高200元,最低1分)
* @param money 红包总金额(单位:分)
* @param num 红包份数
* @return
*/
public static List randomGifts(int money, int num) {
checkRange(money, num);
List gifts;
if (num == 1) {
return Arrays.asList(Integer.valueOf(money));
}
// 刚好最大或刚好最小,则直接均分
if((gifts = quickFill(money, num)) != null) {
return gifts;
}
gifts = new ArrayList(num);
BigDecimal total = BigDecimal.valueOf(money);
// 当前红包金额总和
int sum = 0;
// 当前红包允许最大金额
int curMax = MAX;
// 当前红包允许最小金额
int curMin = MIN;
// 当前红包金额
int c;
// 剩余红包个数
int rn;
// 各个红包权重
int[] an = new int[num];
// 权重份数
BigDecimal share = BigDecimal.valueOf(randomInitArrayAndSum(an, num));
// 当前红包金额占比
BigDecimal rate;
for (int i = 0, size = num; i
// 最后一个红包金额为总金额-已分配
if (i == size - 1) {
c = money - sum;
} else {
rn = num - i - 1;
rate = BigDecimal.valueOf(an[i]).divide(share, SCALE, MODE);
// 按各自权重计算红包金额
c = total.multiply(rate).intValue();
// 预先判断金额是否太大或太小
curMax = money - sum - rn * MIN;
curMax = curMax > MAX ? MAX : curMax;
c = c > curMax ? curMax : c;
curMin = money - sum - rn * MAX;
curMin = curMin
c = c
}
sum += c;
Assert.isTrue(c >= MIN && c
gifts.add(c);
}
// 打乱红包顺序
Collections.shuffle(gifts);
// System.out.println(gifts);
return gifts;
}
好了,如果觉得有收获记得关注哦。
领取专属 10元无门槛券
私享最新 技术干货