微信是我们日常生活中必不可少的聊天工具,里面的红包功能我们也经常使用,其中,拼手气红包有时候可以抢到很大,有时候就只有0.01,那这个功能应该是如何实现的呢?
红包的分配需要遵循几个规则:
- 所有人抢到的红包金额之和要等于总金额,不能多也不能少
- 最低红包金额是一分钱
- 保证所有人抢到的红包金额随机
一. 随机算法
先将总金额减去每个红包的最低金额一分钱,在0~剩余金额中取随机数,加上一分钱就是第一个红包,此时
剩余金额 = 总金额 - 红包数量 * 一分钱 - 第一个红包金额
第二个红包的分配方式类似,用代码来实现,如下:
public static List<BigDecimal> distributeRedPacket(double amount, int redPacketNums){
//将元转为分
int amountFee = (int) (amount * 100);
//设置红包最低金额
int minSum = 1 * redPacketNums;
Random random = new Random();
amountFee -= minSum;
List<BigDecimal> redPacketList = new ArrayList<>();
//先分配前9个红包
for (int i = 0; i < redPacketNums - 1 ; i++) {
int fee = random.nextInt(amountFee);
BigDecimal decimal = new BigDecimal(fee + 1).divide(new BigDecimal("100.0"),2, RoundingMode.FLOOR);
redPacketList.add(decimal);
amountFee -= fee;
}
//最后一个红包
redPacketList.add(new BigDecimal(amountFee + 1).divide(new BigDecimal("100.0"),2, RoundingMode.FLOOR));
return redPacketList;
}
调用main方法执行该过程:
public static void main(String[] args) {
List<BigDecimal> redPacketList = distributeRedPacket(10,10);
BigDecimal amount = new BigDecimal(0);
for (int i = 0; i < redPacketList.size(); i++) {
System.out.println("第"+(i+1)+"个红包金额为:"+ redPacketList.get(i));
amount = amount.add(redPacketList.get(i));
}
System.out.println("红包金额总额为:"+amount.doubleValue());
}
输出结果:
第1个红包金额为:2.07
第2个红包金额为:3.38
第3个红包金额为:4.41
第4个红包金额为:0.07
第5个红包金额为:0.01
第6个红包金额为:0.01
第7个红包金额为:0.01
第8个红包金额为:0.01
第9个红包金额为:0.01
第10个红包金额为:0.02
红包金额总额为:10.0
再执行一次:
第1个红包金额为:6.51
第2个红包金额为:2.29
第3个红包金额为:1.12
第4个红包金额为:0.01
第5个红包金额为:0.01
第6个红包金额为:0.01
第7个红包金额为:0.01
第8个红包金额为:0.01
第9个红包金额为:0.01
第10个红包金额为:0.02
红包金额总额为:10.0
这种方式会导致先抢的人有大概率抢到大金额红包,后面的人很难抢到了,而且金额分配很不均匀,所以需要一个更加平均的方法。
二. 二倍均值算法
通过设置每个人红包的最小值,并使用剩余金额的两倍均值为随机区间,从而更正态的分配,该方法可以让大家获得更公平的红包金额。具体实现如下:
public static List<BigDecimal> distributeRedPacket2(double amount, int redPacketNums){
List<BigDecimal> redPacketList = new ArrayList<>();
//设置最小倍率为0.1 ,最大倍率为2
double minRate = 0.1;
double maxRate = 2.0;
BigDecimal amountBigDecimal = new BigDecimal(amount);
//先分配前9个红包
for (int i = 0; i < redPacketNums - 1 ; i++) {
//生成一个0.1~2.0的随机倍率
double randomRate = minRate + (maxRate - minRate) * Math.random();
//获取剩余金额的平均数
BigDecimal avg = amountBigDecimal.divide(new BigDecimal(redPacketNums - i),2,RoundingMode.FLOOR);
//计算红包金额:平均数 * 倍率
BigDecimal redPacketAmount = avg.multiply(new BigDecimal(randomRate)).setScale(2,RoundingMode.FLOOR);
//总额减去已分配过的
amountBigDecimal = amountBigDecimal.subtract(redPacketAmount);
redPacketList.add(redPacketAmount);
}
//分配最后一个红包
redPacketList.add(amountBigDecimal);
//打乱
Collections.shuffle(redPacketList);
return redPacketList;
}
调用main方法执行结果:
第1个红包金额为:0.50
第2个红包金额为:1.21
第3个红包金额为:0.79
第4个红包金额为:0.68
第5个红包金额为:1.22
第6个红包金额为:0.32
第7个红包金额为:0.26
第8个红包金额为:2.37
第9个红包金额为:1.40
第10个红包金额为:1.25
红包金额总额为:10.0
再试一次:
第1个红包金额为:0.16
第2个红包金额为:2.10
第3个红包金额为:2.21
第4个红包金额为:0.87
第5个红包金额为:0.60
第6个红包金额为:0.46
第7个红包金额为:0.52
第8个红包金额为:1.26
第9个红包金额为:1.47
第10个红包金额为:0.35
红包金额总额为:10.0
可以从结果看出,这种分配方式较第一种更为平均,其中的minRate与MaxRate是控制红包金额的范围,两者相差越大,红包金额差距就越大,生成的随机数若都是小于1的那最后一个红包就最大,若是生成的随机数都大于1 则最后一个红包就最小,所以可以根据应用情况来控制随机数的范围从而控制生成红包的偏差大小。