目前公司各个项目中,有一些奖品抽取的实现逻辑,不尽想同。下面的进行列举,并进行主观点评:
1、idGreedy
image.png
image.png
抽取的思路为,
1)按不同的模式(普通、大奖、低概率(21年10月已废弃)),获取对应的一套所有物品的抽取概率配置(物品概率,配置为整数,作为分子。所有物品的整数之和作为分母)
2)获取[0, 整数之和)的随机数,落到的区间对应的物品,为抽取到的物品
优点:真实地按概率进行抽取,每局有能力完全独立进行抽取(因为有些防亏损逻辑,导致不能独立)
不足与风险:
纯使用概率不能满足业务,常常会补充各种辅助逻辑,使得最终的的逻辑难以理解:1)不同模式都有各自的开启条件,2)不同模式下,有各自判断达到亏损上限,进行废弃抽取结果,排除部分亏损物品,重新抽取的逻辑
大奖物品与非大奖物品并列,且都需要配置每种模式的概率(21年8月的事故,主要原因就是为大奖物品配置低概率模式概率,低概率模式本不应该抽到大奖物品,也不会对大奖物品进行防亏损处理)
2、idLucyGift
image.png
抽取的思路为
1)分配不同的抽取策略:概率抽取(与idGreedy类似)、步长抽取
2)按奖品的配置次序,判断当前次数是否整除“步长”,整除时作为抽取
优点:严格控制奖品出现次数,与抽取次数的比例。
不足与风险:
奖品出现的间隔固定,容易被用户发现这种规律;
达到不同奖品步长的公倍数时,只能发出一个高优先的奖品(步长不是最终的频次,受其它高优先奖品的步长影响)
不同策略的配置参数,混用json对象,字段一部分有效,另一部分无效。
3、欢乐扭蛋机,轮次奖池 打乱+不放回抽取
image.pngimage.png
抽取的思路为
1)将礼物id,添加到一个数据切片(每个id的重复次数为其 在轮次奖池中的数量)
2)打乱轮次奖池(util.ShuffleString(gifts))(对个别特殊奖品出现的次序,有一定的干预)
3)提前将50轮的已打乱轮次发放列表,存入redis
4)抽取时,使用lua脚本,指定发放列表找出命中的奖品
优点:
在每个轮次内,每种奖品有固定的数量,同时进行了打乱;
部分抽取逻辑交由redis执行,避免的分布布锁的使用,并发能力也有了保障。
不足与风险:
lua脚本书写抽取逻辑,开发、调试难度过大;
发放列表提前生成,无法做到进行中,进行即时变更配置。
4、国际化抽奖礼物,轮次奖池 打乱+不放回抽取
image.png
同样实现 轮次奖池 打乱+不放回抽取 ,避免使用lua脚本,配置变更时在新开启的轮次中生效(这个功能独立,可删掉)
抽取的思路为
1)使用redis incr操作,可领取到一个抽取的序号
2)使用伪随机算法,为抽取序号映射出一个奖池中奖品序号
3)使用redis将进行中的轮次配置,维护为一个队列。针对指定轮次,最早写入队列的配置,就是最终要使用的。(并发时,多个容器可能有不同的配置 同时进行写入)
优点:
避免使用lua脚本;
最少使用redis的操作,进一步提高了并发能力;
配置变更时在新开启的轮次中生效;
不足与风险:
修改配置时,轮次的奖池大小要保持不变。变化时,伪随机算法会完全错乱。
网友评论