蓄水池算法

今天在網(wǎng)上看題目時(shí)梯轻,發(fā)現(xiàn)一個(gè)十分有趣的算法褪迟,叫蓄水池算法(Reservoir Sampling)彪杉,牽扯到一點(diǎn)概率論問(wèn)題。

題目:給出一個(gè)數(shù)據(jù)流牵咙,這個(gè)數(shù)據(jù)流的長(zhǎng)度很大或者未知。并且對(duì)該數(shù)據(jù)流中數(shù)據(jù)只能訪問(wèn)一次攀唯。請(qǐng)寫(xiě)出一個(gè)隨機(jī)選擇算法洁桌,使得數(shù)據(jù)流中所有數(shù)據(jù)被選中的概率相等。

抽象:從n中取出k個(gè)數(shù)侯嘀,n未知大小另凌,保證最后n中每個(gè)元素被抽取的概率一樣為k/n

做法:假設(shè)我們從3個(gè)數(shù){1,2,3}中取一個(gè)數(shù)戒幔,那么就要求每個(gè)數(shù)被抽取的概率為1/3吠谢,我們先讀取前2個(gè)數(shù){1,2},我們以1/2的概率選取其中一個(gè)數(shù)诗茎,加入選擇{1}工坊,接下來(lái)讀取數(shù)字{3},因?yàn)橐竺總€(gè)數(shù)被選取的概率為1/3敢订,因此我們以1/3的概率選取數(shù)字{3}王污,2/3的概率選取數(shù)字{1},那么最終數(shù)字1被選取的概率是2/3 * 1/2 = 1/3楚午,同理數(shù)字{2}被選取的概率也是1/3昭齐。

將上述做法n個(gè)數(shù)選擇1個(gè)數(shù),每次要讀取第n個(gè)數(shù)的時(shí)候矾柜,以1/n的概率保留該數(shù)阱驾,以(n-1)/n的概率保留前面n-1個(gè)數(shù)選取出來(lái)的1個(gè)數(shù)就谜。

再推廣到從n個(gè)數(shù)中選取k個(gè)數(shù),假設(shè)讀取到第n個(gè)數(shù)時(shí)(n>=k)里覆,以k/n的概率保留概述丧荐,以(n-k)/n的概率保留前n-1個(gè)中選取出來(lái)的k個(gè)數(shù)。

證明:使用上述步驟租谈,從n中讀取k個(gè)數(shù)篮奄,在讀取n個(gè)元素后,n中每一個(gè)被保留下來(lái)的概率都是k/n割去。假設(shè)n = k + i窟却,那么算法證明的就是第i輪選取中,前k+i個(gè)數(shù)每個(gè)數(shù)被保留的概率為k/(k+i)呻逆,其中( 0 <= i <= n - k)夸赫。
用數(shù)學(xué)歸納法來(lái)證明:

  1. 當(dāng)i=0是,每個(gè)數(shù)字被選取的概率為k/(k+0)=1咖城,正確茬腿;
  2. 假設(shè)當(dāng)i-1輪時(shí),結(jié)論成立宜雀,即前k+i-1中每個(gè)數(shù)被保留的概率為k/(k+i-1)切平;
  3. i輪,因?yàn)槲覀円?code>k/(k+i)的概率選取第k+i個(gè)數(shù)辐董,因此其概率為k/(k+i)悴品,正確;對(duì)于前k+i-1個(gè)數(shù)中的x简烘,其被保留的概率由兩部分組成:
    ①:第k+i個(gè)數(shù)沒(méi)有被選取到苔严,則x被選取的概率是:i/(k+i) * k/(k+i-1),其中k/(k+i-1)2中假設(shè)的條件孤澎,即x在前k+i-1中被保留的概率届氢;
    ②:第k+i個(gè)數(shù)被選取到,要替換前k+i-1中的數(shù)覆旭,那么x不被替換的概率是:k/(k+i) * k/(k+i-1) * (k-1)/k退子,k/(k+i-1)同①,k-1/k是指型将,在被選取為k個(gè)數(shù)之后絮供,不被替換的概率。
    因此茶敏,總概率為(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個(gè)數(shù)讀取前k個(gè)數(shù)惊搏,保存在集合A中贮乳;
  2. 從第i個(gè)數(shù)開(kāi)始(k<=i<=n)忧换,每次以k/i的概率選擇是否保留概述,若保留向拆,將隨機(jī)替換k中任一數(shù)亚茬;
  3. 重復(fù)2,直到結(jié)束浓恳。

Leetcode有關(guān)于蓄水池算法的兩道題刹缝,來(lái)看看怎么應(yīng)用:

  1. Linked List Random Node
    題目要求去鏈表中的任一節(jié)點(diǎn),使得節(jié)點(diǎn)被選取的概率相等颈将,因此我們利用蓄水池法梢夯,遍歷節(jié)點(diǎn);
# 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
    題目要求獲取指定數(shù)字序號(hào)晴圾,每個(gè)數(shù)字在該序列中的序號(hào)被獲取的概率相等颂砸,題目有個(gè)硬性條件,n很大死姚,剛好蓄水池法可以用來(lái)解決人乓。
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:      # 遍歷列表,當(dāng)該值與目標(biāo)值相同時(shí)都毒,才進(jìn)入蓄水池算法
                count += 1
                if random.random() <= 1.0/count:      #滿足概率為1/i
                    select_index = index

        return select_index

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末色罚,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子账劲,更是在濱河造成了極大的恐慌保屯,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涤垫,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡竟终,警方通過(guò)查閱死者的電腦和手機(jī)蝠猬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)统捶,“玉大人榆芦,你說(shuō)我怎么就攤上這事〈瘢” “怎么了匆绣?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)什黑。 經(jīng)常有香客問(wèn)我崎淳,道長(zhǎng),這世上最難降的妖魔是什么愕把? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任拣凹,我火速辦了婚禮森爽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘嚣镜。我一直安慰自己爬迟,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布菊匿。 她就那樣靜靜地躺著付呕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪跌捆。 梳的紋絲不亂的頭發(fā)上徽职,一...
    開(kāi)封第一講書(shū)人閱讀 51,727評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音疹蛉,去河邊找鬼活箕。 笑死,一個(gè)胖子當(dāng)著我的面吹牛可款,可吹牛的內(nèi)容都是我干的育韩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼闺鲸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼筋讨!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起摸恍,我...
    開(kāi)封第一講書(shū)人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤悉罕,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后立镶,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體壁袄,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了搏色。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片咪鲜。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖栈顷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情嵌巷,我是刑警寧澤萄凤,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站搪哪,受9級(jí)特大地震影響靡努,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一颤难、第九天 我趴在偏房一處隱蔽的房頂上張望神年。 院中可真熱鬧,春花似錦行嗤、人聲如沸已日。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)飘千。三九已至,卻和暖如春栈雳,著一層夾襖步出監(jiān)牢的瞬間护奈,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工哥纫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留霉旗,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓蛀骇,卻偏偏與公主長(zhǎng)得像厌秒,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子擅憔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 這個(gè)不錯(cuò)分享給大家鸵闪,從扣上看到的,就轉(zhuǎn)過(guò)來(lái)了 《電腦專業(yè)英語(yǔ)》 file [fail] n. 文件暑诸;v. 保存文...
    麥子先生R閱讀 6,566評(píng)論 5 24
  • 我的女兒今年上二年級(jí)了个榕,雖然沒(méi)有正式要求寫(xiě)作文篡石,可是已經(jīng)有很多寫(xiě)話的作業(yè)。 每每看到女兒干癟的文字諸如:“今天天氣...
    若水瑤閱讀 264評(píng)論 7 4
  • 嘿;-)西采,這是我這里的天氣凰萨,你那里呢?
    摩卡芝士魚(yú)閱讀 161評(píng)論 0 0
  • 見(jiàn)字如晤苛让。 落筆開(kāi)始寫(xiě)這段文字的時(shí)候,是17號(hào)晚23:54湿诊。一個(gè)多小時(shí)前狱杰,和你道過(guò)別。 去年9月厅须,我們一起從北京出...
    劈柴捌哥閱讀 230評(píng)論 0 1
  • 歸來(lái)歌 白衣素錦歸來(lái),落馬抖衣無(wú)人错沽。 抬步不知何向簿晓,思憶已飛過(guò)往。 人生歌 苦思今生何往千埃,瑣事于我無(wú)干憔儿。 行于人世...
    風(fēng)中的追隨閱讀 385評(píng)論 0 1