概要
公司最近给了一个抽奖活动的需求,用户在平台能够在某活动抽奖页面进行抽奖,并且在后台能配置某个奖品的中奖几率。
原型图
数据库表设计
在这里我主要设计了
活动表:活动信息,包括一些活动名字,活动时间等
奖品表:奖品信息,类型,奖品名字等
活动奖品中间表:活动表奖品表是多对多的关系,还有一些配置信息
活动规则表:主要就是抽奖次数限制,中奖次数限制等,关联活动id
技术细节
核心代码
//查询出所有奖品
List<TfAwardInfoNumVo> awardInfoNumVoList = tfAwardInfoMapper.selectByActivityId(drawsEvt.getActivityId());
TfAwardInfoNumVo activityAward = doWeights(awardInfoNumVoList, drawsEvt.getActivityId());
public TfAwardInfoNumVo doWeights(List<TfAwardInfoNumVo> awardInfoNumVoList,Long activityId){
//奖品信息
TfAwardInfoNumVo tfAwardInfoNumVo = null;
//抽到奖品
Long random = getPrize(awardInfoNumVoList);
//然后根据奖品id和对应活动的id去数据库查询对应的奖品
tfAwardInfoNumVo = tfAwardInfoMapper.queryAwardType(random, activityId);
//奖品数量
Integer prizeNum = tfAwardInfoNumVo.getAwardQuotient();
int i = prizeNum;
//奖品数量为0然后循环直到抽到其他奖品
while (i == 0) {
int random1 = getPrize(awardInfoNumVoList);
tfAwardInfoNumVo = tfAwardInfoMapper.queryAwardType(random1, activityId);
i = tfAwardInfoNumVo.getAwardQuotient();
}
i--;
tfAwardInfoNumVo.setAwardQuotient(i);
return tfAwardInfoNumVo;
}
private Long getPrize(List<TfAwardInfoNumVo> awardInfoNumVoList) {
//给个初始值
Long random = 0L;
try {
//计算权重和
double sumWeight = 0;
for (TfAwardInfoNumVo tfAwardInfoNumVo : awardInfoNumVoList) {
sumWeight += Double.valueOf(tfAwardInfoNumVo.getWinningProbability());
}
//产生随机数
double randomNumber;
randomNumber = Math.random();
//根据随机数在所有奖品分布的区域并确定所抽奖品
double d1 = 0;
double d2 = 0;
for (int i = 0; i < awardInfoNumVoList.size(); i++) {
d2 += Double.parseDouble(String.valueOf(awardInfoNumVoList.get(i).getWinningProbability())) / sumWeight;
if (i == 0) {
d1 = 0;
} else {
d1 += Double.parseDouble(String.valueOf(awardInfoNumVoList.get(i - 1).getWinningProbability())) / sumWeight;
}
if (randomNumber >= d1 && randomNumber <= d2) {
//返回抽中奖品的id
random = awardInfoNumVoList.get(i).getId();
break;
}
}
} catch (Exception e) {
log.error("生成抽奖随机数出错,出错原因:" + e.getMessage());
}
return random;
}
}
根据代码中的逻辑,tfAwardInfoNumVo.getWinningProbability() 返回的值表示对应奖品的中奖概率,而且这个概率是按照列表中奖品的顺序累加计算的。
在随机抽奖的过程中,生成一个在 [0, 1) 范围内的随机数 randomNumber,然后根据这个随机数在整个中奖概率分布的区域内确定最终中奖的奖品。如果某个奖品的中奖概率较大,那么在总的中奖概率分布中,该奖品对应的区域就相对较大,因此它更有可能被抽中。
简而言之,tfAwardInfoNumVo.getWinningProbability() 的值越大,对应奖品被抽中的概率就越高。这是一种常见的按权重分配概率的抽奖实现方式。
让我们深入解释一下为什么 tfAwardInfoNumVo.getWinningProbability() 越大,抽中的概率越高。
在这段代码中,tfAwardInfoNumVo.getWinningProbability() 返回的值被用作奖品中奖的概率。概率是一个在 [0, 1] 范围内的值,表示事件发生的可能性。在这里,我们假设概率的最小值为0,表示不可能发生,而最大值为1,表示肯定会发生。
随机抽奖的基本思想是:根据每个奖品的中奖概率,为每个奖品分配一个相应的概率区间,然后生成一个随机数,根据这个随机数所在的区间确定最终抽中的奖品。
在代码中,概率的计算方式是将所有奖品的中奖概率相加,然后根据每个奖品的中奖概率在总概率中的比例,分配对应的区间。因此,一个中奖概率较高的奖品,在总概率中占据较大的比例,从而更有可能在随机抽奖中被选中。
举例来说,如果有两个奖品,A 的中奖概率为0.7,B 的中奖概率为0.3。总概率为1(0.7 + 0.3)。那么,A 的区间为 [0, 0.7],B 的区间为 (0.7, 1]。如果生成的随机数落在 [0, 0.7] 区间内,就会选中 A 奖品,而在 (0.7, 1] 区间内就会选中 B 奖品。
因此,tfAwardInfoNumVo.getWinningProbability() 越大,对应奖品的区间就越大,抽中的概率也就越高。
小结
我在数据库中造了一些假数据,话费劵这个奖品我给的概率是30%,其他都是10%
postman调了几次发现话费劵抽中的概率确实比其他大不少,好了以上就是一个简单的抽奖活动的一个实现。