蓄水池采样算法

版权声明:本文为博主原创文章,未经博主允许不得转载。转载请注明作者和出处:https://blog.csdn.net/weixin_41481113 https://blog.csdn.net/weixin_41481113/article/details/83449009

目录

问题描述分析

算法过程

证明过程

证法1:

 证法2:

代码示例


问题描述分析

采样问题经常会被遇到,比如:

  1. 从 100000 份调查报告中抽取 1000 份进行统计。
  2. 从一本很厚的电话簿中抽取 1000 人进行姓氏统计。
  3. 从 Google 搜索 "Ken Thompson",从中抽取 100 个结果查看哪些是今年的。

这些都是很基本的采用问题。

既然说到采样问题,最重要的就是做到公平,也就是保证每个元素被采样到的概率是相同的。所以可以想到要想实现这样的算法,就需要掷骰子,也就是随机数算法。(这里就不具体讨论随机数算法了,假定我们有了一套很成熟的随机数算法了)

对于第一个问题,还是比较简单,通过算法生成 [0,100000−1)[0,100000−1) 间的随机数 1000 个,并且保证不重复即可。再取出对应的元素即可。

扫描二维码关注公众号,回复: 3917284 查看本文章

但是对于第二和第三个问题,就有些不同了,我们不知道数据的整体规模有多大。可能有人会想到,我可以先对数据进行一次遍历,计算出数据的数量 N,然后再按照上述的方法进行采样即可。这当然可以,但是并不好,毕竟这可能需要花上很多时间。也可以尝试估算数据的规模,但是这样得到的采样数据分布可能并不平均。

算法过程

终于要讲到蓄水池采样算法(Reservoir Sampling)了。先说一下算法的过程:

假设数据序列的规模为 n,需要采样的数量的为 k。

首先构建一个可容纳k 个元素的数组,将序列的前 k 个元素放入数组中。

然后从第 k+1 个元素开始,以 k/n 的概率来决定该元素最后是否被留在数组中(每进来来一个新的元素,数组中的每个旧元素被替换的概率是相同的)。 当遍历完所有元素之后,数组中剩下的元素即为所需采取的样本。

证明过程

证法1:

对于数组中第i个数据(i≤k)。在 k 步之前,被选中的概率为 1。当走到第 k+1 步时,被第 k+1 个数据替换的概率 = 第k+1个元素被选中的概率 * 第i个数 被选中替换的概率,即为 \frac{k}{k+1}\times \frac{1}{k}=\frac{1}{k+1}。则被保留的概率为 1-\frac{k}{k+1}\times \frac{1}{k}=\frac{k}{k+1}。依次类推,在不被第k+1个元素替换的前提下,不被第k+2 个数据替换的条件概率1-\frac{k}{k+2}\times \frac{1}{k}=\frac{k+1}{k+2}。则运行到第 n 步时,被保留的概率 = 被选中的概率 * 不被替换的概率,即(条件概率的连乘):

1\times \frac{k}{k+1}\times \frac{k+1}{k+2} \times \frac{k+2}{k+3} \times ...\times \frac{n-1}{n}=\frac{k}{n}

对于第j个数据(j>k)。第 j个数据被选中的概率为k/j。不被 第j+1 个元素替换的概率为 1-\frac{k}{j+1}\times \frac{1}{k}=\frac{j}{j+1}。则运行到第 nn步时,被保留的概率 = 被选中的概率 * 不被替换的概率,即条件概率的连乘):

\frac{k}{j}\times \frac{j}{j+1}\times \frac{j+1}{j+2} \times \frac{j+2}{j+3} \times ...\times \frac{n-1}{n}=\frac{k}{n}

所以对于其中每个元素,被保留的概率都为 k/n.

 证法2:

用数学归纳法证明,每个样本被选中的概率的都是相等的。

初始情况是,当前只有k个样本,此时每个样本都被选中进入池子,也就是k/k=1的概率。

如果我们一共有n个数据点,假设每个数据点被选进入池子的概率相等,都是k/n。

根据数学归纳法的思想,下面我们只要证明如果一共有n+1个数据点,每个数据进入池子中的概率都是k/(n+1)。

对于第n+1个样本X_{n+1},它进入池子的概率显然是k/(n+1)。

对于第j个样本X_{j},j≤n,它在前一轮中已经在池子里的概率为k/n。

下面我们分两种情况讨论:第一种情况,X_{n+1}没被选中,所以X_{j}依然留在池子中。这种情况的概率为

P_{1}=1-\frac{k}{n+1}=\frac{n+1-k}{n+1}

第二种情况,X_{n+1}被选中但是X_{j}没有被替换。这种情况发生的概率为

P_{2}=\frac{k}{n+1}\times\frac{k-1}{k}=\frac{n}{n+1}

两者相加

P_{1}+P_{2}=\frac{n}{n+1}

所以X_{j}此时在池子中的概率为

\frac{k}{n}\times(P_{1}+P_{2})=\frac{k}{n}\times\frac{n}{n+1}=\frac{k}{n+1}

证毕.

代码示例

python代码示例

import random

'''
名称:蓄水池采样
参数:lis-一个list,模拟数据流
     k-int类型,表示从数据流中采样的个数
功能:从不知道什么时候会结束的数据流中等概率的保留k个数据,本例中使用一个列表模拟了数据流
'''

def Reservoir_Sampling(lis,k):
    result=lis[:k]          #前k个元素就不模拟数据流了,直接切片读取了
    i=0
    while True:
        try:
            ele=lis[k+i]    #尝试读取下一个数据,如果读取失败,说明数据流结束
        except:
            return result   #数据流结束,返回结果
        if random.randint(0,k+i)<k:
            result[random.randint(0,k-1)]=ele   #如果满足替换条件进行替换
        i+=1

if __name__=="__main__":
    lis=list(range(0,10))      #使用一个列表来模拟数据流,在后面会one-by-one读取数据
    #lis=['a','b','c','d','e','f','g','h','i','j']
    result=Reservoir_Sampling(lis,5)   #从数据流中等概率留下5个数据
    print(result)                      #输出结果

猜你喜欢

转载自blog.csdn.net/weixin_41481113/article/details/83449009