前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用Python写算法 | 蓄水池算法实现随机抽样

用Python写算法 | 蓄水池算法实现随机抽样

作者头像
simpleapples
发布2018-10-18 14:53:58
1.3K0
发布2018-10-18 14:53:58
举报
文章被收录于专栏:Python私房菜Python私房菜

现在有一组数,不知道这组数的总量有多少,请描述一种算法能够在这组数据中随机抽取k个数,使得每个数被取出来的概率相等。

如果这组数有n个,那么每个数字取到的概率就是k/n,但是这个问题的难点在于不知道这组数的总数,也就是不知道n,那么该怎么计算每个数取到的概率呢?

01

蓄水池算法

游泳池(蓄水池)大家都不陌生,有些游泳池中的水是活的,有入水管也有出水管,那么和泳池体积相当的水流过之后,是不是泳池中所有的水都会被替换呢?当然不是,有的水在泳池中可能会存留很久,有的可能刚进去就流走了。仿照这种现象,蓄水池抽样算法诞生了,蓄水池算法的关键在于保证流入蓄水池的水和已经在池中的水以相同的概率留存在蓄水池中。并且蓄水池算法可以在不预先知道总量的情况下,在时间复杂度O(N)的情况下,来解决这类采样问题。

02

核心原理

这一部分涉及公式,为了保证效果直接贴了图过来。

03

Python实现

接下来尝试用Python实现一下蓄水池算法,由于蓄水池算法是在事先不知道总量的情况下抽样的,所以定义一个方法来接收单个元素,并且把这个方法放在类中,以持有采样后的数据。

代码语言:javascript
复制
 1import random
 2
 3
 4class ReservoirSample(object):
 5
 6    def __init__(self, size):
 7        self._size = size
 8        self._counter = 0
 9        self._sample = []
10
11    def feed(self, item):
12        self._counter += 1
13        # 第i个元素(i <= k),直接进入池中
14        if len(self._sample) < self._size:
15            self._sample.append(item)
16            return self._sample
17        # 第i个元素(i > k),以k / i的概率进入池中
18        rand_int = random.randint(1, self._counter)
19        if rand_int <= self._size:
20            self._sample[rand_int - 1] = item
21        return self._sample

04

测试代码

接下来实现一个测试用例验证实现的算法是否正确,既然是随机抽样,无法通过单词测试来验证是否正确,所以通过多次执行的方式来验证,比如从1-10里随机取样3个数,然后执行10000次取样,如果算法正确,最后结果中1-10被取样的次数应该是相同的,都是3000上下。

代码语言:javascript
复制
 1import unittest
 2from collections import Counter
 3
 4from reservoir_sample import ReservoirSample
 5
 6
 7class TestMain(unittest.TestCase):
 8
 9    def test_reservoir_sample(self):
10        samples = []
11        for i in range(10000):
12            sample = []
13            rs = ReservoirSample(3)
14            for item in range(1, 11):
15                sample = rs.feed(item)
16            samples.extend(sample)
17        r = Counter(samples)
18        print(r)
19
20if __name__ == '__main__':
21    unittest.main()

输出的结果如下

代码语言:javascript
复制
1Counter({7: 3084, 6: 3042, 10: 3033, 3: 3020, 8: 3016, 5: 2997, 4: 2986, 2: 2972, 9: 2932, 1: 2918})

上面输出了每个数字被取样到的次数,通过图表可以清晰的看到分布情况

可以看出蓄水池算法对于随机抽样还是非常适合的,每个元素的抽样概率都相同。

05

代码

上述的算法和测试代码已经放在Github,地址是https://github.com/python-fan/reservoir-sample,可以直接下载使用。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-08-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python私房菜 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档