部分原文自: https://blog.csdn.net/larva_s/article/details/87532195
1. 抽奖需求
根据配置奖品的概率去抽取奖品,其中总概率不一定是 1 ,此时是按照权重去抽取奖品的。
2. 实现
2.1 思路:区间
区间 | [0, 10):奖品1 | [10, 65):谢谢参与 | [65, 98):奖品2 | [98, 100):奖品3 |
概率 | 奖品1概率:10 | 谢谢参与概率:55 | 奖品2概率:33 | 奖品3概率:2 |
- 根据奖品概率构造一个区间
- 通过随机数Rondom的API获取随机值 [0, 最大值)
- 根据随机值匹配到对应区间的奖品,即是抽取到的奖品
/**
* 奖品类
*/
@Data
@AllArgsConstructor
public class Award {
private Integer id;
private String name;
private Integer weight;
}
/**
* 通用抽奖接口
* @param <T>
*/
public interface ILotteryService<T> {
T draw(List<T> awardList) throws DrawException;
}
/**
* 区间实现抽奖概率
*/
public class IntervalLotteryService implements ILotteryService<Award>{
/**
* 通过区间来实现概率分配
* 奖品1 [0,10)
* 奖品2 [10,90)
* 奖品3 [90,100)
* 奖品4 [100,980)
* @param awardList
* @return
*/
@Override
public Award draw(List<Award> awardList) throws DrawException {
int listSize = awardList.size();
if (awardList == null || listSize < 1){
return null;
}
// 遍历list,计算区间最右值,封装到 int数组中 中
int[] interval = new int[listSize];
for (int i = 0; i < listSize; i++) {
Award award = awardList.get(i);
Integer weight = award.getWeight();
if (i == 0){
interval[0] = weight;
}else {
Integer lastAwardWeight = interval[i - 1];
// 区间最右是上一个 区间最右 + 自己的权重
interval[i] = weight + lastAwardWeight;
}
}
// random int 获取随机到的值
int maxInterval = interval[listSize - 1];
Random random = new Random();
int ranVal = random.nextInt(maxInterval);
// 遍历 int数组 ,找到随机值是位于哪个区间,获取award,返回该award
for (int i = 0; i < interval.length; i++) {
if (ranVal >= interval[i] && ranVal < interval[i + 1]){
return awardList.get(i + 1);
}else if (ranVal < interval[0]){
// 第一个区间之内
return awardList.get(0);
}
}
throw new DrawException("抽奖异常");
}
// 测试
public static void main(String args[]){
IntervalLotteryService lotteryService = new IntervalLotteryService();
List<Award> awardList = Lists.newArrayList(
new Award(1,"0.088 概率奖品", 88),
new Award(2,"0.333 概率奖品", 333),
new Award(3,"0.155 概率奖品", 155),
new Award(4,"0.424 概率奖品", 424)
);
try {
Map<String, Integer> statMap = Maps.newHashMap();
for (int i = 0; i < 1000; i++) {
Award drawAward = lotteryService.draw(awardList);
String awardName = drawAward.getName();
if (statMap.containsKey(awardName)){
Integer count = statMap.get(awardName);
statMap.put(awardName, count + 1);
}else {
statMap.put(awardName, 1);
}
}
statMap.forEach((k, v) -> System.out.println(k + " : " + v));
/*
0.088 概率奖品 : 94
0.155 概率奖品 : 152
0.333 概率奖品 : 320
0.424 概率奖品 : 434
*/
} catch (DrawException e) {
System.out.println("抛异常");
}
}
}
3. 问题
3.1 抽奖成功,但发奖失败,如何处理?
发奖失败,包括抛异常等情况,都设为抽不到奖品,兜底处理即可。
3.2 某一个奖品库存消耗完时,是如何处理的?
仍然可被抽中,只不过扣库存时发现库存已消耗完则返回谢谢参与
来源:oschina
链接:https://my.oschina.net/u/4247262/blog/3164047