点名软件—使用二项分布实现点名均匀分布

需求

最近帮老师制作一个点名软件,需求是这样的:

  • 1.每两个或一个人组队(除非出现总人数为奇数的情况,否则不会单人组队,也就是说最多一个单人队),作为小组研究一个课题
  • 2.每次上课让一个人讲ppt,另一个人回答问题,选择是随机的
  • 3.从下面不上台的人中选出三个来提问,选择也是随机的

预期

  • 1.必须保证上台的人不会出现在提问的三个人之中
  • 2.当所有组上台之后,应该避免出现有人没回答过问题
  • 3.一个人不应该回答过多问题

解决方案

方案一:轮数控制

实现方式

首先在全是双人组队的情况下,点名次数的数学期望应该是1.5次,即使有单人组队也应该差距不大(。。样本不会太小,毕竟至少一个班)。 轮数控制的思想就是,我先将所有人都点一遍之后再点第二遍,每次点名的时候都会检测是否进入了第二轮。根据这个简单的算法,我们应该在第二轮没有完全结束的时候,所有小组的汇报就结束了。

优点

  • 1.确实保证了每人的回答次数,每个人至少都能回答一次,也最多两次
  • 2.设计简单,每一轮中将没有点到的人放入一个集合中直接进行随机抽取即可,开始前先判断当前轮数

缺点

这个算法。。其实也不能称之为算法了,就是这个思想的弊端在于,一个人当前轮被点中之后应该是不会再被点了,这个点名软件的提神效果对其就失效了(手动滑稽)。这个弊端很严重。

方案二:抽屉原理与概率控制

实现方式v1.0

我想保证每个人的回答次数都至少为一次,根据抽屉原理,以33组为例,有66人,就会有99次点名机会,那么分配完66次点名机会保证每人一次,还有33次点名机会,这里我们就可以选择让点名两次的人不会再次被点中,于是最后的分布应该是这样的:33个人被点了一次,33个人被点了两次。
想到这里,我们是不是就可以这样设计了呢?

  • 1.所有可以被点的人都是随机抽取的
  • 2.被点了两次的无法被再次中
  • 3.当被点两次的人数达到33时,被点一次的人就无法被点中

可能存在的BUG

想想上面确实是一个不错的算法,用抽屉原理直接变成了一个简单的数学问题,而且确实达到了分布均匀并且每个人都能回答问题的要求。但是这个算法确实存在一个比较致命的bug,也是完全随机抽取带来的弊端。当最后一组存在一个人回答问题的数量为时,这个算法就直接出现了BUG,由于上台的人无法被选中,这就导致了没有人能够被点中!!虽然从概率来说,这种情况的概率不大,但是可以说是不可忽视的,因为一旦出现就会导致系统崩溃。我们应该要找到一个更完善的算法。

实现方式v2.0

于是我就想到了,可以使用概率论的方法,使得P(33人被点一次,33人被点两次) >= 0.99,这样我们就可以认为几乎可以做到均匀分布了。于是我可以这样限制:

  • 1.回答次数为0的优先级应该明显高于回答次数为1的,即P(回答0次被抽中) 远大于 P(回答1次被抽中)
  • 2.回答次数达到2的直接取消回答的可能性,即P(回答2次被抽中) = 0

设计思路:

  • 1.设抽中0次的权重 W(回答0次被抽中) = α
  • 2.设抽中1次的权重 W(回答1次被抽中) = λα
  • 3.设抽中2次的权重 W(回答2次被抽中) = 0

解释和想法

这里我就设计W(抽中1次后被抽中)是W(抽中0次后被抽中)的λ倍。根据常识我们就可以预测到,只要λ取得足够小,那最后的结果也会不断接近我们的预期,现在问题的关键是取多少。这不是一个简单的概率问题,因为随着点名次数的增加,概率是不断变化的,简单的概率计算并不能解决问题(还不是概率论学的菜。。)。但是可以肯定的是,如果我将λ取的非常小,结果是可以符合预期的。。想出这个想法的原因也是因为这样的话对于编程来说,实现思路很简单。。我现在就恨不得将λ取成一万分之一 !!

实现方式v3.0

第一个方法过于随机存在缺陷,第二个方式可以实现,但是从数学上难以证明λ的取值。因此我在想能不能换一种思路实现第二种算法,这时我就想到了最简单的二项分布(0-1分布) 。可以设一个λ范围为0-1,做99次0到1的随机取值α,小于λ就从回答0次的人中点,大于λ就从回答1次的人中点,计算α最后小于λ的次数大于66的概率,即保证最后能够所有人都被点到过。

如果最后出现第一种的极端情况,即最后那个人没回答过问题,也不会出现逻辑上的BUG。我们现在要做的就是取一个λ,使得99次点名之后,66个人都至少点过一次,我们要保证这个概率很大,至少大于等于0.99吧。。

数学问题化

回答0次被抽中的可能性为p,回答1次被抽中的概率为1-p。随机变量X表示回答0次被抽中,取值为0到99,满足二项分布 。要求随机变量X大于等于66的概率足够大。
下面由等式与不等式来表示:

  • P(回答0次被抽中) = p;
  • P(回答1次被抽中) = 1- p;
  • P(X>=66) >= 0.99;

由题意可知,我们的期望E(X)>=66,所以设P(X=k)为最大值,k的取值必定是大于66的,那P(X=0)到P(X=65)的值应该是递增的,我们可以对等式进行缩放:

P(X≥66)=1-P(X≤65)
≥1-65P(X=65)-P(X=0)
≥0.99

公式

这里我将p=9/10代入满足概率大于0.99

因此,取λ为0.9,完成上述算法即可完成最终设计。

随便扯扯

没怎么接触过算法,就把这个问题用数学问题解决了一下,不知道各路大神有没有更牛逼的方案让我这个菜鸟学习一下。。
项目github地址

猜你喜欢

转载自blog.csdn.net/blueblueskyz/article/details/78305681