摘录三篇主题相关博文
如何在事先不知道文本文件行数n的情况下读取该文件,从中随机选择并输出一行?
(事先不知道n的大小,但是一次可以看到这n个对象)
即蓄水池抽样(Reservoir Sampling)问题
证明如下:
问题: 证明当前任意一行为取出行的概率为1/i,i为当前扫描到的行号,也即每一行取出的概率相等
我们用数学归纳法来证明,
当i=1时,当前只浏览了第一行,因此第一行为取出行的概率为1/1=1,符合直接取出的条件
当i=k时,有前k行为取出行的概率为1/k,我们要证明的是,当i=k+1时,前k+1行每一行被取出的概率均相等,且为1/(k+1)。当扫描到第k+1行时,我们以1/(k+1)概率替换choice,易知,第k+1行为choice的概率即为1/(k+1),对于第k行,其为choice的概率是 第k行为取出行的概率 * 第k+1行没有被取出的概率即,
对于第k行的证明同样可应用到前k-1行,对于其中第m行其为choice的概率是 第m行为取出行的概率 * 第m+1行没有被取出的概率 * … *第k+1行没有被取出的概率,即
由此证得当i=k+1时,所有行的取出概率为1/(k+1)。证毕。
可以对其进行扩展,即如何从未知或者很大样本空间随机地取k个数?
类比下即可得到答案,即先把前k个数放入蓄水池,对第k+1,我们以k/(k+1)概率决定是否要把它换入蓄水池,换入时随机的选取一个作为替换项,这样一直做下去,对于任意的样本空间n,对每个数的选取概率都为k/n。也就是说对每个数选取概率相等。
证明我们仍然使用数学归纳法:
问题,证明对于任意样本号n,n>=k,每个样本作为取出样本的概率相等,即k/n。
证明:
当n=k时,由我们把前k个数放入蓄水池可知,每个样本的取出概率均相等,即k/k=1。 设当前样本号为n,其每个取出样本概率均相等,即为k/n,我们要证明的是这种情况对于n+1也成立。
由于我们以k/(n+1)决定是否把n+1放入蓄水池,那么对于n+1其出现在蓄水池中的概率就是k/(n+1),对于前n个元素中的任意元素m(k+1<=m<=n),其出现在蓄水池中的概率为 m出现在蓄水池中的概率 * [(m+1被选中的概率*m没被m+1替换的概率 + m+1没被选中的概率)*(m+2被选中的概率*m没被m+2替换的概率 + m+2没被选中的概率)*…*(n+1被选中的概率*m没被n+1替换的概率 + n+1没被选中的概率)],即
可见,对于n+1每个样本取出概率也相等,即为k/(n+1)。证毕。
from:
http://hi.baidu.com/jrckkyy/blog/item/2562320a70edee27b0351d56.html
/**************************************************************************************************/
首先,这个问题来自于一道面试题。
原题目的场景大体是这样的:
服务器每天会收到数以亿计的请求,但是目前服务器端不希望保存所有的请求,只想随机保存这些请求中的m个。试设计一种算法,能够使服务器实时保存m个请求,并使这些请求是从所有请求中的大致等概率被选中的结果。注意:不到一天的结束,是不能提前知道当天所有请求数n是多少的。
一、问题简化
有一个整数序列生成器,以一定时间间隔生成一个新的整数,一天之内会生成N个,希望实时保存m个整数,使得任何时刻这m个整数都是当前已生成的所有整数数量n中等概率抽取的结果,即概率均为m/n。
二、思路与解法
如果此题简化成可以存下整个的序列,即全部N个数,则变得非常简单了,最后直接随机选出m个就搞定了。但是现在只能最多保存m个,同时要实时保证概率相等的要求,因此在新来一个请求的时候,需要以某种特殊的方式进行判决保存还是不保存,以使得满足概率要求。
具体方法如下:
- 对于前m个请求直接保存到服务器上,对应整数序列相当于,整数数组的前m个直接存下来。
- 用一个计数器保存当前正在处理的请求是第几个,比如n
- 对于从m+1开始的新请求,以m/n的概率选择保存,并同从已保存的m个请求中随机选出的一个进行交换。
细说就是,
- 对于第m+1个请求,以m/(m+1)的概率选择留下,如果留下了则从已保存的m个请求中随机选出一个,同它交换;
- 对于第m+2个请求,以m/(m+2)的概率选择留下,如果留下了则从已保存的m个请求中随机选出一个,同它交换;
- 对于第m+3个请求,以m/(m+3)的概率选择留下,如果留下了则从已保存的m个请求中随机选出一个,同它交换;
…
- 对于第n个请求,以m/n的概率选择留下,如果留下了则从已保存的m个请求中随机选出一个,同它交换;
三、等概率正确性证明(使用数学归纳法进行)
- 当n=m+1时,
对于第m+1个请求以概率m/(m+1)选择留下,显然满足m/N的要求;
对于前m个请求中的任何一个,能被选择留下有两种情况:a.第m+1个请求被选择留下了并且没有和自己进行交换;b.第m+1个请求没有被选择留下来而自己确实已被选择留下来了。
所以,概率计算为 m/(m+1) * (m-1)/m + (1 – m/(m+1)) * 1 = (m-1)/(m+1) + 1/(m+1) = m/(m+1) - 假设当n=N时,仍然正确,即任何一个请求被选中的概率都是m/N,现在推到证明当n=N+1时,任何一个请求被选中留下的概率是m/(N+1)。
对于第N+1个请求,因为是以m/n=m/(N+1)的概率选中的,所以显然满足要求;
对于前m个请求中任何一个,能被选中留下同样分为同上的两种情况:- 一种是第N+1个被选中了但随机抽取出与它交换的不是自己;
- 另一种情况是自己已留下并且第N+1个未被选中留下。
和概率为: [ m/(N+1) * (m-1)/m + (1-m/(N+1)) ]* m/N = [ (m-1)/(N+1) + (N+1-m)/(N+1) ] * m/N = m/(N+1)。
即当n=N+1时,仍然正确。
综合1)、2)可知,此方法满足等概率要求。
/**************************************************************************************************/
编程珠玑12章后的第10题:
如何在事先不知道文本文件行数的情况下读取该文件,从中随机选择并输出一行?
解法:首先以概率1选择第一行,如果有更多行,比如第i行,我们以1/i的概率选择第i行,最后输出我们选择的行
伪代码:
-----------------------------------------------
-----------------------------------------------
p = 0
i = 0
while more input lines
with the conditional p = 1.0/++i select this line
choice = the i-th line
end while
print choice
-----------------------------------------------
-----------------------------------------------
最后输出结果,其中p = 1.0 / i++ 可以通过rand() % ++i < 1得到,如果满足条件,我们选择第i行。
【转】
扩展:原问题可简化为:如何从n个有序对象中等概率地任意抽取1个,简记为sample(n,1),其中n未知;
若将该问题改为:如何从n个有序对象中等概率地任意抽取m个,简记为sample(n,m),其中n未知;
分析:若n已知,sample(n,m)是普通的抽样问题;当n未知时,可否根据上述算法进行相应的转化求解?
解决方案:将sample(n,m)问题转化为m个sample(n*,1)问题,更具体一点是,转化为sample(n,1);sample(n-1,1);sample(n-2,1)....;sample(n-m+1,1)问题。仍然以一篇6行文档为例,任取其中2行,做法如下:第一遍,以如下概率选中一行:1(1) 2(1/2) 3(1/3) 4(1/4) 5(1/5) 6(1/6)假设选中第2行,接着概率修改如下:3(1) 4(1/2) 5(1/3) 6(1/4) 1(1/5)
【说明】:当选中第2行,从第3行开始修改概率,并将第2行排除在外,继续扫描,这样能保证在剩下的5个数中仍然以等概率抽取其中的一个。