项目github地址:bitcarmanlee easy-algorithm-interview-and-practice
欢迎大家star,留言,一起学习进步

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

很多同学说,擦,这还不简单么,将所有元素保存在一个列表中,然后再随机取k个不就完了么。
好吧,如果你不是专门搞大大大数据的同学,这么说我觉得情有可原。如果你真的是天天跟大大大数据打交道的同学,这么说的话就显得不那么professional了。大数据最重要的特点是什么?一个字,大大大啊!重要的问题必须说三遍。大,那意味着什么?意味着内存放不下呀!所以,我们才需要通过设计精巧的算法,来降低对内存的渴求,达到我们最终的目的。当然可能有土豪会反驳我,俺们公司就是牛逼,俺们就是有钱,内存无限加,128G不够上256G,256G还不够我直接给你加1T内存,反正就是一句话,内存管够!碰到这样的土豪,我也只能淡淡一笑:128G内存到1T内存,即使加上了,也只是翻了10倍。但是现在数据量的增长速度,一般来说可是远远要大于硬件设备的扩容速度的。所以最终,还是需要通过更加精致的算法来解决问题,硬件扩容不是解决问题的根本办法。

前面扯得有点多,下面来看看这个问题怎么解。用到的方法为蓄水池抽样算法(reservoid sampling)。具体的思路是:先初始化一个集合,集合中有k个元素,将此集合作为蓄水池。然后从第k+1个元素开始遍历,并且按一定的概率替换掉蓄水池里面的元素。

来自《The Art of Computer Programming》里的伪代码:

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个数,以ki\frac{k}{i}ik​的概率替换掉蓄水池中的某个元素即可。

简单证明一下每个元素出现的概率都是相同的:
假设n−1n-1n−1时候成立,即前n−1n-1n−1个数据被返回的概率都是1/n−11/n-11/n−1。当前正在读取第nnn个数据,以1/n1/n1/n的概率返回它。那么前n−1n-1n−1个数据中数据被返回的概率为:(1/(n−1))∗((n−1)/n)=1/n(1/(n-1))*((n-1)/n)= 1/n(1/(n−1))∗((n−1)/n)=1/n,假设成立。

参考了hackbuteer1同学的思路,用数学归纳法证明如下。
问题描述:
取前k个元素放入蓄水池中。从i=k+1i=k+1i=k+1开始,以ki\frac{k}{i}ik​的概率取第i个元素。若第i个元素被选中,已均等的概率替换蓄水池中的先前被选中的任一元素。
证明:
当i=k+1i=k+1i=k+1时,第k+1k+1k+1个元素被选中的概率是kk+1\frac{k}{k+1}k+1k​ ,而前k个元素被选中的概率=1 - 被第k+1个元素替换的概率 =1−kk+1×1k=kk+1= 1 - \frac{k}{k+1} \times \frac{1}{k} = \frac{k}{k+1}=1−k+1k​×k1​=k+1k​,说明前面k+1k+1k+1个元素被取到的概率都是相等的且均为kk+1\frac{k}{k+1}k+1k​ 。
假设i=ni=ni=n时,前p个元素都以kn\frac{k}{n}nk​被选中。
那么当i=n+1i=n+1i=n+1是,第n+1n+1n+1个元素被选中的概率为kn+1\frac{k}{n+1}n+1k​
对于前面的n个元素,每个元素被选中的情况分为两种:1.前面n次已经被选中并且第n+1次时,第n+1个元素没有被选中;2.前面n此已经被选中,第n+1个元素被选中但是没有将其替换掉。不难写出此时的概率为:
kn×(1−kn+1)+kn×(kn+1×(1−1k))=kn+1\frac{k}{n} \times(1-\frac{k}{n+1}) + \frac{k}{n} \times (\frac{k}{n+1} \times(1 - \frac{1}{k})) = \frac{k}{n+1}nk​×(1−n+1k​)+nk​×(n+1k​×(1−k1​))=n+1k​
由此可见,第n+1步也满足假设条件。所以问题得到证明。

理论说了那么多,一行代码没有,这显然不是我们的风格。一言不合那就直接上代码:

!/usr/bin/env python
#coding:utf-8import 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而不是iif 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] += 1for k,v in dic.items():print k,":",vrun()

将代码run起来以后,看看最后的输出结果:

0 : 9926
1 : 10024
2 : 10056
3 : 10043
4 : 10004
5 : 9826
6 : 10083
7 : 10036
8 : 9843
9 : 10159

从结果可以看出,每个数抽取的次数都在一万次左右,这也就说明,上述代码达到了我们预期的效果!

大数据工程师必备之蓄水池抽样算法相关推荐

  1. 大数据工程师必备技能

    版权声明: https://blog.csdn.net/laomo_bible/article/details/79582023 引用几张图片: 包括基本技能和高阶能力: 基本技能: 1.java.p ...

  2. 算法工程师、软件工程师、大数据工程师的区别

    前段时间,有几个HR朋友问我: 算法工程师的日常工作到底是在干嘛? 平常看起来似乎还挺闲的,工资还那么高. 有时候算法工程师好像又和大数据工程师是一样的工作? 这到底是怎么回事呢? 大约整理出以下几个 ...

  3. 算法工程师、软件工程师、大数据工程师,傻傻分不清楚

    前段时间,有几个HR朋友问我: 算法工程师的日常工作到底是在干嘛? 平常看起来似乎还挺闲的,工资还那么高. 有时候算法工程师好像又和大数据工程师是一样的工作? 这到底是怎么回事呢? 大约整理出以下几个 ...

  4. 【大数据算法】蓄水池抽样算法

    一.题目来源: 这个题目的由来是周围有人讨论到去面试(某8)的时候遇到了这个问题.另外正好HIT有个视频也有这个内容,故记录一下: 二.题目描述:     该人面试的时候问的是: 如何从二进制文件中等 ...

  5. 算法/数据工程师必备技能

    算法/数据工程师必备技能 基础知识 线性代数 矩阵理论 概率论 随机过程 图论 数值分析 最优化理论 机器学习 统计学习方法 数据挖掘 平台 Linux  语言 Python Linux shell ...

  6. 当我说转行大数据工程师时,众人笑我太疯癫,直到四个月后......

    [不要错过文末彩蛋] 申明: 本文旨在为[大数据自学者|大数据专业学生|工资低的程序员(Java/Python等)]提供一个从入门到入职的的大数据技术学习路径,不适合5年以上大数据工程师的进阶学习. ...

  7. 蓄水池抽样算法(reservoir sampling)

    蓄水池抽样算法(reservoir sampling) 场景:在长度未知的数据流中,等概率地采样一定数量的数据.即,数据量N未知,若要求采样k个数据,采样概率保证kN\frac{k}{N}Nk​. 要 ...

  8. 蓄水池采样算法的python实现_蓄水池抽样算法(Reservoir Sampling)

    蓄水池抽样算法(Reservoir Sampling) 许多年以后,当听说蓄水池抽样算法时,邱simple将会想起,那个小学数学老师带他做"小明对水池边加水边放水,求何时能加满水" ...

  9. 大数据工程师需要哪些基础知识?

    大数据是眼下非常时髦的技术名词,与此同时自然也催生出了一些与大数据处理相关的职业,通过对数据的挖掘分析来影响企业的商业决策. 这群人在国外被叫做数据科学家(Data Scientist),这个头衔最早 ...

  10. 大数据工程师职业发展以及薪酬一览

    大数据是眼下非常时髦的技术名词,与此同时自然也催生出了一些与大数据处理相关的职业,通过对数据的挖掘分析来影响企业的商业决策. 这群人在国外被叫做数据科学家(Data Scientist),这个头衔最早 ...

最新文章

  1. mysql raw_Oracle中的Raw类型解释
  2. RESTFUL框架-网站即软件
  3. web端 复合控件 响应回发
  4. Redis 究竟适不适合当队列来用?
  5. 演义群侠传(五)【素材方式MC or SpriteSheet】
  6. 学习Spring(一) -- 配置Spring
  7. multi-camera项目学习
  8. 6748如何设置edma为事件触发方式_全面分析前端的网络请求方式
  9. hadoop组件及其作用
  10. Gif表情包在线制作小程序
  11. 用C语言开发一个BT下载软件(一) ------ BitTorrent协议 -2
  12. n1怎么进入线刷模式_中国移动N1 M821线刷刷机教程_移动M821线刷包_救砖包
  13. hdu 1864 最大报销额
  14. 技术人员的一点产品思维思考
  15. 自定义可自由移动的浮窗
  16. Matplotlib坐标轴格式
  17. 首先dns服务器自动改变 萤石云,设备的DNS在哪里改 ?
  18. 【HTML系列】之HTML字体颜色设置
  19. MacOS 系统 文件夹解析
  20. phpexcel mysql 导出_PHPEXCEL结合MYSQL+PHP实现数据库数据导出EXCEL实例

热门文章

  1. 小红书创始人瞿芳回应裁员风波:战略部署清晰 人员翻倍
  2. 模拟实现memcpy、memmove函数
  3. 基于 Laravel 的模块化开发框架 Notadd RC1 fix1 发布
  4. CONSUL install 和启动
  5. 数据库(学习整理)----5--Oracle常用的组函数
  6. set 集合容器实现元素的插入与中序排序
  7. 【FPGA】TestBench中关于@eachvec
  8. 第24周SDAI缓解能否预测远期RA骨破坏受抑制
  9. linux定时任务Crond之服务器同步时间05
  10. idea拉出Output窗口和还原窗口