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

原创
2020/03/15 19:13
阅读数 717

关于红包算法,其实笔者早在16年某个直播平台实现过。论彻底随机性,网上可刊资料少之又少。在网上查阅的资料大部分都是非随机分配、均值波动分配等方案,那么以下笔者来分享一套红包的分配算法。

介绍下网上惯用的做法:

1、均值波动分配

    即根据总金额和数量,计算出平均值,为每个红包的金额进行上下随机波动,计算出所有红包的金额。

2、随机金额分配

    从第一个红包开始,从总金额范围取随机值(通常根据平均值进行范围控制),分配所有红包的金额。

那么以上两种做法,需要有过多的判断,包括剩余金额、剩余红包数,控制不好可能出现金额超支,金额剩余等情况。并且对于随机依据,如果处理不好会出现红包大小规律。且本身以上两种做法非完全随机分配。

已知条件和需求:

1、红包的数量,是一个整数

2、红包的金额存在最小单位(如微信是0.01元)

3、需保证每个红包都有金额被分配

4、需保证所有红包分配完,金额刚好应用完

5、需保证红包随机性,避免规律性可查可控

综上所述,那么我们需要做什么呢。

1、根据最低额度和红包总额取指定数量(红包个数)的随机数

2、累加第一步中的随机数,作为权重基数

3、使用每个随机数除以权重基数,得到分配比例

4、使用总额乘以每个分配比例,得到每个红包的实际分配额

其实到了这里,大家应该也就明白了

那么看代码:

package org.coody.plugs.redpack;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.coody.plugs.redpack.util.RandomUtil;

public class RedPacketHandle {

	/**
	 * 红包分配算法
	 * 
	 * @param amount
	 * @param num
	 * @param unit
	 * @return
	 */
	public static List<BigDecimal> makePackates(BigDecimal amount, Integer num, Integer unit) {
		if (num == 1) {
			return new ArrayList<BigDecimal>(Arrays.asList(amount));
		}
		List<BigDecimal> prs = new ArrayList<BigDecimal>();

		BigDecimal minAmount = new BigDecimal(Math.pow(10, -unit)).setScale(unit, RoundingMode.DOWN);

		BigDecimal randomStart = new BigDecimal(num);
		BigDecimal randomEnd = amount.divide(minAmount);
		for (int i = 0; i < num; i++) {
			prs.add(RandomUtil.random(randomStart, randomEnd));
		}
		BigDecimal totalPr = prs.stream().map(item -> item).reduce(BigDecimal.ZERO, BigDecimal::add);
		totalPr.setScale(unit * 2, RoundingMode.DOWN);
		List<BigDecimal> packages = new ArrayList<BigDecimal>();

		BigDecimal surplus = amount;
		for (BigDecimal pr : prs) {
				if (pr == prs.get(prs.size() - 1)) {
					packages.add(surplus.setScale(unit, RoundingMode.DOWN));
					continue;
				}
				BigDecimal value = amount.multiply(pr).divide(totalPr, unit * 2, RoundingMode.DOWN).setScale(unit,
						RoundingMode.DOWN);
				surplus = surplus.subtract(value);
				packages.add(value);

		}
		return packages;
	}

	public static void main(String[] args) {
		/**
		 * 红包金额
		 */
		BigDecimal amount = new BigDecimal(1000);

		/**
		 * 红包个数
		 */
		Integer num = 10;

		/**
		 * 数值精度(小数位数)
		 */
		Integer unit = 2;

		List<BigDecimal> list = makePackates(amount, num, unit);
		
		for (BigDecimal line : list) {
			System.out.println(line);
		}
	}
}

运行这份代码我们可以看到:

74.74
121.35
80.07
137.36
50.71
86.37
150.16
80.19
117.27
101.78

致此,我们的红包分配算法已经完成了。然后对性能进行测试

 

分配1W次耗时->137

==================================分割线==================================

以下是红包分配算法中用到的RandomUtil:

package org.coody.plugs.redpack.util;

import java.math.BigDecimal;

public class RandomUtil {

	public static BigDecimal random(BigDecimal start, BigDecimal end) {

		BigDecimal startMultiplyingPower = new BigDecimal(getMultiplyingPower(start));
		BigDecimal endMultiplyingPower = new BigDecimal(getMultiplyingPower(end));
		BigDecimal multiplyingPower = startMultiplyingPower;
		if (endMultiplyingPower.compareTo(startMultiplyingPower) > 0) {
			multiplyingPower = endMultiplyingPower;
		}

		start = start.multiply(multiplyingPower);
		end = end.multiply(multiplyingPower);

		Long random = random(start.longValue(), end.longValue());

		BigDecimal value = new BigDecimal(random).divide(multiplyingPower);
		return value;
	}

	private static Integer getMultiplyingPower(BigDecimal start) {
		if (start.compareTo(new BigDecimal(start.intValue())) == 0) {
			return 1;
		}
		Integer multiplyingPower = 1;
		while (start.compareTo(new BigDecimal(start.intValue())) > 0) {
			start = start.multiply(new BigDecimal(10));
			multiplyingPower *= 10;
		}
		return multiplyingPower;
	}

	public static long random(long start, long end) {
		return (long) (Math.random() * (end - start + 1)) + start;
	}
}

=================================================================

作者:Coody

Git地址:https://gitee.com/coodyer

版权:©2014-2020 Test404 All right reserved. 版权所有

反馈邮箱:644556636@qq.com

反馈群:Java泛太平洋研究中心 218481849

 

展开阅读全文
加载中
点击加入讨论🔥(3) 发布并加入讨论🔥
3 评论
1 收藏
0
分享
返回顶部
顶部