蓄水池算法

今天在网上看题目时,发现一个十分有趣的算法,叫蓄水池算法(Reservoir Sampling),牵扯到一点概率论问题。

题目:给出一个数据流,这个数据流的长度很大或者未知。并且对该数据流中数据只能访问一次。请写出一个随机选择算法,使得数据流中所有数据被选中的概率相等。

抽象:从n中取出k个数,n未知大小,保证最后n中每个元素被抽取的概率一样为k/n

做法:假设我们从3个数{1,2,3}中取一个数,那么就要求每个数被抽取的概率为1/3,我们先读取前2个数{1,2},我们以1/2的概率选取其中一个数,加入选择{1},接下来读取数字{3},因为要求每个数被选取的概率为1/3,因此我们以1/3的概率选取数字{3}2/3的概率选取数字{1},那么最终数字1被选取的概率是2/3 * 1/2 = 1/3,同理数字{2}被选取的概率也是1/3

将上述做法n个数选择1个数,每次要读取第n个数的时候,以1/n的概率保留该数,以(n-1)/n的概率保留前面n-1个数选取出来的1个数。

再推广到从n个数中选取k个数,假设读取到第n个数时(n>=k),以k/n的概率保留概述,以(n-k)/n的概率保留前n-1个中选取出来的k个数。

证明:使用上述步骤,从n中读取k个数,在读取n个元素后,n中每一个被保留下来的概率都是k/n。假设n = k + i,那么算法证明的就是第i轮选取中,前k+i个数每个数被保留的概率为k/(k+i),其中( 0 <= i <= n - k)
用数学归纳法来证明:

  1. i=0是,每个数字被选取的概率为k/(k+0)=1,正确;
  2. 假设当i-1轮时,结论成立,即前k+i-1中每个数被保留的概率为k/(k+i-1)
  3. i轮,因为我们以k/(k+i)的概率选取第k+i个数,因此其概率为k/(k+i),正确;对于前k+i-1个数中的x,其被保留的概率由两部分组成:
    ①:第k+i个数没有被选取到,则x被选取的概率是:i/(k+i) * k/(k+i-1),其中k/(k+i-1)2中假设的条件,即x在前k+i-1中被保留的概率;
    ②:第k+i个数被选取到,要替换前k+i-1中的数,那么x不被替换的概率是:k/(k+i) * k/(k+i-1) * (k-1)/kk/(k+i-1)同①,k-1/k是指,在被选取为k个数之后,不被替换的概率。
    因此,总概率为(i/(k+i) * k/(k+i-1)) + (k/(k+i) * k/(k+i-1) * (k-1)/k) = (k/(k+i-1)) * (i/(k+i) + (k-1)/(k+i)) = k/k+i

得证。

具体算法步骤:

  1. n个数读取前k个数,保存在集合A中;
  2. 从第i个数开始(k<=i<=n),每次以k/i的概率选择是否保留概述,若保留,将随机替换k中任一数;
  3. 重复2,直到结束。

Leetcode有关于蓄水池算法的两道题,来看看怎么应用:

  1. Linked List Random Node
    题目要求去链表中的任一节点,使得节点被选取的概率相等,因此我们利用蓄水池法,遍历节点;
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
import random

class Solution:

    def __init__(self, head):
        """
        @param head The linked list's head.
        Note that the head is guaranteed to be not null, so it contains at least one node.
        :type head: ListNode
        """
        self.head = head
        
    def getRandom(self):
        """
        Returns a random node's value.
        :rtype: int
        """
        node = self.head
        select_node = node
        i = 1
        while node.next:
            i += 1
            node = node.next
            if random.random() <= 1.0/i:      # 保证被选取的概率为`1/i`
                select_node = node
        return select_node.val
  1. Random Pick Index
    题目要求获取指定数字序号,每个数字在该序列中的序号被获取的概率相等,题目有个硬性条件,n很大,刚好蓄水池法可以用来解决。
import random

class Solution:

    def __init__(self, nums):
        """
        :type nums: List[int]
        """
        self.nums = nums        

    def pick(self, target):
        """
        :type target: int
        :rtype: int
        """
        count = 0
        select_index = 0
        for index,num in enumerate(self.nums):
            if num == target:      # 遍历列表,当该值与目标值相同时,才进入蓄水池算法
                count += 1
                if random.random() <= 1.0/count:      #满足概率为1/i
                    select_index = index

        return select_index

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容

  • 这个不错分享给大家,从扣上看到的,就转过来了 《电脑专业英语》 file [fail] n. 文件;v. 保存文...
    麦子先生R阅读 6,543评论 5 24
  • 我的女儿今年上二年级了,虽然没有正式要求写作文,可是已经有很多写话的作业。 每每看到女儿干瘪的文字诸如:“今天天气...
    若水瑶阅读 261评论 7 4
  • 嘿;-),这是我这里的天气,你那里呢?
    摩卡芝士鱼阅读 158评论 0 0
  • 见字如晤。 落笔开始写这段文字的时候,是17号晚23:54。一个多小时前,和你道过别。 去年9月,我们一起从北京出...
    劈柴捌哥阅读 228评论 0 1
  • 归来歌 白衣素锦归来,落马抖衣无人。 抬步不知何向,思忆已飞过往。 人生歌 苦思今生何往,琐事于我无干。 行于人世...
    风中的追随阅读 380评论 0 1