随机抽样问题(蓄水池问题)

来源:互联网 发布:js正则表达式 i g 编辑:程序博客网 时间:2024/04/28 12:43

     本文整理了一些博客的思路:

                 http://www.cnblogs.com/HappyAngel/archive/2011/02/07/1949762.html

                            http://blog.csdn.net/fanzitao/article/details/7930886

问题定义

      我们常常碰到如下几个问题:

  (1)在不知道文件总行数的情况下,如何从文件中随机的抽取一行?

   上面的问题,我们并不知道文件有多大,如果知道了,我们就可以利用传统的rand()来生成一个随机数,然后选取相应的记录出来就行了。那么如果不知道文件的大小的时候,我们需要一个概念来帮助我们做出猜想,来使得对每一行取出的概率相等,也即随机。这个概念即蓄水池抽样(Reservoir Sampling)

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

  (3)给定一个未知长度的整数流,如何随机选取一个数;


蓄水池抽样问题


1.问题1和2的解答

  对于第(1)(2)个问题其实是一个问题。我们首先定义取出的行号为choice,第一次直接以第一行作为取出行 choice ,而后第二次以二分之一概率决定是否用第二行替换 choice ,第三次以三分之一的概率决定是否以第三行替换 choice ……,以此类推,可用伪代码描述如下:

i = 0while more input lines   with probability 1.0/++i //我们以(1/i)决定当前元素选不选       choice = this input lineprint choice
 
    问题:这种方法能保证n个元素的每个元素选取的概率为1/n,下面我们证明一下。

    证明:对于第i个元素被选中的条件,第i次选择的时候被选中了,同时从i+1到n的选择过程中,第i个元素一直没被替换。换句话说,i+1到n选择过程中,它们都没选中。

   (1)第i个元素被选中的概率:1/i,这里插一个第i个元素没被选中的概率(i-1)/i。

   (2)第i+1到n没有被选中的概率:

   (3)那么最后,第i个元素被选中的概率为多少呢?


   (4)得证。看不懂,可以参考:http://www.cnblogs.com/HappyAngel/archive/2011/02/07/1949762.html

/**copyright@lsj on juyuan*/#include <iostream>#include <cassert>#include <time.h>using namespace std;int RandSelectNum(int arr[],int len){assert(arr && len>0);    int randNum = arr[0];srand(unsigned(time(NULL)));for(int i=1; i<len; ++i){if(rand()%(i+1) == i)randNum = arr[i];}return randNum;}int main(){const int SIZE = 7;int arr[SIZE] = {1,2,3,4,5,6,7};cout<<RandSelectNum(arr,SIZE)<<endl;system("pause");return 0;}


2.问题3的解答

     类比前面(1)(2)的答案,我们对问题(3)可以给出如下解法:先申请一个k个元素的蓄水池(数组),首先把前k个数放入蓄水池。对第k+1,我们以k/(k+1)概率决定是否要把它换入蓄水池,换入时随机的选取一个作为替换项。这样一直做下去,对于任意的样本空间的第i个样本(i>=1000),对每个数的选取概率都为k/i。也就是说对每个数选取概率相等。下面给出伪代码:

Init : a reservoir with the size: kfor i= k+1 to N    M=random(1, i);//当i>k时,从1到i选择一个随机数,选择的随机数小于k,将第i个元素交换到前面去    if( M < k)     SWAP the Mth value and ith valueend for 

注意:这里是从1到i(i>1000)生成一个随机数rand,如果这个数满足小于k,说明第i个元素可以第k个元素交换,即执行swap(arr[rand],arr[i])。如果rand>1000,则第i个元素不放入蓄水池,即不交换。


     问题:这种方法能保证每个元素选取的概率为k/N,其中k表示随机去k个元素,N表示随机取N个元素。

    证明:跟上面一样,我们还是这样分析。对于第i个元素被选中的条件,第i次选择的时候被选中了,同时从i+1到n的选择过程中,第i个元素一直没被替换。换句话说,i+1到n选择过程中,它们都没选中。

   (1)对于第i个元素第i次被选择的概率为:k/i,第i个元素第i次没被选择的概率为(i-k)/i。

   (2)i个元素在最后被选中还要保证一个条件:从i+1到n的选择中,第i个元素没有被交换出去。这里没被交换出去有两种情况,我么考虑从i+1到n过程中的一个元素j

           情况1:第i+1到n的选择中,选择交换了前k个元素,但是没有选到第i个元素。这里我们考虑j个元素(i+1<=j<=n),此时的概率:

                                                                    

           情况2:第i+1到n的选择中,根本没有选中前面的k个元素之一,此时的概率为:

     总结上两种情况,第j个元素选不中j的概率为:

    (3)这样,第i个元素被最后选中的概率就可以想了:第i个元素在第i池选择中被选择进入蓄水池,而从i+1到n过程中都没有选中i,把i从蓄水池中交换出去,这样i就一直留在蓄水池中。

    (4)此时,已全部得证。看不懂,参考:http://blog.csdn.net/fanzitao/article/details/7930886
/**copyright@lsj on juyuan*/#include <iostream>#include <cassert>#include <time.h>using namespace std;void RandSelectKNum(int arr[],int len,int result[],int resLen){assert(arr && len>0 && result && resLen);    srand(unsigned(time(NULL)));int i = 0;for(i=0; i<resLen; ++i){//放k个元素进入蓄水池result[i] = arr[i];}for(i=resLen; i<len; i++){int randNum  = rand()%(i+1);//产生随机数同蓄水池里的数交换if(randNum < resLen)result[randNum] = arr[i];}}int main(){const int SIZE = 7;int arr[SIZE] = {1,2,3,4,5,6,7};const int SELECT = 3;    int result[SELECT];RandSelectKNum(arr,SIZE,result,SELECT);for(int i=0; i<SELECT; ++i){cout<<result[i]<<" ";}cout<<endl;system("pause");return 0;}

附一个概率相关题目的连接:http://www.cnblogs.com/waytofall/archive/2012/08/27/2658757.html



原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 酷狗音乐想退款怎么办 手机qq影音闪退怎么办 胃疼超过24小时怎么办 小米4s无法开机怎么办 微信语音不好听怎么办 微信语音太难听怎么办 苹果x耳机用不了怎么办 10个月幼儿便秘怎么办 太阳晒了长斑怎么办 膀胱癌复发5回了怎么办 右肾囊肿有1cm,怎么办, 牙侧面有小小洞怎么办 喝酒喝的手抖怎么办 孩子在幼儿园不爱说话怎么办 孕妇吃了方头鱼怎么办 油炸的东西软了怎么办 跑步小腿疼怎么办 知乎 健身后小腿粗了怎么办 背心式内衣大了怎么办 运动文胸买大了怎么办 喝碳酸饮料胖了怎么办 奥鹏学费迟交了怎么办 侧乌鸦做不起来怎么办 发烧很难受怎么办 知乎 上班后奶水越来越少怎么办 孕晚期胖的厉害怎么办 怀孕后猛长胖怎么办啊 怀孕坐了按摩椅怎么办 怀孕8个月胎位不正怎么办 缓刑期间被打了怎么办 非法集资人跑了怎么办 玩游戏恶心想吐怎么办 中奖了填了信息怎么办 穿开衫老是溜肩怎么办 开车开久了腰疼怎么办 西药吃了胃难受怎么办 铁海棠叶子变黄怎么办 四季海棠根烂了怎么办 水银弄到皮肤上怎么办 头条指数下降了怎么办 下面长泡泡破了怎么办