遍歷n個元素取出等概率隨機取出其中之一元素


問題描述

1.一個文件中含有n個元素,只能遍歷一遍,要求等概率隨機取出其中之一。

    先講一個例子,5個人抽5個簽,只有一個簽意味着“中簽”,輪流抽簽,那么這種情況,估計這5個人都不會有異議,都覺得這種方法是公平的,這確實也是公平的,“抓鬮”的方法已經有很長的歷史了,要是不公平的話老祖先們就不干了。

    或許有人覺得先抓的人中簽的概率會大一些,因為要是前面的人中了,后面的中簽概率就是0了,也可能有人會覺得后面抓的人更有優勢,因為前面拿去了不中的簽,后面中簽的概率就大,那么我們就計算一下吧。

問題分析

    第一個人中簽的概率是1/5,

    第二個人中簽的情況只能在第一個人未中時才有可能,所以他中的概率是4/5 X 1/4 = 1/5(4/5表示第一個人未中,1/4表示在剩下的4個簽里中簽的概率),所以,第二個人最終的中簽概率也是1/5,

    同理,第三個人中簽的概率為:第一個人未中的概率X 第二個人未中的概率X第三個人中的概率,即為:4/5 X 3/4 X 1/3 = 1/5,

    一樣的可以求出第四和第五個人的概率都為1/5,也就是說先后順序不影響公平性。

    說這個問題是要說明這種前后有關聯的事件的概率計算的方式,我們回到第1個問題。前幾天我的一個同學電面百度是被問到這個問題,他想了想回答說,依次遍歷,遇到每一個元素都生成一個隨機數作為標記,如果當前生成的隨機數大於為之前保留的元素生成的隨機數就替換,這樣操作直到文件結束。

    但面試官問到:如果生成的隨機數和之前保留的元素的隨機數一樣大的話,要不要替換呢?

    你也許會想,一個double的范圍可以是-1.79E+308 ~ +1.79E+308,要讓兩個隨機生成的double相等的概率不是一般的微乎其微啊!但計算機世界里有條很讓人傷心的“真理”:可能發生的事件,總會發生!

    那我們遇到這種情況,是換還是不換?To be or not to be, that’s a question!

    就好比,兩個人百米賽跑,測出來的時間一樣,如果只能有一個人得冠軍的話,對於另一個人始終是不公平的,那么只能再跑一次,一決雌雄了!

我的策略

    下面,說一個個人認為比較滿足要求的選取策略:

  •  順序遍歷,當前遍歷的元素為第L個元素,變量e表示之前選取了的某一個元素,此時生成一個隨機數r,如果r%L == 0(當然0也可以是0~L-1中的任何一個,概率都是一樣的), 我們將e的值替換為當前值,否則掃描下一個元素直到文件結束。

    你要是給面試官說明了這樣一個策略后,面試官百分之一千會問你這樣做是等概率嗎?那我們來證明一下。

證明

     在遍歷到第1個元素的時候,即L為1,那么r%L必然為0,所以e為第一個元素,p=100%,

    遍歷到第2個元素時,L為2,r%L==0的概率為1/2, 這個時候,第1個元素不被替換的概率為1X(1-1/2)=1/2,

     第1個元素被替換,也就是第2個元素被選中的概率為1/2 = 1/2,你可以看到,只有2時,這兩個元素是等概率的機會被選中的。

    繼續,遍歷到第3個元素的時候,r%L==0的概率為1/3,前面被選中的元素不被替換的概率為1/2 X (1-1/3)=1/3,前面被選中的元素被替換的概率,即第3個元素被選中的概率為1/3

    歸納法證明,這樣走到第L個元素時,這L個元素中任一被選中的概率都是1/L,那么走到L+1時,第L+1個元素選中的概率為1/(L+1), 之前選中的元素不被替換,即繼續被選中的概率為1/L X ( 1-1/(L+1) ) = 1/(L+1)。證畢。

    也就是說,走到文件最后,每一個元素最終被選出的概率為1/n, n為文件中元素的總數。好歹我們是一個技術博客,看不到一丁點代碼多少有點遺憾,給出一個選取策略的偽代碼,如下:

偽代碼

Element RandomPick(file):

Int length = 1;

While( length <= file.size )

   If( rand() % length == 0)

        Picked = File[length];

   Length++;

Return picked


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com