水塘抽样是一系列的随机算法,其目的在于从包含n个项目的集合S中选取k个样本,其中n为一很大或未知的数量,尤其适用于不能把所有n个项目都存放到主内存的情况。

在高德纳的计算机程序设计艺术中,有如下问题:可否在一未知大小的集合中,随机取出一元素?。或者是Google面试题: I have a linked list of numbers of length N. N is very large and I don’t know in advance the exact value of N. How can I most efficiently write a function that will return k completely random numbers from the list(中文简化的意思就是:在不知道文件总行数的情况下,如何从文件中随机的抽取一行?)。两题的核心意思都是在总数不知道的情况下如何等概率地从中抽取一行?即是说如果最后发现文字档共有N行,则每一行被抽取的概率均为1/N?

我们可以:定义取出的行号为choice,第一次直接以第一行作为取出行 choice ,而后第二次以二分之一概率决定是否用第二行替换 choice ,第三次以三分之一的概率决定是否以第三行替换 choice ……,以此类推。由上面的分析我们可以得出结论,在取第n个数据的时候,我们生成一个0到1的随机数p,如果p小于1/n,保留第n个数。大于1/n,继续保留前面的数。直到数据流结束,返回此数,算法结束。

问题一

首先考虑k为1的情况,即:给定一个长度很大或者长度未知数据流,限定对每个元素只能访问一次,写出一个随机选择算法,使得所有元素被选中的概率相等

设当前读取的是第n个元素,采用归纳法分析如下:

n = 1 时,只有一个元素,直接返回即可,概率为1。

n = 2 时,需要等概率返回前两个元素,显然概率为1/2。可以生成一个0~1之间的随机数p,p < 0.5 时返回第一个,否则返回第二个。

n = 3 时,要求每个元素返回的概率为1/3。注意此时前两个元素留下来的概率均为1/2。做法是:生成一个0~1之间的随机数,若<1/3,则返回第三个,否则返回上一步留下的那个。元素1和2留下的概率均为:1/2 * (1 - 1/3) = 1/3,即上一步留下的概率乘以这一步留下(即元素3不留下)的概率。

假设 n = m 时,前n个元素留下的概率均为:1/n = 1/m;

那么 n = m+1 时,生成0~1之间的随机数并判断是否<1/(m+1),若是则留下元素m+1,否则留下上一步留下的元素。这样一来,元素m+1留下的概率为1/(m+1),前m个元素留下来的概率均为:1/m * (1 - 1/(m+1)) = 1/(m+1),也就是1/n。

综上可知,算法成立。

问题二

将问题一中的条件变为,k为任意整数的情况,即要求最终返回的元素有k个,这就是水塘抽样(Reservoir Sampling)问题。要求是:取到第n个元素时,前n个元素被留下的几率相等,即k/n。

算法同上面思路类似,将1/n换乘k/n即可。在取第n个数据的时候,我们生成一个0到1的随机数p,如果p小于k/n,替换池中任意一个为第n个数。大于k/n,继续保留前面的数。直到数据流结束,返回此k个数。但是为了保证计算机计算分数额准确性,一般是生成一个0到n的随机数,跟k相比,道理是一样的。

同样采用归纳法来分析:

初始情况 n <= k:此时每个元素留下的概率均为1。

当 n = k+1 时,第k+1个元素留下的概率为k/(k+1),前k个元素留下的概率均为:k/k * (1 - k/(k+1) * 1/k) = k/(k+1),即上一步留下的概率乘以这一步留下的概率。

假设 n = m 时,每个元素留下的概率均为 k/n = k/m。

那么,当 n = m+1 时,第m+1个元素留下的概率为1/(m+1),前m个元素留下的概率均为:k/m * (1 - k/(m+1) * 1/k) = k/(m+1),其中:k/m为上一步留下来的概率,k/(m+1) * 1/k 为这一步不能留下来的概率(第m+1个留下来,同时池中一个元素被踢出的概率)。

综上可知,算法成立。

总结

前提:不知道n的大小。

  • 对于从n个数中取1个数:

    将第一个取出来放到数组中,然后遍历后续第n个数,每个数有1/n的概率去替换数组中的数。
  • 对于从n个数中取k个数:

    将前k个数取出来放到大小为k的数组中,然后遍历后续第n个数,每个数有k/n的概率去替换数组中的数,同时数组中的数每次有1/k的概率被替换。

伪代码如下:

//stream代表数据流
//reservoir代表返回长度为k的池塘 //从stream中取前k个放入reservoir;
for ( int i = 1; i < k; i++)
reservoir[i] = stream[i];
for (i = k; stream != null; i++) {
p = random(0, i);
if (p < k) reservoir[p] = stream[i];
return reservoir;

水塘抽样(Reservoir Sampling)问题

水塘抽样(Reservoir Sampling)问题的更多相关文章

  1. 水库抽样Reservoir Sampling(蓄水池问题)

      知识复习 空间亚线性算法:由于大数据算法中涉及到的数据是海量的,数据难以放入内存计算,所以一种常用的处理办法是不对全部数据进行计算,而只向内存里放入小部分数据,仅使用内存中的小部分数据,就可以得到 ...

  2. 68. 蓄水池抽样(Reservoir Sampling)

    [本文链接] http://www.cnblogs.com/hellogiser/p/reservoir-sampling.html 问题起源于编程珠玑Column 12中的题目10,其描述如下: H ...

  3. Spark MLlib之水塘抽样算法(Reservoir Sampling)

    1.理解 问题定义可以简化如下:在不知道文件总行数的情况下,如何从文件中随机的抽取一行? 首先想到的是我们做过类似的题目吗?当然,在知道文件行数的情况下,我们可以很容易的用C运行库的rand函数随机的 ...

  4. Reservoir Sampling - 蓄水池抽样

    问题起源于编程珠玑Column 12中的题目10,其描述如下: How could you select one of n objects at random, where you see the o ...

  5. Reservoir Sampling - 蓄水池抽样问题

    问题起源于编程珠玑Column 12中的题目10,其描述如下: How could you select one of n objects at random, where you see the o ...

  6. 【算法34】蓄水池抽样算法 (Reservoir Sampling Algorithm)

    蓄水池抽样算法简介 蓄水池抽样算法随机算法的一种,用来从 N 个样本中随机选择 K 个样本,其中 N 非常大(以至于 N 个样本不能同时放入内存)或者 N 是一个未知数.其时间复杂度为 O(N),包含 ...

  7. 【数据结构与算法】蓄水池抽样算法(Reservoir Sampling)

    问题描述 给定一个数据流,数据流长度 N 很大,且 N 直到处理完所有数据之前都不可知,请问如何在只遍历一遍数据(O(N))的情况下,能够随机选取出 m 个不重复的数据. 比较直接的想法是利用随机数算 ...

  8. 蓄水池抽样算法 Reservoir Sampling

    2018-03-05 14:06:40 问题描述:给出一个数据流,这个数据流的长度很大或者未知.并且对该数据流中数据只能访问一次.请写出一个随机选择算法,使得数据流中所有数据被选中的概率相等. 问题求 ...

  9. [大牛翻译系列]Hadoop(7)MapReduce:抽样(Sampling)

    4.3 抽样(Sampling) 用基于MapReduce的程序来处理TB级的数据集,要花费的时间可能是数以小时计.仅仅是优化代码是很难达到良好的效果. 在开发和调试代码的时候,没有必要处理整个数据集 ...

随机推荐

  1. Ubuntu pkg_resources.DistributionNotFound: The 'Scrapy==1.0.3' distribution was not found and is required by the application

    在Ubuntu16.0.4上安装scrapy,参考:Ubuntu16.04安装Scrapy命令 都安装成功后,import scrapy也都没出错,但是在终端输入:scrapy 命令时出现错误: Fi ...

  2. WinForm timer 控件

    timer 控件:按用户定义的时间间隔引发的事件 属性: Enabled 是否启用: Interval 事件发生的事件间隔,单位是毫秒 事件只有一个:Tick 事件经过指定的时间间隔发生 打开一个窗口 ...

  3. BZOJ.3928.[CERC2014]Outer space invaders(区间DP)

    BZOJ3928 双倍经验BZOJ4048 Codeforces GYM100543 L \(Description\) \(Solution\) 考虑出现时间在\([l,r]\)内的敌人,设最远的敌 ...

  4. Java并发程序设计(十)设计模式与并发之Future模式

    设计模式与并发之Future模式 核心思想:异步调用. /** * @author: Tang Jiujia * @version: 2017/9/18 0018 15:22 */ public in ...

  5. python——描述符

    本文主要介绍描述符的定义,个人的一些理解:什么是数据描述符:什么是非数据描述符:描述符的检测等.希望看完这篇文章后,你对描述符有了更清晰的认识.知道怎么判断一个对象是不是描述符,知道如果定义一个描述符 ...

  6. 不涉及框架纯java实现将图片裁成圆形

    package com.wtsrui.utils;import java.awt.Color;  import sun.misc.BASE64Encoder;import java.awt.Graph ...

  7. JS Range使用整理

    1.获取用户网页选中内容 <p>4月13日消息,据台湾媒体报道,32岁的孙燕姿(Sng Ee Tze)和后天将满34岁的荷兰籍印度尼西亚男友纳迪姆(Nadim Van Der Ros)交往 ...

  8. PHP的unset究竟会不会释放内存?

    首先让我们看一个例子 var_dump(memory_get_usage());    $a = "laruence";    var_dump(memory_get_usage( ...

  9. XMR恶意挖矿脚本处理笔记

    一.登录 攻击者如何登录系统未能查出,所有日志已被清除.为防万一,把系统中没用的用户都删掉并修改其他用户密码. 二.被攻击后的表象 1.服务器资源被大量占用,资源占用率飙升: 2.服务器所有JS文件被 ...

  10. 通过action传过来的值在option获取进行验证

    通过action传过来的值在option获取进行验证的方法: for(var i=0;i<document.getElementById("ufacilityType").o ...