前言
当我们参加各种活动和抽奖时,抽奖环节总是能引起极大兴趣和关注。抽奖的公正性和随机性是保证活动公平和成功的重要因素。
本文将介绍抽奖算法的实现原理及其在Java编程语言中的应用,以帮助读者了解抽奖程序的实现细节,同时也为读者提供一些有用的参考和借鉴。
欢迎关注个人公众号【好好学技术】交流学习
创建抽奖表
CREATE TABLE `app_draw_prize` (
`id` bigint NOT NULL,
`activity_id` int unsigned DEFAULT NULL COMMENT '活动id',
`name` varchar(50) DEFAULT NULL COMMENT '奖品名称',
`url` varchar(200) DEFAULT NULL COMMENT '图片地址(未使用)',
`value` varchar(10) DEFAULT '0' COMMENT '值',
`probability` double unsigned DEFAULT '0' COMMENT '概率',
`type` tinyint unsigned DEFAULT NULL COMMENT '类型',
`status` tinyint unsigned DEFAULT '0' COMMENT '上下架状态:0下架1上架',
`position` tinyint(1) DEFAULT NULL COMMENT '位置',
`day_max_times` tinyint unsigned DEFAULT '0' COMMENT '每天最多中奖次数,大于0时生效',
`month_max_times` tinyint unsigned DEFAULT '0' COMMENT '每月最多中奖次数,大于0时生效',
`show` tinyint unsigned DEFAULT '0' COMMENT '是否首页展示中奖记录0否1是',
`default_prize` tinyint(1) DEFAULT '0' COMMENT '抽奖上限时直接返回默认奖品',
`ref_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '关联id',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='抽奖奖品表';
抽奖代码
算法思路
- 1.将奖品按集合中顺序概率计算成所占比例区间,放入比例集合。并产生一个随机数加入其中,排序。
- 2.排序后,随机数落在哪个区间,就表示那个区间的奖品被抽中。
- 3.返回的随机数在集合中的索引,该索引就是奖品集合中的索引。
- 4.比例区间的计算通过概率相加获得。
private DrawPrize doDrawPrize(Long activityId, Long userId) {
List<DrawPrize> drawPrizes = drawPrizeMapper.selectList(new LambdaQueryWrapper<DrawPrize>()
.eq(DrawPrize::getActivityId, activityId)
.eq(DrawPrize::getStatus, 1)
.orderByDesc(DrawPrize::getProbability));
List<Double> probLists = new ArrayList<>(drawPrizes.size());
Double sumProb = 0D;
for (DrawPrize drawPrize : drawPrizes) {
sumProb += drawPrize.getProbability();
}
if (sumProb <= 0) {
throw new IllegalArgumentException("抽奖概率设置错误");
}
Double rate = 0D;
for (DrawPrize drawPrize : drawPrizes) {
rate += drawPrize.getProbability();
probLists.add(rate / sumProb);
}
double random = Math.random();
probLists.add(random);
Collections.sort(probLists);
DrawPrize drawPrize = drawPrizes.get(probLists.indexOf(random));
if (drawPrize.getDayMaxTimes() > 0) {
//判断当天中奖次数
String daily = DateUtil.formatDate(new Date());
Long count = drawRecordMapper.selectCount(new LambdaQueryWrapper<DrawRecord>()
.eq(DrawRecord::getMemberId, userId)
.eq(DrawRecord::getPrizeId, drawPrize.getId())
.eq(DrawRecord::getDaily, daily));
if (count >= drawPrize.getDayMaxTimes()) {
//直接返回谢谢参与
return drawPrizes.stream()
.filter(d -> d.getDefaultPrize() == 1)
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("抽奖错误"));
}
}
if (drawPrize.getMonthMaxTimes() > 0) {
//判断当月中奖次数
Long count = drawRecordMapper.selectCount(new LambdaQueryWrapper<DrawRecord>()
.eq(DrawRecord::getMemberId, userId)
.eq(DrawRecord::getPrizeId, drawPrize.getId())
.eq(DrawRecord::getMonth, DateUtil.month(new Date())));
if (count >= drawPrize.getMonthMaxTimes()) {
//直接返回谢谢参与
return drawPrizes.stream()
.filter(d -> d.getDefaultPrize() == 1)
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("抽奖错误"));
}
}
return drawPrize;
}