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