蓄水池采样算法(例子+证明+代码)

问题描述:
有一个不确定规模的字符流,需要采取k个字符,保证字符流中每个字符的采样概率一样,怎么做?

例子:

  1. 假设数据流只有一个数据,接受数据,发现数据流结束了,则直接返回该数据,该数据返回的概率为1。
  2. 假设数据流有两个数据,读取了第一个数据,数据流没有结束,又读取第二个数据,发现数据流结束了,因此若保证第一个和第二个数据输出概率一样,则可以生成一个0-1的随机数R,如果R<=0.5,则返回第一个数据,如果R>0.5,则返回第二个数据。
  3. 假设数据流有三个数据A,B,C,和前面一样要求只输出一个数。现陆续接收到数据A和B,发现数据流没有结束,则必须淘汰一个数据,A和B均以1/2的概率被淘汰,假设淘汰了A,现接受到C,发现数据流结束了,那么可以分析得到,长度为3的数据流每个数据被输出的概率为1/3才能保证正确性,以1/3的概率留下C,以2/3的概率留下B,那么这三个数据被留下并输出的概率分别为:
    数据A被留下的概率=(1/2)(2/3)=1/3
    数据B被留下的概率=(1/2)
    (2/3)=1/3
    数据C被留下的概率=1/3

总结一下结论:
假设要采用的数量为K,首先构造一个可以容纳K个元素的数组,将序列前K个元素直接放入数组中,然后对于第K+1及以后的元素(假设为第j个)以K/j的概率决定其是否被替换到数组中(保留下来),当遍历完所有元素之后,数组中剩下的元素即采样的样本。

证明:
对于第i个数,i<=k,在K步前直接被选中入数组,保留下来的概率为1,第K+1步,其被替换的概率=(第K+1个样本被选中)*(选择数组中的i替换)=k/(k+1) * 1/k = 1/(k+1),则其不被K+1替换的概率= 1-1/(K+1)=K/(K+1)。以此类推,不被第K+2个元素替换的概率=1-K/(K+2) * 1/K = (K+1)/(K+2),运行到第n步,保留下来的概率 = 被选中的概率 * 不被替换的概率 = 1 * K/(K+1) * (K+1)/(K+2) * ... * (n-1)/n = k/n.
第j个数,j>k,在第j步被选择的概率为 K/j,不被j+1替换的概率 = 1-K/(j+1) * 1/K = j/(j+1),运行到第n步,被保留的概率 = 被选中的概率 * 不被替换的概率 = K/j * j/(j+1) * (j+1)/(j+2) * ... * (n-1)/n = K/n
综上,每一个元素,被保留下来的概率都为 K/n

代码:

import random
class ResevoirSample:
    def __init__(self,size):
    ''' 选择size个样本到sample[]数组中'''
        self.size = size
        self.counter = 0
        self.sample = []

    def sampling(self,item):
        self.counter += 1
        # 前k个数直接入数组
        if len(self.sample)<self.size:
            self.sample.append(item)
            return self.sample
        # i个元素(i>k)开始概率采样
        rand = random.randint(1,self.counter)
        if rand<=self.size:
            self.sample[rand-1] = item
        return self.sample
全部评论

相关推荐

感觉这一周太梦幻了,就像一个梦,很不真实~~~感觉这个暑期,我的运气占了99成,实力只有百分之一4.15上午&nbsp;腾讯csig&nbsp;腾讯云部门,面完秒进入复试状态4.16下午&nbsp;美团优选供应链部门,4.18上午发二面4.17晚上&nbsp;阿里国际一面,纯拷打,面完我都玉玉了4.18下午&nbsp;阿里国际二面,是我们leader面的我,很轻松~~4.18晚上&nbsp;约了hr面4.19上午&nbsp;hr面,下午两点口头oc4.19晚上&nbsp;意向书说起来我的暑期好像一次都没挂过~~~~~难道我是天生面试圣体?----------------------------------------------------------------------六个月前,我还是0项目0刷题,当时想的是先把论文发出来再去找实习。结果一次组会,老师打破了我的幻想(不让投B会,只让投刊或者A)我拿头投啊!!!然后就开始物色着找实习,顺便做完了mit的6.s081,但是基本上还是没刷过题目-----------------------------------------------------------------------11月&nbsp;&nbsp;一次偶然的机会,面进了某个耳机厂的手环部门,大概是做嵌入式的,用的是CPP。12月&nbsp;莫名其妙拿到了国创的面试机会,0基础四天速成java基础!居然也给我面过了hhhhh,可能是面试没写题吧入职国创后的几个月,一直没活,天天搁那看剧,都快忘了还有暑期实习这回事了~~~~命运的齿轮在2.26开始转动,因为这一天美团开了,我开始慌了,因为那时的我什么都不会。lc,八股,sql全部是0进度。然后就开始了女娲补天,上班刷题,下班继续做之前的开源,顺便学一学八股。3月到现在,lc也刷到快200了,一天最多提交了47次~~~~~~~~~~八股根据别人的面经总结和博客,写了快十万字的笔记~~~~~~~~~~简历上的实习经历和开源,也努力去深挖了,写了几万字的记录~~~~~~所以面试的时候,基本上都能cover了,面试官问到的基础基本都会,不基础的我就把他往我会的地方引。结果好像还不错,基本上每个面试官评价都挺好的emmmmmmmm
投递阿里巴巴等公司10个岗位
点赞 评论 收藏
转发
投递美团等公司10个岗位
点赞 评论 收藏
转发
点赞 1 评论
分享
牛客网
牛客企业服务