对这个问题我们首先从最简单的例子出发:数据流只有一个数据。我们接收数据,发现数据流结束了,直接返回该数据,该数据返回的概率为1。看来很简单,那么我们试试难一点的情况:假设数据流里有两个数据。
我们读到了第一个数据,这次我们不能直接返回该数据,因为数据流没有结束。我们继续读取第二个数据,发现数据流结束了。因此我们只要保证以相同 的概率返回第一个或者第二个数据就可以满足题目要求。因此我们生成一个 0 到 1 的随机数R,如果R小于 0.5 我们就返回第一个数据,如果R大于 0.5,返回第二个数据。
接着我们继续分析有三个数据的数据流的情况。为了方便,我们按顺序给流中的数据命名为1、2、3。我们陆续收到了数据1、2 和前面的例子一样,我们只能保存一个数据,所以必须淘汰 1 和 2 中的一个。应该如何淘汰呢?不妨和上面例子一样,我们按照二分之一的概率淘汰一个,例如我们淘汰了 1. 继续读取流中的数据3,发现数据流结束了,我们知道在长度为 3 的数据流中,如果返回数据 3 的概率为1/3 那么才有可能保证选择的正确性。也就是说,目前我们手里有1,3 两个数据,我们通过一次随机选择,以1/3 的概率留下数据3,以2/3 的概率留下数据 1. 那么数据 1 被最终留下的概率是多少呢?
- 数据 1 被留下:(1/2)*(2/3) = 1/3
- 数据 2 被留下概率:(1/2)*(2/3) = 1/3
- 数据 3 被留下概率:1/3
这个方法可以满足题目要求,所有数据被留下返回的概率一样!
因此,我们做一下推论:假设当前正要读取第n个数据,则我们以1/n的概率留下该数据,否则留下前n-1 个数据中的一个。以这种方法选择,所有数据流中数据被选择的概率一样。简短的证明:假设n-1 时候成立,即前n-1 个数据被返回的概率都是1/n-1,当前正在读取第n个数据,以1/n的概率返回它。那么前n-1 个数据中数据被返回的概率为:(1/(n-1))*((n-1)/n)= 1/n,假设成立。
其伪代码如下:
Init : a reservoir with the size: kfor i= k+1 to N
M=random(1, i);
if( M < k)
SWAP the Mth value and ith value
end for
证明每个数被取到的概率为k/n:
-
对于第i个数(i<k),在前k步被选中的概率是1, 从第k+1步开始,i不被选中的概率为k/k+1,那么读到第n个数时, 第i个数(i<k)被选中的概率 = 被选中的概率 * 以后每一步都不被换走的概率,即
1 * k/k+1 * k+1/k+2 …n-1/n = k/n -
对于第j个数(j>=k)被选中的概率为: 在他出现时被选中的概率 * 在他出现以后不被换走的概率,即:
k/j * j /j+1 。。。n-1/n = k/n -
综上得证。