Java实现随机红包分配算法 [非均值波动]

关于红包算法,其实笔者早在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

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

java常用类(2)

三、时间处理相关类Date类:计算机世界把1970年1月1号定为基准时间,每个度量单位是毫秒(1秒的千分之一),用long类型的变量表示时间。Date分配Date对象并初始化对象,以表示自从标准基准时间(称为“历元”(epoch),即1970年1月1日08:00:00GMT)以来的指定毫秒数。示例:packagecn.tanjian

RokectMQ 顺序性 和分布式事务

1.顺序性是根据参数的id来使其同时投递到统一队列上。//RocketMQ通过MessageQueueSelector中实现的算法来确定消息发送到哪一个队列上//RocketMQ默认提供了两种MessageQueueSelector实现:随机/Hash//当然你可以根据业务实现自己的MessageQueueSelecto

2021 春节微信红包封面制作教程

!(https://imgblog.csdnimg.cn/20210209192409262.jpeg)哈喽,2021春节就要来了!为大家准备了微信红包封面制作教程,如有需要,请自行取用!前期准备素材红包封面图片红包封面故事封面故事是指,在好友领取你的红包之后,下拉红包详情页面看到的完整图片