盲盒抽奖功能实现


盲盒抽奖功能实现

盲盒、抽奖类的功能的要点是如何计算商品获得的概率区间;

例如现在我们有5件商品:[“铅笔”,”笔记本”,”书包”,”耳机”,”手机”],它们的获得概率是[0.6, 0.2, 0.1, 0.08, 0.02];它们的概率总和是1,也就是说任何一个随机小数一定是能获得以上商品的;我们不能以该概率数组为商品获得概率区间,因为如果随机生成一个随机数0.7那么就没有与之匹配的商品,那么就不能满足概率总和为1;

一般会使用离散算法解决以上问题,也就是将奖品获取的概率坐落在0~1的区间内;如:[0.6, 0.2, 0.1, 0.08, 0.02]在0~1内的区间为[1, 0.4, 0.2, 0.1, 0.02];这样生成一个随机小数,小于0.02的代表手机,0.02 ~ 0.1代表耳机,0.1 ~ 0.2代表书包,0.2 ~ 0.4代表笔记本,0.4 ~ 1代表铅笔;

生成一个值为0.5的随机数该值属于0.4 ~ 1的范围内,那么获得的商品就是铅笔。

离散算法可以把概率坐落在0 ~ 1的坐标内,基本可以保证概率的平均分布。

代码实现:

盲盒类:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class BlindBox {

    /**
     * id
     */
    private int id;

    /**
     * 商品名字
     */
    private String name;

    /**
     * 获得权重
     */
    private int weight;
}

抽奖类:

public class Lottery {

    /**
     * 盲盒列表
     */
    private List<BlindBox> blindBoxes;

    public Lottery() {
        //创建盲盒
        this.blindBoxes = new ArrayList<>();
        //添加盲盒商品及获得权重
        this.blindBoxes.add(new BlindBox(1, "铅笔", 60));
        this.blindBoxes.add(new BlindBox(2, "笔记本", 20));
        this.blindBoxes.add(new BlindBox(4, "耳机", 8));
        this.blindBoxes.add(new BlindBox(5, "手机", 2));
        this.blindBoxes.add(new BlindBox(3, "书包", 10));
        //根据权重进行排序
        this.blindBoxes.sort((o1, o2) -> o1.getWeight() - o2.getWeight());
        System.out.println("排序后的列表:" + this.blindBoxes);
    }

    /**
     * 获取盲盒
     *
     * @return {@link BlindBox }
     */
    public BlindBox getBlindBox() {
        //1.计算每个盲盒的获取概率
        List<Double> probabilities = new ArrayList<>();
        //获取总权重
        Integer sum = blindBoxes.stream().map(BlindBox::getWeight).reduce((o1, o2) -> o1 + o2).get();
        System.out.println("总权重:" + sum);
        //商品的概率区间
        double probabilityArea = 0;
        //计算商品获取的概率区间
        for (int i = 0; i < blindBoxes.size(); i++) {
            BlindBox blindBox = blindBoxes.get(i);
            double probability = (double) blindBox.getWeight() / sum;
            System.out.println(blindBox.getName() + "的概率:" + probability);
            //前一个概率区间
            double preProbabilityArea = probabilityArea;
            probabilityArea += probability;
            System.out.println(blindBox.getName() + "的概率区间:" + preProbabilityArea + "~" + probabilityArea);
            probabilities.add(probabilityArea);
        }
        System.out.println("所有商品概率区间:" + probabilities);
        //2.随机生成一个随机数,计算该随机数在哪个区间内,返回该区间内对应的盲盒
        ThreadLocalRandom random = ThreadLocalRandom.current();
        //获取随机概率
        double rate = random.nextDouble(0, 1);
        System.out.println("获取随机概率:" + rate);
        //计算随机概率在哪个区间内
        probabilities.add(rate);
        //排序概率集合
        probabilities.sort(Double::compareTo);
        System.out.println("概率区间数组:" + probabilities);
        //获取概率的索引
        int idx = probabilities.indexOf(rate);
        System.out.println("获取概率的索引:" + idx);
        return blindBoxes.get(idx);
    }

    public static void main(String[] args) {
        Lottery lottery = new Lottery();
        System.out.println("抽中商品:" + lottery.getBlindBox());
    }
}

运行结果:

排序后的列表:[BlindBox(id=5, name=手机, weight=2), BlindBox(id=4, name=耳机, weight=8), BlindBox(id=3, name=书包, weight=10), BlindBox(id=2, name=笔记本, weight=20), BlindBox(id=1, name=铅笔, weight=60)]
总权重:100
手机的概率:0.02
手机的概率区间:0.0~0.02
耳机的概率:0.08
耳机的概率区间:0.02~0.1
书包的概率:0.1
书包的概率区间:0.1~0.2
笔记本的概率:0.2
笔记本的概率区间:0.2~0.4
铅笔的概率:0.6
铅笔的概率区间:0.4~1.0
所有商品概率区间:[0.02, 0.1, 0.2, 0.4, 1.0]
获取随机概率:0.3618944022013255
概率区间数组:[0.02, 0.1, 0.2, 0.3618944022013255, 0.4, 1.0]
获取概率的索引:3
抽中商品:BlindBox(id=2, name=笔记本, weight=20)

文章作者: 威@猫
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 威@猫 !
评论