蓄水池抽样

1. 蓄水池的容积为1

(1)问题

如何随机从n个对象中选择一个对象,这n个对象是按序排列的,但是在此之前你不知道n的值?

(2)算法与证明

算法:我们先将第一个对象作为备选放入蓄水池,也就是说第一个对象被放入蓄水池的概率为1,以 1 / 2 1/2 1/2的概率选择第二个,以 1 / 3 1/3 1/3的概率选择第三个,以此类推,以 1 / m 1/m 1/m的概率选择第m个对象。当该过程结束时,每一个对象具有相同的选中概率,即 1 / n 1/n 1/n

证明:第m个对象最终被选中的概率P=选择m的概率*其后面所有对象不被选择的概率,即

P = 1 m × ( m m + 1 × m + 1 m + 2 × ⋅ ⋅ ⋅ × n − 1 n ) = 1 n P=\frac{1}{m}\times(\frac{m}{m+1}\times\frac{m+1}{m+2}\times···\times\frac{n-1}{n})=\frac{1}{n} P=m1×(m+1m×m+2m+1××nn1)=n1

由于这里处理的对象是以流的形式出现的,所以当第m个对象出现时,在已有的数据中选中第m个对象的概率为 1 m \frac{1}{m} m1,当第m+1个对象出现时没选中第m+1个对象的概率为 m m + 1 \frac{m}{m+1} m+1m,以此类推。

所以在实际问题的处理中,只需要对每个新增的对象按照 1 m \frac{1}{m} m1的概率来选择就好了。

(3)代码实现

假设我们要在n行的文本文件中随机选取一行,但事先不知道n为多少,则代码如下:

m = 0
s = null
for line in 文本文件:
    m = m+1
    r = (0,1)之间的随机数
    if r < 1/m:
        s = line

2.蓄水池的容积为m

(1)问题

如何随机从n个对象中选择m个对象,这n个对象是按序排列的,但是在此之前你不知道n的值?

(2)算法与证明

前m个对象被选中的概率为1,当我们读到第i-1行时(i-1大于m),我们假设前i-1个对象被选中的概率均为 m i − 1 \frac{m}{i-1} i1m,那么,当我们读到第i行的时候,前i-1行中每一行被选中的概率为上一次被选中的概率乘以这一次没有被选中的概率,即为
P = m i − 1 × ( 1 − m i × 1 m ) = k i P=\frac{m}{i-1}\times(1-\frac{m}{i}\times\frac{1}{m})=\frac{k}{i} P=i1m×(1im×m1)=ik

(3)代码

i = 0
s = []
for line in 文本文件:
    i = i+1
    if i <= m:
        s.append(line)
    else:
        j = [1,i]之间的随机数
        if j <= m:
            s[j] = line
### 蓄水池抽样算法原理 蓄水池抽样算法是一种用于从流数据中随机抽样的算法。其核心思想在于,当面对一个非常大的数据集而内存无法承载全部数据时,可以仅保留一定数量的数据,并按特定概率对新到来的数据进行采样[^2]。 具体来说,对于每一个新的元素,会以 \( \frac{k}{i} \) 的概率决定是否将其加入到大小为 k 的样本集合(即所谓的“蓄水池”)之中;这里的 i 表示当前处理的是第几个元素,k 则表示希望抽取多少个样本。这种机制确保了即使不知道整个输入序列的具体长度也能均匀地从中选取样本[^3]。 #### 数学解释 为了理解为什么这种方法能够保证每个项目被选入的概率相等,考虑前 i 个项目出现在蓄水池里的可能性由两方面构成: - 在第 i+1 次选择之前已经在蓄水池里; - 不会在第 i+1 次的选择过程中被淘汰出去。 因此,通过这种方式构建起来的过程能使得所有项目的入选几率保持一致[^4]。 ### Java 实现案例 针对不同情况下的蓄水池抽样需求,以下是两个具体的Java实现版本: #### 当 `K>1` 时的完整版 ```java import java.util.Random; public class ReservoirSampling { private final int[] reservoir; private final Random random = new Random(); private int count = 0; public ReservoirSampling(int size) { this.reservoir = new int[size]; } public void addElement(int element) { if (count < reservoir.length) { reservoir[count++] = element; } else { int r = random.nextInt(count); if (r < reservoir.length) { reservoir[r] = element; } } count++; } @Override public String toString() { StringBuilder sb = new StringBuilder("["); for (int value : reservoir) { sb.append(value).append(", "); } return sb.substring(0, Math.max(sb.length() - 2, 0)) + "]"; } } ``` 这段代码展示了如何在一个未知规模的数据流上执行固定大小为 K 的简单无放回随机抽样操作。 #### LeetCode 应用实例 在实际应用中,蓄水池抽样也被广泛应用于解决各种编程挑战问题,比如 LeetCode 上编号为 398 和 382 的题目分别涉及到了基于权重的随机挑选以及链表结构上的节点随机访问等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

comli_cn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值