Reservoir Sampling( 水塘抽样 / 蓄水池抽样 )
用于解决当内存无法加载全部数据时,从包含未知大小的数据流中随机选取 k k k 个数据,并且要保证每个数据被抽取到的概率相等的问题。
说人话就是对于不知道数量的数据,仅遍历一遍且不用保存全部数据就能得出结果。
正常情况下需要先遍历一遍得到数据的大小n,再抽取结果,抽取结果时要么是将数据再遍历一遍选取对应位置的数据,要么是将所有数据保存下来,直接获取对应位置的数据。
- k = 1 k = 1 k=1
对 于 第 i 个 数 据 , 以 1 i 的 几 率 保 留 , 作 为 最 终 的 结 果 。 对于第 i 个数据,以 \frac{1}{i} 的几率保留,作为最终的结果。 对于第i个数据,以i1的几率保留,作为最终的结果。
总共有 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)×(1−p(n2))=1×(1−21)=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)×(1−p(n2))×(1−p(n3))=1×(1−21)×(1−31)=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)×(1−p(n3))=21×(1−31)=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×…×不保留nn−1×不保留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×…×n−1n−2×nn−1=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+i个数据,以k+ik的几率保留,以k1的几率替换当前保留的k个数中的一个,作为最终的结果。
总共有 k k k 个数时 :
p ( n r [ r ∈ 1 : k ] ) = 1 p(n_r[r\in1:k])=1 p(nr[r∈1: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[r∈1:k]的概率=选择nr×nk+1不替换nr
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+1不替换nr的概率=不保留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×kk−1)=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+1不替换nm×nm+2不替换nm×…×nn−1不替换nm×nn不替换nm
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×…×n−1n−2×nn−1=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;
}