多件商品根据几率抽奖

  最近在项目中分配了一个抽奖模块的任务,这里先说一下需求把:每一个抽奖活动后台会配置多个中奖奖品,分为特殊奖品和普通奖品,全部奖品的中奖几率之和加起来为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()方法,这个方法很重要,后面再说。dom

/**
     * 从抽奖奖品列表中随机抽中一个
     *
     * @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来比较,最后选中奖品。ide

相关文章
相关标签/搜索