目录
前言
相信大家应该都参与过微信的拼手气红包吧,那运气王是先抢的概率大还是后抢的概率大呢?我们一起来看一下拼手气红包的生成方式——随机分配红包。
介绍
生成拼手气红包的逻辑就是将已知的总金额和总数量进行随机分配值的过程,那这个过程中需要注意下面这三个规则:
随机分配红包金额时一般需要遵循3个规则:
(1)所有人抢到金额之和等于红包总金额,不能超过,也不能少于;
(2)抢到的红包金额至少是一分钱;
(3)要保证抢到红包的人获取到的红包金额是随机的。
代码实现
编写一个静态方法接收两个参数:BigDecimal totalAmount 总金额 int numPackets 红包数量
/**
* @param totalAmount 总金额
* @param numPackets 红包数量
* @return
*/
public static List<BigDecimal> reduceRedPackets(BigDecimal totalAmount, int numPackets) {
// 声明用来存放生成红包的集合
List<BigDecimal> packets = new ArrayList<>();
// 计算每个红包金额
for (int i = 0; i < numPackets - 1; i++) {
//当前金额平均值(当前金额平均值 = 总金额 / 剩余红包数 eg: 100/10=10)结果保留两位小数 BigDecimal.ROUND_DOWN:两位小数之后的直接舍去
BigDecimal maxAmount = totalAmount.divide(BigDecimal.valueOf(numPackets - i), 2, BigDecimal.ROUND_DOWN);
//随机金额的浮动范围,也是控制生成的随机红包相差大小的阈值
// eg:生成一个在 0.1——2.0之间的数,随机数范围越大 则产生的随机红包相差金额越大,都等于1则是平均分配,
// 生成的随机数若都是小于1的那最后一个红包就最大,若是生成的随机数都大于1 则最后一个红包就最小
double min = 0.1;
double max = 2.0;
double result = min + (max - min) * Math.random();
//生成随机金额 (随机金额 = 0.01 + (当前金额平均值 - 0.01) * 随机数)
BigDecimal amount = new BigDecimal(0.01).add(maxAmount.subtract(new BigDecimal(0.01))).multiply(BigDecimal.valueOf(result));
//生成的随机金额保留两位小数
amount = amount.setScale(2, BigDecimal.ROUND_DOWN);
//将随机数添加进红包集合
packets.add(amount);
//总金额减去随机数
totalAmount = totalAmount.subtract(amount);
}
// 遍历numPackets -1次之后 剩余最后一个红包 那最后一个红包金额就是 总金额 - 前面的红包金额之和 保留两位小数
packets.add(totalAmount.setScale(2, BigDecimal.ROUND_DOWN));
// 打乱红包顺序 规避分配随机数 阈值带来的影响
Collections.shuffle(packets);
return packets;
}
注:其中随机金额的浮动范围,也是控制生成的随机红包相差大小的阈值
随机数范围越大 则产生的随机红包相差金额越大,都等于1则是平均分配
生成的随机数若都是小于1的那最后一个红包就最大
若是生成的随机数都大于1 则最后一个红包就最小
所以大家可以根据应用情况来控制随机数的范围从而控制生成红包的偏差大小。
使用 BigDecimal 和setScale(2, BigDecimal.ROUND_DOWN) 保留两位小数方式规避金额错误和总金额超限的情况。
使用main方法运行查看分配结果:
public static void main(String[] args) {
double totalAmount = 100; // 总金额
int numPackets = 10; // 红包数量
List<BigDecimal> redPackets = reduceRedPackets(BigDecimal.valueOf(totalAmount), numPackets);
// 输出每个红包金额
System.out.println("红包金额分配如下:");
BigDecimal summoney = new BigDecimal(0);
for (int i = 0; i < redPackets.size(); i++) {
summoney = summoney.add(redPackets.get(i));
System.out.printf("红包 %d: %.2f 元%n", i + 1, redPackets.get(i));
}
System.out.println("红包总额:" + summoney);
}
红包金额分配如下:
红包 1: 12.29 元
红包 2: 2.20 元
红包 3: 16.68 元
红包 4: 9.59 元
红包 5: 10.46 元
红包 6: 7.07 元
红包 7: 13.10 元
红包 8: 8.18 元
红包 9: 15.99 元
红包 10: 4.44 元
红包总额:100.00
因为顺序都是被打乱的所以先领和后领并不会影响你成为运气王的概率。
附:去做了是勇气,坚持了才是胜利。