蓄水池随机抽样算法

蓄水池算法在实际应用中比较常见,比如在一次抽奖活动中,在不知道总人数的情况下,每进来一个人都打上编号(1~n,共n个人),我们需要等概率抽取k个获奖人员,如何保证等概率?

在已知长度的数组中随机等概率抽取k个数据很容易,但如果长度未知呢?事先计算一次长度?如果内存不够呢?数据量非常大呢?蓄水池采样(Reservoir Sampling)算法就是来解决这类问题的, 它在分析一些大数据集的时候会非常有效。

问题已经清楚了,那接下来我们该如何切入这类问题呢?

我们先给出结论:

如果数据处理过程满足如下三个条件,那么这k个元素获取的概率均为k/n,条件如下:

1、首先数据流中的前k个元素,并保存在集合A中;
2、从第m(k + 1 <= m <= n)个元素开始,每次先以概率p = k/m选择是否让第m个元素留下。若m被选中,则从集合A中随机选择一个元素r( 1<= r <= k)并用该元素m替换r;否则直接淘汰该元素(不做处理);
3、重复步骤2直到m = n,最终集合A中剩下的k个元素选中概率均为k/n


简单证明:

假设元素m被选中,即上述1、2、3过程均执行完后,元素m最终保留在集合A中,此时计算元素m被选中的概率p:

首先要清楚:

元素m最终被选中的概率 = 元素m被选中 * (元素m后面的元素没有被选中 + 元素m后面的元素被选中*替换过程中元素m没有被替换)

上述表达式中的“后面的元素”并不止指的m+1,还包含m+2, m+3, .... n,因为元素m是否最终被选中(注意“最终”两个字),跟后面的元素是有关系的,因为比如后面选中后,需要执行一步替换过程(若不替换,无法满足等概率,可以理解为硬性操作),万一替换过程将前面的m元素替换了,m就不可能(注意,不是“可能不”,是“不可能”)在最终的元素集合A中了。如下图所示:

蓄水池随机抽样算法_第1张图片

伪代码实现:

Init : a reservoir with the size: k
        for i= k+1 to N
            M=random(1, i);
            if( M < k)
                SWAP the Mth value and ith value
 end for

伪代码中的if语句,目的是确保当前的i值被选中(只要判断随机数是否在1-k范围内即可),因为根据上述条件2,如果不被选中,则不care。

可以通过示例蓄水池例子加深对蓄水池算法的理解。

你可能感兴趣的:(java)