多件商品根据概率抽奖

Posted xslzjbra

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多件商品根据概率抽奖相关的知识,希望对你有一定的参考价值。

  最近在项目中分配了一个抽奖模块的任务,这里先说一下需求把:每个抽奖活动后台会配置多个中奖奖品,分为特殊奖品和普通奖品,所有奖品的中奖概率之和加起来为1。用户端用户抽奖需要根据概率来随机抽中一个商品。开始我脑子生出来的第一想法是生成一个随机数,然后让这个随机数跟概率去比较,取小于这个随机数的最大一个概率对应的商品为中奖商品,后来一想,发现自己想的太简单直观了,这样抽中的商品中奖概率不满足配置的中奖概率。在网上搜了一下相关的问题,然后就弄清楚了。说来惭愧,这么一个简单的算法题,自己竟然第一时间没有没有想到。所以在这里把这个问题记录下来。

public class DrawGoodsDO implements Comparable<DrawGoodsDO>{

    //主键id
    private long id;
    //抽奖id
    private long drawId;
    //商品名称
    private String goodsName;
    //商品图片地址
    private String goodsImageUrl;
    //上架库存
    private int drawStock;
    //当前库存
    private int drawStockCur;
    //商品类型,1特殊商品,2普通商品
    private int goodsType;
    // 中奖概率
    private double drawRate;
    // 添加时间
    private Timestamp addTime;

    //商品图片地址,绝对路径
    private String goodsImageUrlFormat;

    /**
     * 商品图片地址,绝对路径
     *
     * @return GoodsImageUrlFormat the GoodsImageUrlFormat
     */
    public String getGoodsImageUrlFormat() {
        return goodsImageUrlFormat;
    }

    /**
     * 商品图片地址,绝对路径
     *
     * @param goodsImageUrlFormat the goodsImageUrlFormat to set
     */
    public void setGoodsImageUrlFormat(String goodsImageUrlFormat) {
        this.goodsImageUrlFormat = goodsImageUrlFormat;
    }

    //主键id
    public long getId() {
        return this.id;
    }

    //主键id
    public void setId(long id) {
        this.id = id;
    }

    //抽奖id
    public long getDrawId() {
        return this.drawId;
    }

    //抽奖id
    public void setDrawId(long drawId) {
        this.drawId = drawId;
    }

    //商品名称
    public String getGoodsName() {
        return this.goodsName;
    }

    //商品名称
    public void setGoodsName(String goodsName) {
        this.goodsName = goodsName;
    }

    //商品图片地址
    public String getGoodsImageUrl() {
        return this.goodsImageUrl;
    }

    //商品图片地址
    public void setGoodsImageUrl(String goodsImageUrl) {
        if (goodsImageUrl != null) {
            setGoodsImageUrlFormat(FileUrlConfig.file_visit_url + goodsImageUrl);
        }
        this.goodsImageUrl = goodsImageUrl;
    }

    //上架库存
    public int getDrawStock() {
        return this.drawStock;
    }

    //上架库存
    public void setDrawStock(int drawStock) {
        this.drawStock = drawStock;
    }

    //当前库存
    public int getDrawStockCur() {
        return this.drawStockCur;
    }

    //当前库存
    public void setDrawStockCur(int drawStockCur) {
        this.drawStockCur = drawStockCur;
    }

    //商品类型,1特殊商品,2普通商品
    public int getGoodsType() {
        return this.goodsType;
    }

    //商品类型,1特殊商品,2普通商品
    public void setGoodsType(int goodsType) {
        this.goodsType = goodsType;
    }

    // 获取 中奖概率
    public double getDrawRate() {
        return this.drawRate;
    }

    // 设置 中奖概率
    public void setDrawRate(double drawRate) {
        this.drawRate = drawRate;
    }

    // 获取 添加时间
    public Timestamp getAddTime() {
        return this.addTime;
    }

    // 设置 添加时间
    public void setAddTime(Timestamp addTime) {
        this.addTime = addTime;
    }

    @Override
    public int compareTo(DrawGoodsDO drawGoods) {
        if (this.drawRate >= drawGoods.getDrawRate()) {
            return 1;
        }
        return -1;
    }
}

这个是抽奖奖品实体类,他实现了Comparable接口,实现了compareTo()方法,这个方法很重要,后面再说。

/**
     * 从抽奖奖品列表中随机抽中一个
     *
     * @param drawGoodsList 奖品列表
     * @return
     */
    private DrawGoodsDO randomGetDrawGoods(List<DrawGoodsDO> drawGoodsList) {
        if (ValidateUtil.isNull(drawGoodsList)) {
            return null;
        }
        //将奖品按概率从小到大排序
        Collections.sort(drawGoodsList);
        //求出总概率
        double sumRate = 0D;
        for (DrawGoodsDO drawGoodsDO : drawGoodsList) {
            sumRate += drawGoodsDO.getDrawRate();
        }
        if (sumRate != 100) {
            //如果总概率之和不为100,重新计算他们的概率,让他们的概率和为100
            for (DrawGoodsDO drawGoodsDO : drawGoodsList) {
                drawGoodsDO.setDrawRate(drawGoodsDO.getDrawRate() * 100 / sumRate);
            }
        }

        //将每个奖品中奖区间段保存到list里面
        List<Double> list = new ArrayList<>();
        double rate = 0D;
        for (DrawGoodsDO drawGoodsDO : drawGoodsList) {
            rate += drawGoodsDO.getDrawRate() / sumRate;
            list.add(rate);
        }
        //找出符合概率得奖品所占的索引位置
        int index = 0;
        double randomNum = Math.random();
        for (int i=0;i<list.size();i++) {
            if (randomNum > list.get(i)) {
                index = i + 1;
            }
        }
        return drawGoodsList.get(index);
    }

上面就是主要的实现方法,这里最重要的就是先将奖品列表按照概率从小到大排序,因为Collections.sort()方法需要列表元素实现Comparable接口。所以上面的实体类中才那样写。然后根据中奖概率算出中奖区间段并且保存到list中,最后在生成一个随机数与这个中间区间段的list来比较,最后选中奖品。

以上是关于多件商品根据概率抽奖的主要内容,如果未能解决你的问题,请参考以下文章

前端+php实现概率抽奖

项目实战——Java根据奖品权重计算中奖概率实现抽奖(适用于砸金蛋大转盘等抽奖活动)

微信抽奖系统源码 v3.0

js转盘大抽奖 自定义概率

使用postman实现半自动化

概率抽奖