您的位置:首页 > 其它

随机算法之蓄水池抽样问题

2014-09-30 01:07 204 查看
蓄水池抽样问题是从动态变化的N个元素中随机抽选出M个元素(N>=M)

算法描述如下:
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


由于N的个数是不确定的,这就意味着不论N的个数是多少,里面元素都要被等概率抽取,例如,先从10个元素中抽取2个出来,现在又往里面添加了10个元素,变成了从20个元素中抽取两个出来,如何保证这次变化后每个元素被抽取的概率还是一样的呢?

问题,证明对于任意样本号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)。证毕。

面试题:

给你一个长度为N的链表。N很大,但你不知道N有多大。你的任务是从这N个元素中随机取出k个元素。你只能遍历这个链表一次。你的算法必须保证取出的元素恰好有k个,且它们是完全随机的(出现概率均等)。

此问题有个限制点:

1.元素个数不可知

2.只能遍历一边

解题思路如下:

1.遍历链表到第k个元素,构造一个数组A[K]存储这k个元素

2.从k+1开始遍历,与前面算法一样
for    i= k+1 to N
M=random(1, i);
if( M < k)
SWAP the Mth value and ith value in array A
end for


3.最后得到的数组A[k]就是想要的。

在网上流传的这个算法有问题,wiki 上面的描述如下:
array R[k];    // result
integer i, j;
// fill the reservoir array
for each i in 1 to k do
R[i] := S[i]
done;
// replace elements with gradually decreasing probability
for each i in k+1 to length(S) do
j := random(1, i);   // important: inclusive range
if j <= k then
R[j] := S[i]
fi
done


(ps:注意第10行的 j <= k)

我个人倾向于有等号的算法,欢迎各位朋友讨论,指导。

参考链接:

http://hi.baidu.com/cpuramdisk/item/260611ca0082bcd796445239
http://en.wikipedia.org/wiki/Reservoir_sampling
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: