您的位置:首页 > 编程语言

取样问题 总数n事先不知道,等概率取样 (编程珠玑chapter12 课后题10)

2015-07-11 20:31 363 查看
摘录三篇主题相关博文

如何在事先不知道文本文件行数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个数中仍然以等概率抽取其中的一个。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: