二 leetcode之蓄水池抽樣

2021-10-12 07:29:21 字數 3195 閱讀 5171

5.優化後的**實現

注:引用資料

作者鏈結

handspeaker

up主:五點七邊

前幾天在leetcode中刷題遇到一道題,398:隨機數索引,題目描述如下:

給定乙個可能含有重複元素的整數陣列,要求隨機輸出給定的數字的索引。 您可以假設給定的數字一定存在於陣列中。

注意:陣列大小可能非常大。 使用太多額外空間的解決方案將不會通過測試。

示例:int

nums =

newint

;solution solution =

newsolution

(nums)

;// pick(3) 應該返回索引 2,3 或者 4。每個索引的返回概率應該相等。

solution.

pick(3

);// pick(1) 應該返回 0。因為只有nums[0]等於1。

solution.

pick(1

);

我的思路是,使用乙個hashmap來儲存每個值對應的索引陣列,然後當呼叫pick方法的時候,隨機返回當前target對應索引陣列中的乙個值。雖然通過了,但是在空間複雜度上o(n2),時間複雜度上o(n).感覺不太合適,想做一些優化。下面是我的**,比較拙劣?:

/**

思路:遍歷給定陣列,使用對映結構來確定乙個元素的對應位置列表;之後從列表中隨機取索引

空間換時間

解法一:

**/class

solution

else}}

public

intpick

(int target)

}

因為我自己沒有想出來較好的演算法,只想到乙個不需要使用hashmap的解法,就是每次給定乙個target的時候,我提前定義好乙個陣列,用來儲存索引值,遍歷陣列,如果當前值==target,將下標位置加入陣列中,當遍歷完後,隨機從這個陣列中抽取乙個索引值。(因為思路和上述**類似,這裡不貼出**了)

分析:空間複雜度o(n) , 時間複雜度o(n)

偽**:

init indexs[

]for i in nums[

]if target = i

indexs add i

get indexs[random]

從網上發現這種問題有著一種經典的解決方法,那就是蓄水池抽樣(也可以稱作流水線抽樣)。這種題目有很多變種,例如:

1.從100000行資料中,隨機不重複抽取10行資料

2.從一本很厚的書中,隨機閱讀10頁(提前不曉得書有多少頁)

3.在網路傳輸中,隨機抓取1個包

可以從上面看到,這些問題都是蓄水池的變種提問,我們乙個乙個來分析:

對於第乙個問題來講,出現隨機關鍵字也就是要概率相同,要不然就不叫隨機了。那麼我們抽取到每一頁的概率是 10/100000。(這裡為了方便理解我舉個?:我去買彩票,已知今日的彩票一共100張,裡面10張是一等獎,那麼如果我今天買了一張彩票,我中一等獎的概率是多少?是10/100,別人中獎的概率呢?也是10/100。所以這個事就是完全隨機的。)所以,第乙個問題,我們只要使用[1,100000]中的資料進行rand抽取即可。

對於第二個問題和第三個問題來講,我們無法得到總資料的大小,所以無法使用第一種的思路,可能有人會說,我遍歷完一遍不就行了嗎,但是在一些場景下,遍歷完可能是乙個很大的工作量,另外不知道結束的情況下如何遍歷完呢?

所以我們需要乙個嶄新的思路,我們需要抽取每個資料的時候,保證概率是一致的,不管當前資料流結束與否,我們抽取的時候概率就是一致的。

高能地帶 (我看了好多資料才懂,反應有點慢?)

前提條件:

n(資料總量,未知)

t(需要獲取的數量)

i(當前讀取資料的位置)

以下思路均為倒推思路:

我們只要證明乙個結論:在不知道n大小的情況下,存在 0< i < n,得到 第 (i+1)個元素的概率是 t/(i+1),同時,得到前i個元素的概率也是t/(i+1);

情景1:現在我們從總數為1的資料量中抽取乙個,那麼概率是t/n = 1/1 ,也就是所有的元素抽取的概率一致-----成立

情景2:現在我們從總數為2的資料中隨機抽取乙個,那麼概率如何?首先我們保證抽取2的時候概率是t/n = 1/2,那麼對應抽取1的概率也是1/2。-----成立

情景3:現在我們從總數為3的資料中隨機抽取乙個,那麼概率如何?首先我們保證抽取3的時候概率是t/n=1/3,那麼對應抽取1的概率是如何,首先我們需要保證在第二次抽取的元素是1,才能保證第三次抽取的時候有可能抽取到1,因為第一次抽取1的概率是1/1,第二次抽取到1的概率是1/2,第三次抽取到1的概率是多少呢?答案是抽取到3並且替換掉1的概率的補集。也就是1-1/3 = 2/3 ,概率疊加相乘 = 1/3. 同理抽取到2的概率也是1/3.-----成立

情景1:現在我們從總數為2的資料量中抽取2個,一共兩個元素抽取兩個,那麼概率是一樣的-----成立

情景2:現在我們從總數為3的資料中隨機抽取2個,那麼概率如何?可以得出,在抽取前兩個元素的時候都是一樣的概率,因為2個裡面抽2個,肯定是概率一致的。百分百抽中,從第三個開始,抽取第三個概率是2/3 . 並且替換掉當前陣列中的任意乙個的概率是1/2,所以最後留下3的概率是2/3*1/2 = 1/3. 再來看如果留下的元素是1的概率:留下元素是1,那麼需要保證抽取元素3同時不被替換掉,替換掉的概率是1/2,那麼概率是2/3 * (1-1/2) = 1/3。元素2同理。-----成立

/**

解法二:使用蓄水池抽樣解法隨機抽取目標為target的下標,隨機抽取乙個

**/class

solution

public

intpick

(int target)}}

return res;

}public

static

void

main

(string[

] args)

throws interruptedexception );

while

(true)}

}

蓄水池 抽樣

蓄水池抽樣問題描述的是,在乙個無窮盡的樣本中,要求隨即抽取一些樣本,這些樣本被抽取到的概率必須保持一致。乙個蓄水池就可以理解為無窮大的樣本空間。解決方案就是蓄水庫抽樣 reservoid sampling 主要思想就是保持乙個集合,作為蓄水池,依次遍歷所有資料的時候以一定概率替換這個蓄水池中的數字。...

蓄水池抽樣

題目 要求從n個元素中隨機的抽取k個元素,其中n無法確定 解法 首先選擇n中的前k個數加入 蓄水池 中,然後從第k 1個數開始,以k k i i 1,2,3.的概率選擇這個數,然後在蓄水池中隨機選擇乙個數,並將其替換,n個元素遍歷完畢後,蓄水池中的k個數就是隨機選擇的。證明 這裡即需要證明每個數出現...

蓄水池抽樣

給定乙個長度很長的資料流,在處理完成之前不知道其具體長度,如何在遍歷一遍資料流的情況下,隨機地抽出m個不重複的資料。key words 長度很長,遍歷完之前不可知 o n 複雜度 等概率地抽出m個數,每個數被抽中的概率為m n。如果接收的資料量小於m,直接放入蓄水池reservoir如果接收的數量大...