一个简单的抽奖算法

通过随机数的区间分布实现一个抽奖算法。接受一个包含奖品中奖概率的list,返回中奖的奖品。
先定义一个抽象奖品类。

public class AbstractPrize {

	private double probability; // 中奖概率

	private int prizeNum; // 奖品数量(暂时没用到)

	private String prizeName; // 奖品名称

	public double getProbability() {
		return probability;
	}

	public void setProbability(double probability) {
		this.probability = probability;
	}

	public int getPrizeNum() {
		return prizeNum;
	}

	public void setPrizeNum(int prizeNum) {
		this.prizeNum = prizeNum;
	}

	public String getPrizeName() {
		return prizeName;
	}

	public void setPrizeName(String prizeName) {
		this.prizeName = prizeName;
	}

	public AbstractPrize(double probability, int prizeNum, String prizeName) {
		super();
		this.probability = probability;
		this.prizeNum = prizeNum;
		this.prizeName = prizeName;
	}
}

抽奖算法,思路是通过奖品中奖概率的最大小数位数获得一个基数,比如奖品为a,b,c,d,e中奖概率分别为0.3,0.050,0.1,0.3,0.25,那么得到基数为1000,奖品分布区间为0<=a<300,300<b<350,350<=c<450,450<=d<750,750<=e<1000,随后获得一个0-1000的随机数,顺序遍历奖品(很重要,不能多线程遍历),<300返回a,<350返回b,<450返回c,<750返回d,<1000返回e。
 

import java.math.BigDecimal;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;

public class Lottery {

	/**
	 * 抽奖算法
	 * 
	 * @param prizes
	 * @param <T>
	 * @return
	 * @throws IllegalArgumentException
	 */
	@SuppressWarnings("unchecked")
	public static <T> T lotteryFunction(List<? extends AbstractPrize> prizes) throws IllegalArgumentException {

		prizes = prizes.stream().filter(e -> e.getProbability() != 0d).filter(e -> e.getPrizeNum() >= 0)
				.collect(Collectors.toList());

		Double base = verification(prizes);
		int index = 0;
		if (base != 0d) {
			BigDecimal fixRate = BigDecimal.ZERO;
			// 产生随机数
			long result = nextLong(new Random(), base.longValue());
			BigDecimal res = new BigDecimal(String.valueOf(result));
			// 计算奖品分布区间
			for (AbstractPrize prize : prizes) {
				if (prize.getProbability() == 0d) {
					continue;
				}
				fixRate = fixRate.add(new BigDecimal(String.valueOf(prize.getProbability()))
						.multiply(new BigDecimal(String.valueOf(base))));
				if (res.compareTo(fixRate) == -1) {
					break;
				}
				index++;
			}

			if (index <= prizes.size() - 1) {
				return (T) prizes.get(index);
			}
		}
		return null;
	}

	/**
	 * 产生一个指定范围内的long型随机数
	 * 
	 * @param random
	 * @param base
	 * @return
	 */
	private static long nextLong(Random random, long base) {
		long bits, val;
		do {
			bits = (random.nextLong() << 1) >>> 1;
			val = bits % base;
		} while (bits - val + (base - 1) < 0L);
		return val;
	}

	/**
	 * 计算基数精度,校验所有概率和不能超过1
	 * 
	 * @param prizes
	 * @return
	 * @throws IllegalArgumentException
	 */
	private static Double verification(List<? extends AbstractPrize> prizes) throws IllegalArgumentException {
		BigDecimal rateSum = BigDecimal.ZERO;
		Double base = 0d;
		for (AbstractPrize prize : prizes) {
			rateSum = rateSum.add(new BigDecimal(String.valueOf(prize.getProbability())));
			Boolean verify = prize.getProbability() < 0 || rateSum.compareTo(new BigDecimal("1")) == 1;
			if (verify) {
				throw new IllegalArgumentException("概率参数设置异常!");
			}
			String str = prize.getProbability() + "";
			int len = str.length() - str.indexOf(".") - 1;
			// 最小基数为100,好像没必要
			Double temp = Math.pow(10, len < 2 ? 2 : len);
			if (temp > base) {
				base = temp;
			}
		}
		return base;
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值