蓄水池采样

文章目录

问题

给定一个数据流,数据流长度 N N N很大,且 N N N直到处理完所有数据之前都不可知,请问如何在只遍历一遍数据(O(N))的情况下,能够等概率随机选取出 k k k个不重复的数据。

解决

这个主要考察蓄水池算法,具体就是:保存一个 k k k大小的窗口,然后依次接收数据流,对于数据流第 i i i个元素( i > k i>k i>k),以 k i \frac{k}{i} ik 的概率替换窗口中的某个元素,最终得到的 k k k个元素就是均匀采样得到的,每个元素被采到的概率都是 k N \frac{k}{N} Nk

python实现:

import random

def reservoir_sampling(window, k, i, data):
    """
    每次得到数据流中的一个元素后怎么更新窗口
    :param window: 窗口
    :param k: 需要采样k个元素
    :param i: 当前数据流元素的索引,从0开始
    :param data: 当前得到的数据流元素
    :return: 更新后的window
    """
    if len(window) < k:
        window.append(data)
    else:
        # 从[0, i]中随机得到一个整数
        replace = random.randint(0, i)
        if replace < k:
            window[replace] = data
    return window

使用数学归纳法给出该算法合理性的简单证明。

  1. 假设 i = 1 i=1 i=1,每个元素被选中的概率为1;
  2. 设前 i i i个元素被选中的概率为 k i \frac{k}{i} ik。对于第 i + 1 i+1 i+1个元素,我们以 k i + 1 \frac{k}{i+1} i+1k的概率替换窗口中的元素,也就是说对于这个元素,被选中的概率是 k i + 1 \frac{k}{i+1} i+1k。对于窗口中原有的 k k k个元素,其被替换掉的概率为 k i + 1 ∗ 1 k \frac{k}{i+1}*\frac{1}{k} i+1kk1,那么除了最近得到的元素,其余元素被采样得到的概率为:
    k i ( 1 − k i + 1 1 k ) = k i + 1 \frac{k}{i}(1-\frac{k}{i+1}\frac{1}{k})=\frac{k}{i+1} ik(1i+1kk1)=i+1k
    所以遍历数据流之后,得到的 k k k个元素是以概率 k N \frac{k}{N} Nk均匀采样得到的。

除此之外也有分布式的蓄水池采样,具体就是分 P P P个数据流: N 1 , ⋯   , N P , ∑ p = 1 P N p = N N_1,\cdots,N_P,\sum_{p=1}^{P}N_p=N N1,,NP,p=1PNp=N,假设对于任意数据流都有 N p > = k N_p>=k Np>=k,分别进行蓄水池采样,得到 P P P个大小为 k k k的蓄水池。

然后按照数据流的大小进行选择,即对于数据流 N p N_p Np,其蓄水池被选中的概率为 N p N \frac{N_p}{N} NNp,并从中随机移出一个元素,重复 k k k次即可。这个过程可以分解为两两一组合并,类似归并,这样又可以并行了。

每个元素每次被采样到的概率为 k N p N p N 1 k = 1 N \frac{k}{N_p}\frac{N_p}{N}\frac{1}{k}=\frac{1}{N} NpkNNpk1=N1(这个 k k k可以看做当前蓄水池的大小),重复 k k k次,任意元素被采样到的概率为 k N \frac{k}{N} Nk

猜你喜欢

转载自blog.csdn.net/qq_38163755/article/details/116850053
今日推荐