水塘抽样 / 蓄水池抽样算法 (详解+推导+代码) 史上最完美笔记


Reservoir Sampling( 水塘抽样 / 蓄水池抽样 )

用于解决当内存无法加载全部数据时,从包含未知大小的数据流中随机选取 k k k 个数据,并且要保证每个数据被抽取到的概率相等的问题。

说人话就是对于不知道数量的数据,仅遍历一遍且不用保存全部数据就能得出结果。
正常情况下需要先遍历一遍得到数据的大小n,再抽取结果,抽取结果时要么是将数据再遍历一遍选取对应位置的数据,要么是将所有数据保存下来,直接获取对应位置的数据。

  • k = 1 k = 1 k=1

对 于 第 i 个 数 据 , 以 1 i 的 几 率 保 留 , 作 为 最 终 的 结 果 。 对于第 i 个数据,以 \frac{1}{i} 的几率保留,作为最终的结果。 ii1

总共有 1 1 1 个数时 :

p ( n 1 ) = 1 1 = 1 p(n_1) = \frac{1}{1} = 1 p(n1)=11=1

总共有 2 2 2 个数时 :

结 果 为 n 1 的 概 率 = 选 择 n 1 的 概 率 × 不 保 留 n 2 的 概 率 结果为n_1的概率 = 选择n_1的概率 \times 不保留n_2的概率 n1=n1×n2

p ( n 1 ) = p ( n 1 ) × ( 1 − p ( n 2 ) ) = 1 × ( 1 − 1 2 ) = 1 2 p(n_1) = p(n_1) \times (1-p(n_2)) = 1 \times ( 1 - \frac{1}{2}) = \frac{1}{2} p(n1)=p(n1)×(1p(n2))=1×(121)=21

p ( n 2 ) = 1 2 p(n_2) = \frac{1}{2} p(n2)=21

总共有 3 3 3 个数时 :

结 果 为 n 1 的 概 率 = 选 择 n 1 的 概 率 × 不 保 留 n 2 的 概 率 × 不 保 留 n 3 的 概 率 结果为n_1的概率 = 选择n_1的概率 \times 不保留n_2的概率 \times 不保留n_3的概率 n1=n1×n2×n3

p ( n 1 ) = p ( n 1 ) × ( 1 − p ( n 2 ) ) × ( 1 − p ( n 3 ) ) = 1 × ( 1 − 1 2 ) × ( 1 − 1 3 ) = 1 3 p(n_1) = p(n_1) \times (1-p(n_2)) \times (1-p(n_3)) = 1 \times ( 1 - \frac{1}{2}) \times ( 1 - \frac{1}{3}) = \frac{1}{3} p(n1)=p(n1)×(1p(n2))×(1p(n3))=1×(121)×(131)=31

结 果 为 n 2 的 概 率 = 选 择 n 2 的 概 率 × 不 保 留 n 3 的 概 率 结果为n_2的概率 = 选择n_2的概率 \times 不保留n_3的概率 n2=n2×n3

p ( n 2 ) = p ( n 2 ) × ( 1 − p ( n 3 ) ) = 1 2 × ( 1 − 1 3 ) = 1 3 p(n_2) = p(n_2) \times (1-p(n_3)) = \frac{1}{2} \times ( 1 - \frac{1}{3}) = \frac{1}{3} p(n2)=p(n2)×(1p(n3))=21×(131)=31

p ( n 3 ) = 1 3 p(n_3) = \frac{1}{3} p(n3)=31

总共有 n n n 个数,对于第 m m m 个和第 n n n 数 :

结 果 为 n m 的 概 率 = 选 择 n m × 不 保 留 n m + 1 × 不 保 留 n m + 2 × … × 不 保 留 n n − 1 × 不 保 留 n n 结果为n_m的概率 = 选择n_m \times 不保留n_{m+1} \times 不保留n_{m+2} \times\ldots \times 不保留n_{n-1} \times 不保留n_{n} nm=nm×nm+1×nm+2××nn1×nn

p ( n m ) = 1 m × m m + 1 × m + 1 m + 2 × … × n − 2 n − 1 × n − 1 n = 1 n p(n_m) = \frac{1}{m} \times \frac{m}{m+1} \times \frac{m+1}{m+2} \times \ldots \times \frac{n-2}{n-1} \times \frac{n-1}{n} = \frac{1}{n} p(nm)=m1×m+1m×m+2m+1××n1n2×nn1=n1

p ( n n ) = 1 n p(n_n) = \frac{1}{n} p(nn)=n1

public int getRandom(Sample sample)
{
    int index = 1;
    int choice = 0;

    while (sample != null)
    {
        if (random.nextInt(index) == 0)
        {
            choice = sample.val;
        }

        index++;
        sample = sample.nextSample;
    }

    return choice;
}
  • k > 1 k > 1 k>1

对 于 前 k 个 数 据 , 全 部 保 留 。 对于前 k 个数据,全部保留。 k
对 于 第 k + i 个 数 据 , 以 k k + i 的 几 率 保 留 , 以 1 k 的 几 率 替 换 当 前 保 留 的 k 个 数 中 的 一 个 , 作 为 最 终 的 结 果 。 对于第 k+i 个数据,以 \frac{k}{k+i} 的几率保留,以 \frac{1}{k}的几率替换当前保留的k个数中的一个,作为最终的结果。 k+ik+ikk1k

总共有 k k k 个数时 :

p ( n r [ r ∈ 1 : k ] ) = 1 p(n_r[r\in1:k])=1 p(nr[r1:k])=1

总共有 k + 1 k+1 k+1 个数时 :

结 果 为 n r [ r ∈ 1 : k ] 的 概 率 = 选 择 n r × n k + 1 不 替 换 n r 结果为n_r[r\in1:k]的概率 = 选择n_r \times n_{k+1}不替换n_r nr[r1:k]=nr×nk+1nr

n k + 1 不 替 换 n r 的 概 率 = 不 保 留 n k + 1 + 保 留 n k + 1 × 不 替 换 n r n_{k+1}不替换n_r的概率 = 不保留n_{k+1} +保留n_{k+1} \times 不替换n_r nk+1nr=nk+1+nk+1×nr

p ( n r ) = 1 × ( 1 k + 1 + k k + 1 × k − 1 k ) = k k + 1 p(n_r) =1\times (\frac{1}{k+1} + \frac{k}{k+1} \times \frac{k-1}{k}) = \frac{k}{k+1} p(nr)=1×(k+11+k+1k×kk1)=k+1k

p ( n k + 1 ) = k k + 1 p(n_{k+1}) =\frac{k}{k+1} p(nk+1)=k+1k

总共有 n n n 个数,对于第 m m m 个和第 n n n 个数 :

结 果 为 n m 的 概 率 = 选 择 n m × n m + 1 不 替 换 n m × n m + 2 不 替 换 n m × … × n n − 1 不 替 换 n m × n n 不 替 换 n m 结果为n_m的概率 = 选择n_m \times n_{m+1}不替换n_m \times n_{m+2}不替换n_m \times\ldots \times n_{n-1}不替换n_m \times n_n不替换n_m nm=nm×nm+1nm×nm+2nm××nn1nm×nnnm

p ( n m ) = k m × m m + 1 × m + 1 m + 2 × … × n − 2 n − 1 × n − 1 n = k n p(n_m) = \frac{k}{m} \times \frac{m}{m+1} \times \frac{m+1}{m+2} \times \ldots \times \frac{n-2}{n-1} \times \frac{n-1}{n} = \frac{k}{n} p(nm)=mk×m+1m×m+2m+1××n1n2×nn1=nk

p ( n n ) = k n p(n_n) = \frac{k}{n} p(nn)=nk

public int[] getRandom(Sample sample, int k)
{
    int index = 1;
    int[] choice = new int[k];

    for (int i = 0; i < k; i++)
    {
        choice[i] = sample.val;
        sample = sample.nextSample;
    }

    while (sample != null)
    {
        int randomNum = random.nextInt(index);

        if (randomNum < k)
        {
            choice[randomNum] = sample.val;
        }

        index++;
        sample = sample.nextSample;
    }

    return choice;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值