关于红包算法,其实笔者早在16年某个直播平台实现过。论彻底随机性,网上可刊资料少之又少。在网上查阅的资料大部分都是非随机分配、均值波动分配等方案,那么以下笔者来分享一套红包的分配算法。
介绍下网上惯用的做法:
1、均值波动分配
即根据总金额和数量,计算出平均值,为每个红包的金额进行上下随机波动,计算出所有红包的金额。
2、随机金额分配
从第一个红包开始,从总金额范围取随机值(通常根据平均值进行范围控制),分配所有红包的金额。
那么以上两种做法,需要有过多的判断,包括剩余金额、剩余红包数,控制不好可能出现金额超支,金额剩余等情况。并且对于随机依据,如果处理不好会出现红包大小规律。且本身以上两种做法非完全随机分配。
已知条件和需求:
1、红包的数量,是一个整数
2、红包的金额存在最小单位(如微信是0.01元)
3、需保证每个红包都有金额被分配
4、需保证所有红包分配完,金额刚好应用完
5、需保证红包随机性,避免规律性可查可控
综上所述,那么我们需要做什么呢。
1、根据最低额度和红包总额取指定数量(红包个数)的随机数
2、累加第一步中的随机数,作为权重基数
3、使用每个随机数除以权重基数,得到分配比例
4、使用总额乘以每个分配比例,得到每个红包的实际分配额
其实到了这里,大家应该也就明白了
那么看代码:
1package org.coody.plugs.redpack; 2 3import java.math.BigDecimal; 4import java.math.RoundingMode; 5import java.util.ArrayList; 6import java.util.Arrays; 7import java.util.List; 8 9import org.coody.plugs.redpack.util.RandomUtil; 10 11public class RedPacketHandle { 12 13 /** 14 * 红包分配算法 15 * 16 * @param amount 17 * @param num 18 * @param unit 19 * @return 20 */ 21 public static List<BigDecimal> makePackates(BigDecimal amount, Integer num, Integer unit) { 22 if (num == 1) { 23 return new ArrayList<BigDecimal>(Arrays.asList(amount)); 24 } 25 List<BigDecimal> prs = new ArrayList<BigDecimal>(); 26 27 BigDecimal minAmount = new BigDecimal(Math.pow(10, -unit)).setScale(unit, RoundingMode.DOWN); 28 29 BigDecimal randomStart = new BigDecimal(num); 30 BigDecimal randomEnd = amount.divide(minAmount); 31 for (int i = 0; i < num; i++) { 32 prs.add(RandomUtil.random(randomStart, randomEnd)); 33 } 34 BigDecimal totalPr = prs.stream().map(item -> item).reduce(BigDecimal.ZERO, BigDecimal::add); 35 totalPr.setScale(unit * 2, RoundingMode.DOWN); 36 List<BigDecimal> packages = new ArrayList<BigDecimal>(); 37 38 BigDecimal surplus = amount; 39 for (BigDecimal pr : prs) { 40 if (pr == prs.get(prs.size() - 1)) { 41 packages.add(surplus.setScale(unit, RoundingMode.DOWN)); 42 continue; 43 } 44 BigDecimal value = amount.multiply(pr).divide(totalPr, unit * 2, RoundingMode.DOWN).setScale(unit, 45 RoundingMode.DOWN); 46 surplus = surplus.subtract(value); 47 packages.add(value); 48 49 } 50 return packages; 51 } 52 53 public static void main(String[] args) { 54 /** 55 * 红包金额 56 */ 57 BigDecimal amount = new BigDecimal(1000); 58 59 /** 60 * 红包个数 61 */ 62 Integer num = 10; 63 64 /** 65 * 数值精度(小数位数) 66 */ 67 Integer unit = 2; 68 69 List<BigDecimal> list = makePackates(amount, num, unit); 70 71 for (BigDecimal line : list) { 72 System.out.println(line); 73 } 74 } 75}
运行这份代码我们可以看到:
174.74 2121.35 380.07 4137.36 550.71 686.37 7150.16 880.19 9117.27 10101.78
致此,我们的红包分配算法已经完成了。然后对性能进行测试

分配1W次耗时->137
==================================分割线==================================
以下是红包分配算法中用到的RandomUtil:
1package org.coody.plugs.redpack.util; 2 3import java.math.BigDecimal; 4 5public class RandomUtil { 6 7 public static BigDecimal random(BigDecimal start, BigDecimal end) { 8 9 BigDecimal startMultiplyingPower = new BigDecimal(getMultiplyingPower(start)); 10 BigDecimal endMultiplyingPower = new BigDecimal(getMultiplyingPower(end)); 11 BigDecimal multiplyingPower = startMultiplyingPower; 12 if (endMultiplyingPower.compareTo(startMultiplyingPower) > 0) { 13 multiplyingPower = endMultiplyingPower; 14 } 15 16 start = start.multiply(multiplyingPower); 17 end = end.multiply(multiplyingPower); 18 19 Long random = random(start.longValue(), end.longValue()); 20 21 BigDecimal value = new BigDecimal(random).divide(multiplyingPower); 22 return value; 23 } 24 25 private static Integer getMultiplyingPower(BigDecimal start) { 26 if (start.compareTo(new BigDecimal(start.intValue())) == 0) { 27 return 1; 28 } 29 Integer multiplyingPower = 1; 30 while (start.compareTo(new BigDecimal(start.intValue())) > 0) { 31 start = start.multiply(new BigDecimal(10)); 32 multiplyingPower *= 10; 33 } 34 return multiplyingPower; 35 } 36 37 public static long random(long start, long end) { 38 return (long) (Math.random() * (end - start + 1)) + start; 39 } 40}
=================================================================
作者:Coody
Git地址:https://gitee.com/coodyer
版权:©2014-2020 Test404 All right reserved. 版权所有
反馈邮箱:644556636@qq.com
反馈群:Java泛太平洋研究中心 218481849