蓄水池问题

题目描述:
给出一个数据流,这个数据流的长度很大或者未知,并且对该数据流中数据只能访问一次。请写出一个随机选择算法,使得数据流中所有数据被选中的概率相等。
或者也可以这么说:
要求从N个元素中随机的抽取k个元素,其中N的大小未知。

解题思路:
用到的方法为蓄水池抽样算法(reservoid sampling)。具体的思路是:先初始化一个集合,集合中有k个元素,将此集合作为蓄水池。然后从第k+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  

具体描述如下:先将前k个数取出来放入结果集中,然后从第k+1个数开始遍历。假设遍历到第i个数,以 k/i 的概率替换掉蓄水池中的某个元素即可。

!/usr/bin/env python
#coding:utf-8

import random
import collections

#用蓄水池算法模拟从10个数中随机抽取一个数
def reservoir():
    raw_list = [0,1,2,3,4,5,6,7,8,9]
    ret_num = raw_list[0] #蓄水池初始化。因为只需要抽取一个,所以给一个变量即可

    for i in range(1,10): #从第k+1个元素开始遍历
        m = random.randint(1,i+1) #因为列表下标从0开始,所以随机数上限为i+1而不是i
        if m <= 1:
            ret_num = raw_list[i] #蓄水池里的元素替换

    return ret_num

#抽取十万次,看看最后的结果
def run():
    dic = collections.defaultdict(int)
    for i in range(100000):
        ret_num = reservoir()
        dic[ret_num] += 1

    for k,v in dic.items():
        print(k,":",v)

run()

从结果可以看出,每个数被抽到的次数基本时接近的。

0 : 9926
1 : 10024
2 : 10056
3 : 10043
4 : 10004
5 : 9826
6 : 10083
7 : 10036
8 : 9843
9 : 10159
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值