問題定義: 算法
給你一個長度爲N的鏈表。N很大,但你不知道N有多大。你的任務是從這N個元素中隨機取出k個元素。你只能遍歷這個鏈表一次。你的算法必須保證取出的元素剛好有k個,且它們是徹底隨機的(出現機率均等)。 dom
蓄水池抽樣算法: spa
該算法是針對從一個序列中隨機抽取不重複的k個數,保證每一個數被抽取到的機率爲k/n這個問題而構建的。作法是: -
首先構建一個可放k個元素的蓄水池,將序列的前k個元素放入蓄水池中。
而後從第k+1個元素開始,以k/n的機率來決定該元素是否被替換到池子中。 當遍歷完全部元素以後,就能夠獲得隨機挑選出的k個元素。複雜度爲O(n). it
其僞代碼以下: 遍歷
Init : a reservoir with the size: kfor i= k+1 to N 鏈表
M=random(1, i);
if( M < k)
SWAP the Mth value and ith value
end for co
證實每一個數被取到的機率爲k/n: block
對於第i個數(i<k),在前k步被選中的機率是1, 從第k+1步開始,i不被選中的機率爲k/k+1,那麼讀到第n個數時, 第i個數(i<k)被選中的機率 = 被選中的機率 * 之後每一步都不被換走的機率,即
1 * k/k+1 * k+1/k+2 …n-1/n = k/n background
對於第j個數(j>=k)被選中的機率爲: 在他出現時被選中的機率 * 在他出現之後不被換走的機率,即:
k/j * j /j+1 。。。n-1/n = k/n
綜上得證。