大转盘抽奖逻辑--区间

半腔热情 提交于 2020-02-26 22:15:39

部分原文自: 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 某一个奖品库存消耗完时,是如何处理的?

​ 仍然可被抽中,只不过扣库存时发现库存已消耗完则返回谢谢参与

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!