如何随机选取1000个关键字

来源:互联网 发布:网络图片大全带文字 编辑:程序博客网 时间:2024/04/20 08:13

给定一个数据流,其中包含无穷尽的搜索关键字(比如,人们在谷歌搜索时不断输入的关键字)。如何才能从这个无穷尽的流中随机的选取1000个关键字?

解决方案就是蓄水库抽样(reservoid sampling)。主要思想就是保持一个集合(这个集合中的每个数字出现),作为蓄水池,依次遍历所有数据的时候以一定概率替换这个蓄水池中的数字。

其伪代码如下:

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

解释一下:程序的开始就是把前k个元素都放到水库中,然后对之后的第i个元素,以k/i的概率替换掉这个水库中的某一个元素。



定义长度为1000的数组。

对于数据流中的前1000个关键字,显然都要放到数组中。

对于数据流中的的第n(n>1000)个关键字,我们知道这个关键字被随机选中的概率为 1000/n。所以我们以 1000/n 的概率用这个关键字去替换数组中的随机一个。这样就可以保证所有关键字都以 1000/n的概率被选中。

对于后面的关键字都进行这样的处理,这样我们就可以保证数组中总是保存着1000个随机关键字。


另外一种分析(与第一个算法相同):

随机取样问题可以分为:

1、 确定数目元素的随机取样,可以表述为:从N个元素中获得K个相异且随机的元素。

2、 不确定数目元素的随机取样,可以表述为:从一个数据流中获得k个相异且随机的元素。

其实第二个问题包含着第一个问题,我们只要探讨第二个问题即可,我直接给出下面的算法吧,首先要注意取样问题的描述:“k个”“相异”“随机”,代码如下:

T* random_sample(stream s,intk)

{

     T* rand_list=newT[k];

     inti=k;

     s.open();

     /*store first k elements in stream*/

     while(k-->=0)

           rand_list[i]=s.next();

     i=k;

     while(!s.end())

     {

           i++;

           intidx=rand(0, i-1);//create random number [0, i-1]

           if(idx<=k-1)//[0,k-1]

                rand_list[idx]=s.next();

     }

}

下面对其正确性进行证明,正确性应该保证“k个”“相异”“随机”,前两个条件显然成立,开始状态前两个状态就成立,函数执行过程中,每次替换的元素都不相同(我们认为在stream中不同位置的元素不同),对于“随机”的证明可以使用数学归纳法进行:

待证明问题,对于任意n>=k,经过上述方法后,得到的结果中任何一个元素出现的概率均为k/n。

1、当n=k时,rand_list[]中存储前k个元素,p=k/n=k/k=1成立。

2、假设当n=i时,命题成立:i个元素出现在rand_list[]中的概率为k/i。

当n=i+1时,执行上述算法,我们只关注最后一步:对于第i+1个元素的操作。

对第i+1个元素执行上述算法之前,对于前i个元素中任意一个元素,它们在rand_list[]中出现的概率为k/i,我们以k/(i+1)的概率去替换rand_list[]中的元素,前i个元素中任意一个元素被替换的概率为:

       k(1/i)* ( 1/(i+1))

前i个元素中任意一个元素最终被取到的概率:

       k/i - k(1/i)* ( 1/(i+1)) = k/(i+1)

对于第i+1个元素,它被取到的概率可以直接计算得到:

       P(i+1)=k/(i+1)

所以当n=i+1时,上述命题也成立。

下面给两个相关的题目:

1、给定一个未知长度的整数流,如何随机选取一个数

2、给定一个数据流,其中包含无穷尽的搜索关键字(比如,人们在谷歌搜索时不断输入的关键字)。如何才能从这个无穷尽的流中随机的选取1000个关键字?