【题目】
有一个机器按自然数顺序的方式吐出球(1号球、2号、3号…),你有一个袋子,袋子最多只能装下K个球,并且除此之外你没有更多的空间。设计一种选择方式,使得当机器吐出第N号球的时候(N > K),你袋子中球的个数是K个,同时保证从1号球到N号球中的每一个,被选进袋子的概率是K/N。
【基本思路】
这道题的核心解法就是蓄水池算法。过程如下:
1、将第1~k个球直接放入袋子
2、处理第i号球时(i > k),以k/i的概率决定是否将第i号球放入袋子中。如果不决定放入袋子,直接扔掉第i号球;否则,在袋子中随机选择一个扔掉,然后放入第i号球
3、重复步骤2直到 i == N
证明:
对于第i号球进袋子的分析分两部分:
1、当 1≤i≤k 时,那么在选第k+1号球时,第i号球留在袋子中的概率是1。
在选第k+1号球的时候,只有决定第k+1号球入袋,并且第i号球正好被随机选中的时候,第i号球才会被淘汰。所以第i号球被淘汰的概率是 p=kk+1∗1k ,所以第i号球留下来的概率就是 1−p=kk+1 。那么从第1号球到第K+1号球的过程中,第i号球最终留下来的概率是 kk+1
同理,在选第k+2号球的时候,第i号球被淘汰的概率是 p=kk+2∗1k ,所以第i号球留下来的概率就是 1−p=k+1k+2 。那么从第1号球到第K+2号球的过程中,第i号球最终留下来的概率是 kk+1∗k+1k+2 。
依次类推,在选第N号球的时候,从第1号球到第N号球的过程中,第i号球最终留下来的概率是 kk+1∗k+1k+2∗...N−1N=kN 。
2、当 k<i 时,那么在选第i号球时,第i号球留在袋子中的概率是k/i。
在选第i+1号球的时候,只有决定第i+1号球入袋,并且第i号球正好被随机选中的时候,第i号球才会被淘汰。所以第i号球被淘汰的概率是 p=ki+1∗1k=1i+1 ,所以第i号球留下来的概率就是 1−p=ii+1 。那么从第i号球被选中到第i+1号球的过程中,第i号球最终留下来的概率是 ki∗ii+1
同理,在选第i+2号球的时候,第i号球被淘汰的概率是 p=ki+2∗1k ,所以第i号球留下来的概率就是 1−p=i+1i+2 。那么从第i号球到第i+2号球的过程中,第i号球最终留下来的概率是 ki∗ii+1∗i+1i+2 。
依次类推,在选第N号球的时候,从第i号球到第N号球的过程中,第i号球最终留下来的概率是 ki∗ii+1∗...N−1N=kN 。
所以,按照该方法,当吐出球数为N时,每一个球被选进袋子的概率都是K/N。
【代码实现】
#python3.5
def getKNumRand(k, max):
def rand(max):
return int(random.random() * max) + 1
if k < 1 or max < 1:
return None
res = []
for i in range(k):
res.append(i+1)
for i in range(k+1, max+1):
if rand(i) <= k:
res[rand(k)-1] = i
return res