最近在项目中分配了一个抽奖模块的任务,这里先说一下需求把:每个抽奖活动后台会配置多个中奖奖品,分为特殊奖品和普通奖品,所有奖品的中奖概率之和加起来为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来比较,最后选中奖品。
原文地址:https://www.cnblogs.com/xslzjbra/p/10037304.html
时间: 2024-10-09 22:29:10