【數學】時間複雜度O(1)的離散採樣算法—— Alias method/別名採樣方法

因爲需要用到Alias Sampling Method的方法,但是查了一下,發現沒有找到靠譜的關於Alias Method的中文介紹,所以乾脆自己寫一個好了。
關於Alias Method的介紹的比較好的是一個外國Blog:Darts, Dice, and Coins: Sampling from a Discrete Distribution,以下的介紹也主要參考這篇Blog裏的算法。

問題:比如一個隨機事件包含四種情況,每種情況發生的概率分別爲: 12,13,112,112 ,問怎麼用產生符合這個概率的採樣方法。

最容易想到的方法

我之前有在【數學】均勻分佈生成其他分佈的方法中寫過均勻分佈生成其他分佈的方法,這種方法就是產生0~1之間的一個隨機數,然後看起對應到這個分佈的CDF中的哪一個,就是產生的一個採樣。比如落在0~ 12 之間就是事件A,落在12 ~56 之間就是事件B,落在56 ~1112 之間就是事件C,落在1112 ~1之間就是事件D。
但是這樣的複雜度,如果用BST樹來構造上面這個的話,時間複雜度爲O(logN) ,有沒有時間複雜度更低的方法。

一個Naive的辦法

一個Naive的想法如下:
這裏寫圖片描述
1. 可以像上圖這樣採樣,將四個事件排成4列:1~4,扔兩次骰子,第一次扔1~4之間的整數,決定落在哪一列。
這裏寫圖片描述
2. 如上如所示,將其按照最大的那個概率進行歸一化。在1步中決定好哪一列了之後,扔第二次骰子,0~1之間的任意數,如果落在了第一列上,不論第二次扔幾,都採樣時間A,如果落在第二列上,第二次扔超過23 則採樣失敗,重新採樣,如果小於23 則採樣時間B成功,以此類推。
3. 這樣算法複雜度最好爲O(1) 最壞有可能無窮次,平均意義上需要採樣O(N)

那怎麼去改進呢?

Alias Method

這樣Alias Method採樣方法就橫空出世了
這裏寫圖片描述
還是如上面的那樣的思路,但是如果我們不按照其中最大的值去歸一化,而是按照其均值歸一化。即按照1N (這裏是$\frac{1}{4})歸一化,即爲所有概率乘以N,得到如下圖:
這裏寫圖片描述
其總面積爲N,然後可以將其分成一個1*N的長方形,如下圖:
這裏寫圖片描述
將前兩個多出來的部分補到後面兩個缺失的部分中。
先將1中的部分補充到4中:
這裏寫圖片描述
這時如果,將1,2中多出來的部分,補充到3中,就麻煩了,因爲又陷入到如果從中採樣超過2個以上的事件這個問題中,所以Alias Method一定要保證:每列中最多隻放兩個事件
所以此時需要講1中的補滿3中去:
這裏寫圖片描述
再將2中的補到1中:
這裏寫圖片描述
至此整個方法大功告成
Alias Method具體算法如下:
1. 按照上面說的方法,將整個概率分佈拉平成爲一個1*N的長方形即爲Alias Table,構建上面那張圖之後,儲存兩個數組,一個裏面存着第i 列對應的事件i 矩形站的面積百分比【也即其概率】,上圖的話數組就爲Prab[23 , 1 , 13 , 13 ],另一個數組裏面儲存着第i 列不是事件i 的另外一個事件的標號,像上圖就是Alias[2 NULL 1 1]
2.產生兩個隨機數,第一個產生1~N 之間的整數i,決定落在哪一列。扔第二次骰子,0~1之間的任意數,判斷其與Prab[i]大小,如果小於Prab[i],則採樣i,如果大於Prab[i],則採樣Alias[i]

這個算法是不是非常的精妙而且簡潔,做到了O(1) 事件複雜度的採樣。但是還有一個問題是,如何去構建上面的第1步?即如何去拉平整個概率分佈?這樣預處理的時間複雜度又是多少呢?

Alias Method程序化構建

Naive方法

構建方法:
1.找出其中面積小於等於1的列,如i列,這些列說明其一定要被別的事件矩形填上,所以在Prab[i]中填上其面積
2.然後從面積大於1的列中,選出一個,比如j列,用它將第i列填滿,然後Alias[i] = j,第j列面積減去填充用掉的面積。

以上兩個步驟一直循環,直到所有列的面積都爲1了爲止。

存在性證明

那麼Alias Table一定存在嗎,如何去證明呢?
要證明Alias Table一定存在,就說明上述的算法是能夠一直運行下去,直到所有列的面積都爲1了爲止,而不是會中間卡住。
一個直覺就是,這一定是可以一直運行下去的。上述方法每運行一輪,就會使得剩下的沒有匹配的總面積減去1,在第n輪,剩下的面積爲N-n,如果存在有小於1的面積,則一定存在大於1的面積,則一定可以用大於1的面積那部分把小於1部分給填充到1,這樣就進入到了第n+1輪,最後一直到所有列面積都爲1。
更爲嚴謹的證明見上面給出的那個Blog。

更快的構建方法

如果按照上面的方法去構建Alias Table,算法複雜度是O(n2) 的,因爲最多需要跑N輪,而每跑一輪的最大都需要遍歷N次。一個更好的辦法是用兩個隊列A,B去儲存面積大於1的節點標號,和小於1的節點標號,每次從兩個隊列中各取一個,將大的補充到小的之中,小的出隊列,再看大的減去補給之後,如果大於1,繼續放入A中,如果等於1,則也出去,如果小於1則放入B中。
這樣算法複雜度爲O(n)

至此Alias Method就講完了,感覺還是一個非常精妙的方法,而且方法實現起來也非常的簡單。值得學習。

發佈了89 篇原創文章 · 獲贊 163 · 訪問量 35萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章