抽奖算法的实现

最近的一个业务需求是开发一个抽奖管理功能,要求在一个奖池中放一堆奖品,分别给它们设置不同的数量和概率,在奖品没有发完的情况下,概率高的被抽中的几率就大,反之则低。另外,概率为0的不能被抽中,概率为100则一定要被抽中。

这里只讨论下其中的核心算法的设计及一个示例函数,算法之外的系统控制暂不提及。

实现抽奖的方法应该有很多,没有仔细去考察和搜索那些非常复杂的算法,这里仅做了一个简单的假设,并在此基础上推出后面所有的控制逻辑。

1. 基本假设

假设系统生成一个1到100之间的随机整数R,C为1到100之间的一个任意整数,那么R小于C的概率为C/100。

暂且不去从数学上论证这个假设是否真的成立,我们仅从直观上来看,R是随机的,它的值不论是多少,取到1-100之间任意一个整数的概率都是一样的。

但C越小,R落入0-C之间的概率也会越小,所以我们大致上可以用C来控制某个奖品的概率。Good!

2. 实现逻辑

有了上面的假设,我们就可以考虑实现一个奖池内不同奖品配置情况下的控制逻辑。

首先我们把奖池中的奖品做一个过滤,剔除掉那些不满足条件的奖品,比如概率为0的、已经发完的等等。剩下的奖品都是可以被抽中的,只是概率大小不同而已。

OK,下面我们来设置一些概念,以方便后面的行文。

假设有N个奖品,它们都以100为满概率,那么它们总共的概率空间为O=N*100;
如果这N个奖品的概率分别为C1,C2,C3...Cn,那么他们总共的中奖概率空间就是H=C1+C2+C3+...+Cn,因为Cn总是小于等于100,所以
H总是小于等于O。

我们把以上这些参数在后台配置好,当抽奖行为发生时,我们让系统生成一个随机数R,1<=R<=O,那么当R<=C时,我们就认为中奖了,否则就不中奖。Good10!

在判断出是否中奖后,我们就可以进一步判断中了什么奖。
首先把奖品以数组形式A按概率从小到大进行排序,然后求出每个奖品在总中奖概率空间H中的中奖区间,并且把各区间的最大值保存成一个数组D。

例如有a和b两个奖品,概率分别为20和30,那么a的概率空间为20,中奖区间为1-20;而b的概率空间为30,但它的中奖区间是20-50,这样D就是(1,20,50)。

然后我们再把D从小到大排序并循环,当R小于20时,我们认为a被抽中;R小于50时,认为b被抽中。Good11!

这里有个问题,就是为什么不直接把R跟a和b的概率比较,而要比较它们各自中间区间的最大值?

因为我们设想的情况是,不仅某种奖品概率调大时其抽中的几率增大,而且所有奖品的概率都调大时,它们被抽中的几率都增大。

如果直接把R跟奖品各自的概率比较,根据我们上面的逻辑,它们总的中奖空间H=2×100=200,只要R的值小于200,我们就已经认为中奖了;但是当a和b两种奖品的概率为99时,只有当R小于100时它们才会被抽中,R落在100到200之间将不被认为中奖,这显然是不对的。

搞清楚了上面的逻辑,剩下的就是处理一些特殊情况了。
比如,如果某些奖品的概率为100,这就是我们之前说的存在满概率奖品。按我们的设想,当有百分百中奖的奖品时,我们一定要这种奖品被抽中。

处理这个问题,我的方法是把奖品按概率分成两组,一组是满概率奖品,一组是非满概率奖品。当满概率奖品组不为空时,从中随机取出一个作为被抽中的奖品放出,直到这些奖品被抽完。

到此为止整个逻辑基本结束,可以着手写代码了。Good101!



作者:codemarker
链接:http://www.jianshu.com/p/1d0316eaa35a
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,让我来回答你的问题。 根据概率、剩余奖品数量动态抽奖算法实现概率总和可以不为100%。这种算法通常被称为概率加权抽样(Probability Weighted Sampling)。 下面是一个简单的Java实现示例: ```java import java.util.*; public class ProbabilityWeightedSampling { public static void main(String[] args) { // 奖品列表,每项奖品包括名称、概率、数量 List<Prize> prizes = new ArrayList<>(); prizes.add(new Prize("奖品A", 0.5, 10)); prizes.add(new Prize("奖品B", 0.3, 5)); prizes.add(new Prize("奖品C", 0.2, 3)); // 抽奖 Prize prize = draw(prizes); System.out.println("恭喜您获得了" + prize.name); } // 抽奖方法 public static Prize draw(List<Prize> prizes) { double totalProbability = 0; for (Prize prize : prizes) { totalProbability += prize.probability * prize.quantity; } double random = Math.random() * totalProbability; double currentProbability = 0; for (Prize prize : prizes) { currentProbability += prize.probability * prize.quantity; if (random < currentProbability) { prize.quantity--; return prize; } } return null; } // 奖品类 static class Prize { String name; // 奖品名称 double probability; // 中奖概率 int quantity; // 剩余数量 public Prize(String name, double probability, int quantity) { this.name = name; this.probability = probability; this.quantity = quantity; } } } ``` 在这个示例中,我们定义了一个奖品列表,每项奖品包括名称、概率、数量。抽奖时,我们遍历奖品列表,计算出每项奖品的加权概率,然后根据随机数在加权概率上进行抽奖。抽中奖品后,我们将该奖品的数量减1,防止重复中奖。 需要注意的是,由于概率总和不一定为100%,因此我们需要将概率乘以奖品数量,得到每项奖品的加权概率
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值