题目:
微软电面问到的问题,前面提了一大堆背景,最后归结为一个问题,如何在连续的网络流量中,等概率随机抽取1个数据包。
分析:
参加电面时,还没看过蓄水池抽样问题,后来回来一搜,这就是典型的蓄水池抽样问题,不过当时自己也想到了一种解法,使用一个变量保存数据包,从1到i个数据包,每次遇到第i个数据包,以1/i的概率选取这个数据包,并且替换原来的数据包,这样到第n个数据包,我们能保证我们以1/n的概率选取这n个数据包中的1个。
证明:
加入第i个数据包到来,我们以1/i的概率选取这个数据包,显然,它的概率是符合的,那么第i-1个数据是不是也符合呢?我们假设第i-1次取数据包时,前面i-1个数据包以1/(i-1)的概率选取,那么第i-1个数据包被保留下来的概率是1-p(第i个数据包保存)再乘上p(1到i-1次选择被保存的概率),这里p(1到i-1次选择被保存的概率)就是假设的1/(i-1),那么第i次取包,前i-1个数据包被保存的概率是:
(1 - 1/i) * 1/(i - 1) = 1/i,说明前i-1个数据包的选取概率也是1/i;
由此进行归纳,可以得到最终结果。
扩展:
考虑蓄水池中抽取k个元素。
方法还是一样,假设前i次选择时,前i个数据包被选到水库的概率是k/i,那么第i+1次选择时,前面i个数据包保留在水库中的概率是
k/i * (1 - k/(i + 1) * 1/k) = k/(i + 1)
所以第i+1次选择,所有的数据包还是以k/(i + 1)的概率保存到水库中,得证。
伪代码如下:
Init : a reservoir with the size: k
for i= k+1 to N
M=random(1, i);
if( M < k)
SWAP the Mth value and ith value
end for
总结:
蓄水池问题非常经典,而且非常实用,需要理解其中原理。