蓄水池抽样算法

xiaoxiao2021-02-28  97

刚好看到了记录一下:

“给出一个数据流,这个数据流的长度很大或者未知。并且对该数据流中数据只能访问一次。请写出一个随机选择算法,使得数据流中所有数据被选中的概率相等。”

首先,我们考虑从n个对象中等概率的选取1个对象。如果我们知道n的值,那么只需要从0-n-1中随机一个数选取它就可以了,被选中的概率是1/n

如果我们不知道n的值的时候,这个问题就转换成了一个蓄水池抽样问题,即我们总是选择第一个对象,以1/2的概率选择第二个,以1/3的概率选择第三个,

以此类推,以1/m的概率选取地m个对象,当该过程结束时,对每个对象具有相同选中的概率,均为1/n

证明如下:

m个对象最终被选中的概率P = 选择m的概率*后面所有的对象均不被选中的概率

伪代码如下: i = 0 while more input items with probability 1.0 / ++i choice = this input item print choice

Java代码如下:

k个中的代码k=1即可。

对于选取K的对象,思路类似。首先把读到的前K个对象放入“水库”,从第k+1个对象开始对于第m个对象,以k/m的概率选取,并以1/k的概率替换水库中的一个对象(貌似替换水库中的一个对象就是1/k,因为只能替换一个嘛),当这个过程结束,每个被选中的概率均为k/n。其实选取一个就是k=1这个特例

证明如下:

m个对象被选中的概率=选择m的概率*(其后元素不被选中的概率+其后元素被选中*不替换已经在水库中的m的概率)

伪代码如下: array S[n]; //source, 0-based array R[k]; // result, 0-based integer i, j; // fill the reservoir array for each i in 0 to k - 1 do R[i] = S[i] done; // replace elements with gradually decreasing probability for each i in k to n do j = random(0, i); // important: inclusive range if j < k then R[j] = S[i] fi done java代码:

public static int[] doReservoirSampling(int[] source, int k){ int[] result = new int[k]; int index = -1; for (int i : source) { ++index; if (index < k) { //将前k项先保存进结果数组中 result[index] = source[index]; continue; } int j = random.nextInt(index + 1);//生成随机数 下标是从0开始,但是概率是从1开始计算的 if (j < k) { //一共可以产生0-index 这么多数,其中0到k-1都可以满足条件,这样概率就是k/index+1 result[j] = source[index]; } } return result; } 测试结果(10000次):

K=1的时候:

total used : 0ms 1 : 993 2 : 1000 3 : 968 4 : 988 5 : 990 6 : 990 7 : 1072 8 : 1039 9 : 950 10 : 1010

K=5的时候:

total used : 15ms 1 : 4959 2 : 5007 3 : 4968 4 : 5077 5 : 4964 6 : 4926 7 : 5037 8 : 4974 9 : 5088 10 : 5000

参考链接:

http://blog.jobbole.com/42550/

http://blog.csdn.net/huagong_adu/article/details/7619665

转载请注明原文地址: https://www.6miu.com/read-48260.html

最新回复(0)