要求:
保证每个红包最少分得0.01元保证每个红包金额概率尽量均衡所有红包累计金额等于红包总金额本文提供4中红包算法及Java代码实现demo,仅供参考。其中每种算法测试场景为:0.1元10个包,1元10个包,100元10个包,1000元10个包。
一、剩余金额随机法以10元10个红包为例,去除每个红包的最小金额后,红包剩余9.9元;

public static void main(String[] args) { //初始化测试场景 BigDecimal[][] rrr = { {new BigDecimal("0.1"), new BigDecimal("10")}, {new BigDecimal("1"), new BigDecimal("10")}, {new BigDecimal("100"), new BigDecimal("10")}, {new BigDecimal("1000"), new BigDecimal("10")} }; BigDecimal min = new BigDecimal("0.01"); //测试个场景 for (BigDecimal[] decimals : rrr) { final BigDecimal amount = decimals[0]; final BigDecimal num = decimals[1]; System.out.println(amount + "元" + num + "个人抢======================================================="); test1(amount, min, num); }}private static void test1(BigDecimal amount, BigDecimal min, BigDecimal num) { BigDecimal remain = amount.subtract(min.multiply(num)); final Random random = new Random(); final BigDecimal hundred = new BigDecimal("100"); BigDecimal sum = BigDecimal.ZERO; BigDecimal redpeck; for (int i = 0; i < num.intValue(); i++) { final int nextInt = random.nextInt(100); if (i == num.intValue() - 1) { redpeck = remain; } else { redpeck = new BigDecimal(nextInt).multiply(remain).divide(hundred, 2, RoundingMode.FLOOR); } if (remain.compareTo(redpeck) > 0) { remain = remain.subtract(redpeck); } else { remain = BigDecimal.ZERO; } sum = sum.add(min.add(redpeck)); System.out.println("第" + (i + 1) + "个人抢到红包金额为:" + min.add(redpeck)); } System.out.println("校验每个红包累计额度是否等于红包总额结果:" + (amount.compareTo(sum) == 0));}
测试结果如下:可以看出此算法有明显缺陷,即:先领取的红包金额较大,后领取的红包金额较小,这就使得抢红包便的不公平。
0.1元10个人抢=======================================================第1个人抢到红包金额为:0.01第2个人抢到红包金额为:0.01第3个人抢到红包金额为:0.01第4个人抢到红包金额为:0.01第5个人抢到红包金额为:0.01第6个人抢到红包金额为:0.01第7个人抢到红包金额为:0.01第8个人抢到红包金额为:0.01第9个人抢到红包金额为:0.01第10个人抢到红包金额为:0.01校验每个红包累计额度是否等于红包总额结果:true1元10个人抢=======================================================第1个人抢到红包金额为:0.09第2个人抢到红包金额为:0.28第3个人抢到红包金额为:0.19第4个人抢到红包金额为:0.20第5个人抢到红包金额为:0.15第6个人抢到红包金额为:0.02第7个人抢到红包金额为:0.03第8个人抢到红包金额为:0.01第9个人抢到红包金额为:0.01第10个人抢到红包金额为:0.02校验每个红包累计额度是否等于红包总额结果:true100元10个人抢=======================================================第1个人抢到红包金额为:19.99第2个人抢到红包金额为:29.58第3个人抢到红包金额为:38.27第4个人抢到红包金额为:11.85第5个人抢到红包金额为:0.11第6个人抢到红包金额为:0.13第7个人抢到红包金额为:0.01第8个人抢到红包金额为:0.01第9个人抢到红包金额为:0.03第10个人抢到红包金额为:0.02校验每个红包累计额度是否等于红包总额结果:true1000元10个人抢=======================================================第1个人抢到红包金额为:60.00第2个人抢到红包金额为:695.54第3个人抢到红包金额为:229.72第4个人抢到红包金额为:8.95第5个人抢到红包金额为:0.29第6个人抢到红包金额为:4.64第7个人抢到红包金额为:0.01第8个人抢到红包金额为:0.69第9个人抢到红包金额为:0.12第10个人抢到红包金额为:0.04校验每个红包累计额度是否等于红包总额结果:true
二、二倍均值法(微信红包采用此法)
还是以10元10个红包为例,去除每个红包的最小金额后,红包剩余9.9元,二倍均值计算公式:2 剩余金额/剩余红包数
第一个红包在[0,1.98]范围随机,假设随机得1.9,则第一个红包金额为2.0,红包剩余8元。第二个红包在[0,2]范围随机,假设随机的1元,则第二个红包金额为1.1元,红包剩余7元。第三个红包在[0,2]范围随机,假设随机的0.5元,则第三个红包金额为0.6元,红包剩余5.5元。以此类推。public static void main(String[] args) { //初始化测试场景 BigDecimal[][] rrr = { {new BigDecimal("0.1"), new BigDecimal("10")}, {new BigDecimal("1"), new BigDecimal("10")}, {new BigDecimal("100"), new BigDecimal("10")}, {new BigDecimal("1000"), new BigDecimal("10")} }; BigDecimal min = new BigDecimal("0.01"); //测试个场景 for (BigDecimal[] decimals : rrr) { final BigDecimal amount = decimals[0]; final BigDecimal num = decimals[1]; System.out.println(amount + "元" + num + "个人抢======================================================="); test2(amount, min, num); }}private static void test2(BigDecimal amount,BigDecimal min ,BigDecimal num){ BigDecimal remain = amount.subtract(min.multiply(num)); final Random random = new Random(); final BigDecimal hundred = new BigDecimal("100"); final BigDecimal two = new BigDecimal("2"); BigDecimal sum = BigDecimal.ZERO; BigDecimal redpeck; for (int i = 0; i < num.intValue(); i++) { final int nextInt = random.nextInt(100); if(i == num.intValue() -1){ redpeck = remain; }else{ redpeck = new BigDecimal(nextInt).multiply(remain.multiply(two).divide(num.subtract(new BigDecimal(i)),2,RoundingMode.CEILING)).divide(hundred,2, RoundingMode.FLOOR); } if(remain.compareTo(redpeck) > 0){ remain = remain.subtract(redpeck); }else{ remain = BigDecimal.ZERO; } sum = sum.add(min.add(redpeck)); System.out.println("第"+(i+1)+"个人抢到红包金额为:"+min.add(redpeck)); } System.out.println("校验每个红包累计额度是否等于红包总额结果:"+amount.compareTo(sum));}
测试结果如下:此算法很好的保证了抢红包几率大致均等。
0.1元10个人抢=======================================================第1个人抢到红包金额为:0.01第2个人抢到红包金额为:0.01第3个人抢到红包金额为:0.01第4个人抢到红包金额为:0.01第5个人抢到红包金额为:0.01第6个人抢到红包金额为:0.01第7个人抢到红包金额为:0.01第8个人抢到红包金额为:0.01第9个人抢到红包金额为:0.01第10个人抢到红包金额为:0.01校验每个红包累计额度是否等于红包总额结果:true100元10个人抢=======================================================第1个人抢到红包金额为:6.20第2个人抢到红包金额为:7.09第3个人抢到红包金额为:10.62第4个人抢到红包金额为:18.68第5个人抢到红包金额为:18.74第6个人抢到红包金额为:2.32第7个人抢到红包金额为:15.44第8个人抢到红包金额为:5.43第9个人抢到红包金额为:15.16第10个人抢到红包金额为:0.32校验每个红包累计额度是否等于红包总额结果:true1元10个人抢=======================================================第1个人抢到红包金额为:0.08第2个人抢到红包金额为:0.05第3个人抢到红包金额为:0.17第4个人抢到红包金额为:0.17第5个人抢到红包金额为:0.08第6个人抢到红包金额为:0.06第7个人抢到红包金额为:0.18第8个人抢到红包金额为:0.10第9个人抢到红包金额为:0.02第10个人抢到红包金额为:0.09校验每个红包累计额度是否等于红包总额结果:true1000元10个人抢=======================================================第1个人抢到红包金额为:125.99第2个人抢到红包金额为:165.08第3个人抢到红包金额为:31.90第4个人抢到红包金额为:94.78第5个人抢到红包金额为:137.79第6个人抢到红包金额为:88.89第7个人抢到红包金额为:156.44第8个人抢到红包金额为:7.97第9个人抢到红包金额为:151.01第10个人抢到红包金额为:40.15校验每个红包累计额度是否等于红包总额结果:true
三、整体随机法
还是以10元10个红包为例,随机10个数,红包金额公式为:红包总额 随机数/随机数总和,假设10个随机数为[5,9,8,7,6,5,4,3,2,1],10个随机数总和为50,
第一个红包105/50,得1元。第二个红包109/50,得1.8元。第三个红包108/50,得1.6元。以此类推。public static void main(String[] args) { //初始化测试场景 BigDecimal[][] rrr = { {new BigDecimal("0.1"), new BigDecimal("10")}, {new BigDecimal("1"), new BigDecimal("10")}, {new BigDecimal("100"), new BigDecimal("10")}, {new BigDecimal("1000"), new BigDecimal("10")} }; BigDecimal min = new BigDecimal("0.01"); //测试个场景 for (BigDecimal[] decimals : rrr) { final BigDecimal amount = decimals[0]; final BigDecimal num = decimals[1]; System.out.println(amount + "元" + num + "个人抢======================================================="); test3(amount, min, num); }}private static void test3(BigDecimal amount,BigDecimal min ,BigDecimal num){ final Random random = new Random(); final int[] rand = new int[num.intValue()]; BigDecimal sum1 = BigDecimal.ZERO; BigDecimal redpeck ; int sum = 0; for (int i = 0; i < num.intValue(); i++) { rand[i] = random.nextInt(100); sum += rand[i]; } final BigDecimal bigDecimal = new BigDecimal(sum); BigDecimal remain = amount.subtract(min.multiply(num)); for (int i = 0; i < rand.length; i++) { if(i == num.intValue() -1){ redpeck = remain; }else{ redpeck = remain.multiply(new BigDecimal(rand[i])).divide(bigDecimal,2,RoundingMode.FLOOR); } if(remain.compareTo(redpeck) > 0){ remain = remain.subtract(redpeck); }else{ remain = BigDecimal.ZERO; } sum1= sum1.add(min.add(redpeck)); System.out.println("第"+(i+1)+"个人抢到红包金额为:"+min.add(redpeck)); } System.out.println("校验每个红包累计额度是否等于红包总额结果:"+(amount.compareTo(sum1)==0));}
测试结果如下:此算法随机性较大。
0.1元10个人抢=======================================================第1个人抢到红包金额为:0.01第2个人抢到红包金额为:0.01第3个人抢到红包金额为:0.01第4个人抢到红包金额为:0.01第5个人抢到红包金额为:0.01第6个人抢到红包金额为:0.01第7个人抢到红包金额为:0.01第8个人抢到红包金额为:0.01第9个人抢到红包金额为:0.01第10个人抢到红包金额为:0.01校验每个红包累计额度是否等于红包总额结果:true100元10个人抢=======================================================第1个人抢到红包金额为:2.35第2个人抢到红包金额为:14.12第3个人抢到红包金额为:5.74第4个人抢到红包金额为:6.61第5个人抢到红包金额为:0.65第6个人抢到红包金额为:10.97第7个人抢到红包金额为:9.15第8个人抢到红包金额为:7.93第9个人抢到红包金额为:1.31第10个人抢到红包金额为:41.17校验每个红包累计额度是否等于红包总额结果:true1元10个人抢=======================================================第1个人抢到红包金额为:0.10第2个人抢到红包金额为:0.02第3个人抢到红包金额为:0.12第4个人抢到红包金额为:0.03第5个人抢到红包金额为:0.05第6个人抢到红包金额为:0.12第7个人抢到红包金额为:0.06第8个人抢到红包金额为:0.01第9个人抢到红包金额为:0.04第10个人抢到红包金额为:0.45校验每个红包累计额度是否等于红包总额结果:true1000元10个人抢=======================================================第1个人抢到红包金额为:148.96第2个人抢到红包金额为:116.57第3个人抢到红包金额为:80.49第4个人抢到红包金额为:32.48第5个人抢到红包金额为:89.39第6个人抢到红包金额为:65.60第7个人抢到红包金额为:20.77第8个人抢到红包金额为:16.03第9个人抢到红包金额为:36.79第10个人抢到红包金额为:392.92校验每个红包累计额度是否等于红包总额结果:true
四、割线法
还是以10元10个红包为例,在(0,10)范围随机9个间隔大于等于0.01数,假设为[1,1.2,2,3,4,5,6,7,8]
第一个红包得1元第二个红包得0.2元第三个红得0.8元。以此类推。public static void main(String[] args) { //初始化测试场景 BigDecimal[][] rrr = { {new BigDecimal("0.1"), new BigDecimal("10")}, {new BigDecimal("1"), new BigDecimal("10")}, {new BigDecimal("100"), new BigDecimal("10")}, {new BigDecimal("1000"), new BigDecimal("10")} }; BigDecimal min = new BigDecimal("0.01"); //测试个场景 for (BigDecimal[] decimals : rrr) { final BigDecimal amount = decimals[0]; final BigDecimal num = decimals[1]; System.out.println(amount + "元" + num + "个人抢======================================================="); test3(amount, min, num); }}private static void test3(BigDecimal amount,BigDecimal min ,BigDecimal num){ final Random random = new Random(); final int[] rand = new int[num.intValue()]; BigDecimal sum1 = BigDecimal.ZERO; BigDecimal redpeck ; int sum = 0; for (int i = 0; i < num.intValue(); i++) { rand[i] = random.nextInt(100); sum += rand[i]; } final BigDecimal bigDecimal = new BigDecimal(sum); BigDecimal remain = amount.subtract(min.multiply(num)); for (int i = 0; i < rand.length; i++) { if(i == num.intValue() -1){ redpeck = remain; }else{ redpeck = remain.multiply(new BigDecimal(rand[i])).divide(bigDecimal,2,RoundingMode.FLOOR); } if(remain.compareTo(redpeck) > 0){ remain = remain.subtract(redpeck); }else{ remain = BigDecimal.ZERO; } sum1= sum1.add(min.add(redpeck)); System.out.println("第"+(i+1)+"个人抢到红包金额为:"+min.add(redpeck)); } System.out.println("校验每个红包累计额度是否等于红包总额结果:"+(amount.compareTo(sum1)==0));}
测试结果如下:此算法随机性较大,且性能不好
0.1元10个人抢=======================================================第1个人抢到红包金额为:0.01第2个人抢到红包金额为:0.01第3个人抢到红包金额为:0.01第4个人抢到红包金额为:0.01第5个人抢到红包金额为:0.01第6个人抢到红包金额为:0.01第7个人抢到红包金额为:0.01第8个人抢到红包金额为:0.01第9个人抢到红包金额为:0.01第10个人抢到红包金额为:0.01校验每个红包累计额度是否等于红包总额结果:true100元10个人抢=======================================================第1个人抢到红包金额为:19.84第2个人抢到红包金额为:2.73第3个人抢到红包金额为:8.95第4个人抢到红包金额为:14.10第5个人抢到红包金额为:18.60第6个人抢到红包金额为:3.66第7个人抢到红包金额为:9.17第8个人抢到红包金额为:15.49第9个人抢到红包金额为:5.61第10个人抢到红包金额为:1.85校验每个红包累计额度是否等于红包总额结果:true1元10个人抢=======================================================第1个人抢到红包金额为:0.02第2个人抢到红包金额为:0.28第3个人抢到红包金额为:0.03第4个人抢到红包金额为:0.02第5个人抢到红包金额为:0.11第6个人抢到红包金额为:0.23第7个人抢到红包金额为:0.18第8个人抢到红包金额为:0.09第9个人抢到红包金额为:0.03第10个人抢到红包金额为:0.01校验每个红包累计额度是否等于红包总额结果:true1000元10个人抢=======================================================第1个人抢到红包金额为:69.28第2个人抢到红包金额为:14.68第3个人抢到红包金额为:373.16第4个人抢到红包金额为:274.73第5个人抢到红包金额为:30.77第6个人抢到红包金额为:30.76第7个人抢到红包金额为:95.55第8个人抢到红包金额为:85.20第9个人抢到红包金额为:10.44第10个人抢到红包金额为:15.43校验每个红包累计额度是否等于红包总额结果:true