题目描述:一个文件中含有多个元素,只能遍历一遍,要求等概率随机取出其中之一。
先讲一个例子,5个人抽5个签,只有一个签意味着“中签”,轮流抽签,那么这种情况,每个人中签的概率分别是多大呢?
第一个人中签的概率是1/5,
第二个人中签的情况只能在第一个人未中时才有可能,所以他中的概率是4/5 * 1/4 = 1/5(4/5表示第一个人未中,1/4表示在剩下的4个签里中签的概率),所以,第二个人最终的中签概率也是1/5,
同理,第三个人中签的概率为:第一个人未中的概率X 第二个人未中的概率X第三个人中的概率,即为:4/5 * 3/4 * 1/3 = 1/5,
一样的可以求出第四和第五个人的概率都为1/5,也就是说先后顺序不影响公平性。
回到最开始的问题,一个文件中包含n个数据,而且n是不可知的,那么下面的思路就不太可行:随机选取1-n之间的任意一个数i = rand(1,n),然后返回文件中的第i个数。
此题思路如下:
顺序遍历文件,当前遍历的元素为第i个元素,picked表示之前选取了的某一个元素,此时生成一个随机数r=rand(),那么r%i == 0的概率是 。当r%i== 0的时候,将picked替换为当前值,否则扫描下一个元素直到文件结束。
伪代码如下:
ElementRandomPick(file)
int length = 1;
While( length <= file.size )
if( rand() % length == 0)
picked = File[length];
length++;
Return picked
上面这种策略,当遍历完所有的n个数之后,任取一数的概率都是相同的 ,
用归纳法证明:
在遍历第1个元素的时候,即length为1,那么rand() % length == 0的概率为1,所以,目前取第1个的概率为1。
遍历到第2个元素时,length=2,rand() % length == 0的概率为1/2 , 所以目前,取第2个数的概率为1/2 ,取第一个数的概率为1*(1-(1/2) )= 1/2。
遍历到第i个元素时,length=i, rand() % length== 0的概率为1/i , rand() % length != 0的概率为(i-1/i) ,所以目前,取第i个数的概率为1/i 。取第m个数(m<i)的概率是(1/m) *(m/m+1) *(m+1/m+2) *...*(1/i) = 1/i。
走到文件最后,每一个元素最终被选出的概率为 1/n 。
(http://blog.csdn.net/v_july_v/article/details/6712171)