算法学习之---蓄水池抽样问题

问题:如何在无限大的数据流中随机选取K个数据,保证当前遍历过的i个元素中每一个元素被选中的概率均为 k/i?从而对于n个元素,每个元素被选中的概率均为 k/n。

解:对于前k个元素,我们直接选中放入一个虚拟的蓄水池中,对于第 k+1 个元素,我们用 k/(k+1) 的概率选中它,一旦选中了,就随机替换掉蓄水池中的某一个元素,这样前 k 个被选中的元素在第 k+1 个元素到来时依然被选中的概率即为它不被替换掉的概率:

p = 1*(1-被替换掉的概率q)

q = k/(k+1) * 1/k               #即前k个元素中某个元素被替换时,某个被替换的概率为 1/k

这样 p = k/(k+1)

对于当前被遍历到的第 k+1 个元素,它被放到蓄水池中的概率就是它被选中的概率,即 k/(k+1),因为只要它被选中,就一定会替换掉蓄水池中的某个元素而留在水池中;

同理,对于第 m +1 个元素(m>>k)的到来,前m个元素被选中的概率均为 k/m,进行第 m+1 次选择时,前 m 个元素依然保留在蓄水池中的概率为:

p = k/m * { 1 - [ k/(m+1) * 1/k ] } = k/(m+1)            #即 p=之前在水池中的概率*(1 - 被第m+1个元素替换替换掉的概率 )

或者 p = k/m * { (m+1-k)/(m+1) + [ k/(m+1) * (k-1)/k ] } = k/(m+1)    #即 p=之前在水池中的概率*(第m+1个元素未被选中的概率 + 第m+1个元素被选中但是替换了其他k-1个元素的概率)

代码实现时,遍历到第 i 个元素时(i>k),可以直接生成一个 (1,i)之间的随机数 r ,如果 r<=k,那么就让当前第 i 个元素替换蓄水池中的第 r 个元素即可,把选中和替换哪一个元素融合为一步了,概率是不变的,即被选中的概率为 k/i,蓄水池中的 k 个元素每一个被替换的概率也为 1/k 。

Init : a reservoir with the size: k
        for i= k+1 to N
            R=random(1, i);
            if( R < k)
                 SWAP the R th value and i th value
       end for

参考:http://blog.csdn.net/lxmky/article/details/7951099

时间: 2024-10-11 00:00:00

算法学习之---蓄水池抽样问题的相关文章

MySTL:蓄水池抽样算法

给你一个长度为N的链表.N很大,但你不知道N有多大.你的任务是从这N个元素中随机取出k个元素.你只能遍历这个链表一次.你的算法必须保证取出的元素恰好有k个,且它们是完全随机的(出现概率均等). 这一题应该可以用来解决微信红包分配之类的那种问题,主要是概率的证明挺有意思. 1 #include <iostream> 2 #include <algorithm> 3 #include <time.h> 4 5 using namespace std; 6 7 typedef

洗牌算法与蓄水池抽样

今儿看到了,就在此记录一下吧. 洗牌算法 递归做法:先将1~n-1洗牌,然后取随机数k(0<k<n),并交换n与k,代码很简单: 1 int[] shuffle(int[] cards, int n){ 2 if(n == 1){ 3 return cards; 4 } 5 shuffle(cards, n-1); 6 int k = random(1, n-1); 7 swap(card[k], card[n]); 8 return card; 9 } 也可以转成非递归的: 1 void s

蓄水池抽样算法

问题定义: 给你一个长度为N的链表.N很大,但你不知道N有多大.你的任务是从这N个元素中随机取出k个元素.你只能遍历这个链表一次.你的算法必须保证取出的元素恰好有k个,且它们是完全随机的(出现概率均等). 蓄水池抽样算法: 该算法是针对从一个序列中随机抽取不重复的k个数,保证每个数被抽取到的概率为k/n这个问题而构建的.做法是: - 首先构建一个可放k个元素的蓄水池,将序列的前k个元素放入蓄水池中. 然后从第k+1个元素开始,以k/n的概率来决定该元素是否被替换到池子中. 当遍历完所有元素之后,

Reservoir Sampling - 蓄水池抽样算法

蓄水池抽样——<编程珠玑>读书笔记 382. Linked List Random Node 398. Random Pick Index         问题:如何随机从n个对象中选择一个对象,这n个对象是按序排列的,但是在此之前你是不知道n的值的.  思路:如果我们知道n的值,那么问题就可以简单的用一个大随机数rand()%n得到一个确切的随机位置,那么该位置的对象就是所求的对象,选中的概率是1/n. 但现在我们并不知道n的值,这个问题便抽象为蓄水池抽样问题,即从一个包含n个对象的列表S中

转-spark抽样之蓄水池抽样

1.蓄水池抽样算法(Reservoir Sampling) https://www.jianshu.com/p/7a9ea6ece2af 2.spark抽样之蓄水池抽样 https://blog.csdn.net/snaillup/article/details/69524931?utm_source=blogxgwz3 代码: /** * Reservoir sampling implementation that also returns the input size. * * @param

算法学习 - 表达树的建立(后缀表达式法),树的先序遍历,中序遍历,后序遍历

表达树就是根据后缀表达式来建立一个二叉树. 这个二叉树的每个叶子节点就是数,真祖先都是操作符. 通过栈来建立的,所以这里也会有很多栈的操作. 树的先序遍历,中序遍历,后序遍历的概念我就不讲了,不会的自行百度,不然也看不懂我的代码. 下面是代码: // // main.cpp // expressionTree // // Created by Alps on 14-7-29. // Copyright (c) 2014年 chen. All rights reserved. // #includ

我的算法学习之路

关于 严格来说,本文题目应该是我的数据结构和算法学习之路,但这个写法实在太绕口--况且CS中的算法往往暗指数据结构和算法(例如算法导论指的实际上是数据结构和算法导论),所以我认为本文题目是合理的. 这篇文章讲了什么? 我这些年学习数据结构和算法的总结. 一些不错的算法书籍和教程. 算法的重要性. 初学 第一次接触数据结构是在大二下学期的数据结构课程.然而这门课程并没有让我入门--当时自己正忙于倒卖各种MP3和耳机,对于这些课程根本就不屑一顾--反正最后考试划个重点也能过,于是这门整个计算机专业本

算法学习三阶段

?? 第一阶段:练经典经常使用算法,以下的每一个算法给我打上十到二十遍,同一时候自己精简代码, 由于太经常使用,所以要练到写时不用想,10-15分钟内打完,甚至关掉显示器都能够把程序打 出来. 1.最短路(Floyd.Dijstra,BellmanFord) 2.最小生成树(先写个prim,kruscal 要用并查集,不好写) 3.大数(高精度)加减乘除 4.二分查找. (代码可在五行以内) 5.叉乘.判线段相交.然后写个凸包. 6.BFS.DFS,同一时候熟练hash 表(要熟,要灵活,代码要

周总结(2017.2.16):第一周算法学习。

周总结:算法学习总结之DFS和BFS 一:DFS算法 目的:达到被搜索结构的叶节点. 定义:假定给定图G的初态是所有的定点都没有访问过,在G中任选一定点V为初始出发点,首先访问出发点并标记,然后依次从V出发搜索V的每个相邻点W,若W未曾出现过,则对W进行深度优先遍历(DFS),知道所有和V有路径相通的定点被访问. 如果从V0开始寻找一条长度为4的路径的话: 思路步骤: 先寻找V0的所有相邻点:dis{v1,v2,v3},V1没有访问过,所以对V1进行深度遍历并将V1标记为访问过,此时路径长度为1