現在有一組數,不知道這組數的總量有多少,請描述一種演算法能夠在這組資料中隨機抽取k個數,使得每個數被取出來的概率相等。如果這組數有n個,那麼每個數字取到的概率就是k/n,但是這個問題的難點在於不知道這組數的總數,也就是不知道n,那麼該怎麼計算每個數取到的概率呢?
游泳池(蓄水池)大家都不陌生,有些游泳池中的水是活的,有入水管也有出水管,那麼和泳池體積相當的水流過之後,是不是泳池中所有的水都會被替換呢?當然不是,有的水在泳池中可能會存留很久,有的可能剛進去就流走了。仿照這種現象,蓄水池抽樣演算法誕生了,蓄水池演算法的關鍵在於保證流入蓄水池的水和已經在池中的水以相同的概率留存在蓄水池中。並且蓄水池演算法可以在不預先知道總量的情況下,在時間複雜度o(n)的情況下,來解決這類取樣問題。
這一部分涉及公式,為了保證效果直接貼了圖過來。
接下來嘗試用python實現一下蓄水池演算法,由於蓄水池演算法是在事先不知道總量的情況下抽樣的,所以定義乙個方法來接收單個元素,並且把這個方法放在類中,以持有取樣後的資料。
import random
class reservoirsample(object):
def __init__(self, size):
self._size = size
self._counter = 0
self._sample =
def feed(self, item):
self._counter += 1
# 第i個元素(i <= k),直接進入池中
if len(self._sample) < self._size:
return self._sample
# 第i個元素(i > k),以k / i的概率進入池中
rand_int = random.randint(1, self._counter)
if rand_int <= self._size:
self._sample[rand_int - 1] = item
return self._sample
接下來實現乙個測試用例驗證實現的演算法是否正確,既然是隨機抽樣,無法通過單詞測試來驗證是否正確,所以通過多次執行的方式來驗證,比如從1-10裡隨機取樣3個數,然後執行10000次取樣,如果演算法正確,最後結果中1-10被取樣的次數應該是相同的,都是3000上下。
import unittest
from collections import counter
from reservoir_sample import reservoirsample
class testmain(unittest.testcase):
def test_reservoir_sample(self):
samples =
for i in range(10000):
sample =
rs = reservoirsample(3)
for item in range(1, 11):
sample = rs.feed(item)
samples.extend(sample)
r = counter(samples)
print(r)
if __name__ == '__main__':
unittest.main()
輸出的結果如下
counter()
上面輸出了每個數字被取樣到的次數,通過圖表可以清晰的看到分布情況
可以看出蓄水池演算法對於隨機抽樣還是非常適合的,每個元素的抽樣概率都相同。
蓄水池演算法
參考文章 問題定義 給你乙個長度為n的鍊錶。n很大,但你不知道n有多大。你的任務是從這n個元素中隨機取出k個元素。你只能遍歷這個鍊錶一次。你的演算法必須保證取出的元素恰好有k個,且它們是完全隨機的 出現概率均等 求解蓄水池抽樣演算法 該演算法是針對從乙個序列中隨機抽取不重複的k個數,保證每個數被抽取...
蓄水池演算法
適用情況 從n個數中等概率隨機取出k個數,n很大,k也很大 n不固定增量型 內容 當i屬於1 k i入池 當i k i以概率k i決定是否進入池,1 k概率剔除池中乙個數 證明 1 當i 當k個數時,i留下概率 1 當k 1個數時,i被淘汰的概率 1 k k k 1 1 k 1 i留下概率 1 1 ...
蓄水池演算法
在乙個未知的池子裡選1個數字,讓他們被選擇概率一致。假設n的時候,前n個數字被選擇的概率都是1n 當為n 1時候,當前n 1這個數,我們選擇的概率是1n 1,其餘數字的概率是nn 1,那麼一共有n個數字的概率是1n 所以 n 1n 1 n 1 1n 1 具體演算法 使用的方式是i random.ra...