通过随机数的区间分布实现一个抽奖算法。接受一个包含奖品中奖概率的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;
}
}

3684

被折叠的 条评论
为什么被折叠?



