1. 问题定义
在保证$n$个元素被抽取的概率是相同的前提下,从总量为$n$的样本空间中随机抽取$k$个元素
2. 应用场景和一般算法
(1) 对于总数$n$值已知的情况
我们可以用最简单的随机数算法,生成范围在 $[1, n]$间的$k$个随机数。
(2) 对于总数$n$值提前未知的情况
一种方法是,首先遍历这个样本空间下所有样本并计数,得到n,之后再用(1)中的方法等随机抽样。
但是先遍历一遍样本空间,在样本很大的情况下是很耗时的。有没有不需要提前遍历一遍整个样本空间的,又能公平抽样的算法呢?
这里将介绍蓄水池算法,是一个被证明绝对公平的,不需提前预知样本空间大小的抽样算法。
(3) 实际场景
什么时候是"总数提前未知且要求随机抽样"的场景呢?
ex1. 从一本很厚的电话簿中抽取 1000 人进行姓氏统计
ex2. 从 Google 搜索 "Ken Thompson",从中抽取 100 个结果查看哪些是今年的
ex3. 我自己的实际工作场景:在推词工作中,要对最终项目产出的推荐词进行相关性评估,即评估推荐词和已购词的相关性。这里需要抽取,比如300条case人工评定一下相关性。那这里我们词库中的词太多啦,在hdfs中保存成了几千的part。要怎么对这么多数据随机抽取300条呢?
最初自己的想法是随机选几个part, 把随机取的几个part拼接,之后用随机数算法抽取。
但是有个问题,由于hadoop的工作原理,在reduce阶段把key相同/相近的放在一起了,使得保存在集群上的各个part间的数据可能不是完全独立的,并且不同part包含的数据量也可能不同。那在第一步随机选part这个阶段就不能保证完全公平了。虽然这个粗糙的方法也不太伤大雅,但是有更好的算法就可以学着用一下~
3. 蓄水池算法抽样过程
假设数据序列的规模为n, 需要采样的数量为$k$.
首先构建一个可容纳$k$个元素的数组,将序列的前$k$个元素放入数组中。
然后从第$k + 1$个元素开始,以$\frac{k}{n}$的概率来决定该元素是否被替换到数组中 (数组中的元素被替换的概率是相同的)。
当遍历完所有元素后,数组中剩下的元素即为所需采样的样本。
4. 证明
(1) 对于第$i$个数 ($i <= k$)
(i) 在第$1, ..., k$步,该元素被选中到蓄水池数组的概率为1.
(ii) 在第$k+1$步,数据序列规模为$k + 1$.
该元素被替换的概率 = $k + 1$个元素被选中的概率 * $i$被选中替换的概率 = $\frac{k}{k + 1} \times \frac{1}{k} = \frac{1}{k + 1}$
该元素被保留的概率 = $1 - \frac{1}{k + 1} = \frac{k}{k + 1}$
(iii) 在第$k+2$步,数据序列规模为$k + 2$.
该元素不被第$k + 1$个元素替换(被保留)的概率 = $1 - \frac{k}{k + 2} \times \frac{1}{k} = \frac{k + 1}{k + 2}$
(iv) 第$n$步,该元素被保留的概率
$$1 \times \frac{k}{k + 1} \times \frac{k + 1}{k + 2} \times ... \times \frac{n - 1}{n} = \frac{k}{n} $$
(2) 对于第$j$个数 ($j > k$)
在第$j$步被选中的概率为$\frac{k}{j}$。
在第$j + 1$步,不被$j + 1$个元素替换的概率为$1 - \frac{k}{j + 1} \times \frac {1}{k} = \frac{j}{j + 1}$。
在第$j + 2$步,不被$j + 2$个元素替换的概率为$1 - \frac{k}{j + 2} \times \frac {1}{k} = \frac{j + 1}{j + 2}$
运行到第$n$步,该元素被保留的概率
$$1 \times \frac{j}{j + 1} \times \frac{j + 1}{j + 2} \times ... \times \frac{n - 1}{n} = \frac{k}{n}$$
因此,对于这$n$个元素,被保留的概率均为$\frac{k}{n}$。
参考链接:https://www.cnblogs.com/snowInPluto/p/5996269.html