蓄水池抽样算法

刚好看到了记录一下:

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

首先,我们考虑从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




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值