从一个问题引出
如何随机从n个对象中(这n个对象是按序排列的,但是在此之前你是不知道n的值的)随机选择一个对象?
具体来说,如何在实现不知道文本文件行数的情况下读取该文件,从中随机选择并输出一行?
这是《编程珠玑》中的一个习题,如果我们知道n的值,那么问题就可以简单的用一个大随机数rand()%n得到一个确切的随机位置,那么该位置的对象就是所求的对象,选中的概率是1/n。
现在并不知道n的值,
我们总是选择第一个对象,以1/2的概率选择第二个,以1/3的概率选择第三个,以此类推,以1/m的概率选择第m个对象。当该过程结束时,每一个对象具有相同的选中概率,即1/n。
针对读取文本文件的例子,我们总选择第1行,并以概率1/2选择第2行,以1/3的概率选择第3行,
依此类推,在这一过程结束时,每一行的选中概率是相等的,都是1/n,其中n是文件的总行数。
给出伪代码:
1
2
3
4
5
|
i=
0
while
more input lines
with probaility
1
/++i
choice=
this
input line
print choice
|
这个问题可以抽象到更普遍的情况,就是蓄水池抽样问题。
蓄水池抽样问题
从n个元素中随机抽取k个元素,但n的个数无法事先确定。
在实际应用中,往往会遇到很大数据流的情况。因此,我们无法先保存整个数据流然后再从中选取,而是期望有一种将数据流遍历一遍就得到所选取的元素,并且保证得到的元素是随机的算法。
上面的问题就是k=1的的蓄水池抽样问题。
看一下解决蓄水池抽样问题的算法:
1.先选取n个元素中的前k个元素,保存在集合A中;
2.即从第k+1个元素开始,
每次先以k/(k+1)概率选择该元素,以k/(k+2)的概率选择第k+2个元素,
以此类推,以k/m的概率选择第j个对象(k+1<=m<=n)。
如果第m个元素被选中,则从A中随机选择一个元素并用元素m替换它;否则直接淘汰元素m;
最终每个对象被选中的概率均为k/n。
证明如下:
蓄水池抽样算法的典型应用
给定一个长度为N且没有重复元素的数组arr和一个整数M,实现函数等概率随机打印arr中的M个数。
首先在0~n-1中随机得到一个位置a,打印arr[a],然后把arr[a]和数组最后位置的arr[n-1]交换;
然后在0~n-2中随机得到一个位置b,然后把arr[b]和数组最后位置的arr[n-2]交换,
arr:{0,1,2,3,...,b,...,n-2},n-1,
依此类推,直到打印m个数即可。