多件商品根據機率抽獎

  最近在項目中分配了一個抽獎模塊的任務,這裏先說一下需求把:每一個抽獎活動後臺會配置多箇中獎獎品,分爲特殊獎品和普通獎品,全部獎品的中獎機率之和加起來爲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

相關文章
相關標籤/搜索