通过随机数的区间分布实现一个抽奖算法。接受一个包含奖品中奖概率的list,返回中奖的奖品。
先定义一个抽象奖品类。
public abstract class AbstractPrize { /** * 奖品概率 */ private double probability;public double getProbability() { return probability; } public void setProbability(double probability) { this.probability = probability; }}
抽奖算法,思路是通过奖品中奖概率的最大小数位数获得一个基数,比如奖品为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.
/** * 抽奖算法 * @param prizes * @param <T> * @return * @throws IllegalArgumentException */ public static <T> T lotteryFunction(List<? extends AbstractPrize> prizes) throws IllegalArgumentException{
prizes = prizes.stream().filter(e -> e.getProbability() != 0d).collect(Collectors.toList());
Double base = this.verification(prizes); int index = 0; if(base != 0d) { BigDecimal fixRate = new BigDecimal("0"); //产生随机数 Random random = new Random(); long result = nextLong(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)))); Boolean flag = res.compareTo(fixRate) == -1; if (flag) { break; }
index++;}
if(index <= prizes.size() - 1 ) { return (T) prizes.get(index); } } return null; }
/** * 产生一个指定范围内的long型随机数 * @param rng * @param n * @return */ private static long nextLong(Random rng, long n) { long bits, val; do { bits = (rng.nextLong() << 1) >>> 1; val = bits % n; } while (bits-val+(n-1) < 0L); return val; } /** * 计算基数精度,校验所有概率和不能超过1 * @param prizes * @return * @throws IllegalArgumentException */ private static Double verification(List<? extends AbstractPrize> prizes) throws IllegalArgumentException{ BigDecimal rateSum = new BigDecimal("0"); 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("probability error"); } 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; }
版权声明:本文为u013979547原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。