前兩天,睡覺前,偶爾翻起算法導論,看到隨機函數這一塊內容,里面有一個練習題.
5.1-2 描述random(a,b)過程的一種實現,它只調用random(0,1).作為a和b的函數,你的程序的期望運行時間是多少?
注:random(a,b)為產生a,a+1,a+2,...,b的函數發生器,且產生各整數的概率相等,同為1/(b - a + 1).
看到這個題目時,似曾相識,腦海浮現了利用random(0,1)產生0或1,從而組成二進制數,來完成random(a,b)的實現.但是細想以后,感覺有個問題在腦海中有點不明不白.
運行random(0,1)函數k次,使得2k>=(b-a+1),將得到[0,2k)的整數區間,如何將[0,2k)映射到[a,b]的整數區間,保證產生各整數的概率相等,同為1/(b-a+1).
1.當存在k使得2k=(b-a+1)時,只需將產生的二進制數與[a,b]整數一一對應,即可滿足概率同為1/(b-a+1)的要求.
例如,random(3,6),k=2. 此時,對應關系可為00~3,01~4,10~5,11~6.產生的概率為1/4.
2.當不存在k使得2k=(b-a+1)時,產生[0,2k)區間整數的概率為1/2k,小於1/(b-a+1).[0,2k)如何映射到[a,b]整數區間.
思路一:擴大[0,2k)區間,使得2k可以被(b-a+1)整除,這樣可以把[0,2k)分成N段時,每一段對應[a,b]里的一個整數.
但這個思路,是不可行的,因為不存在這樣的k值.要么2k=(b-a+1),要么2k>(b-a+1)且不可被(b-a+1)整除.
思路二:參取截斷映射,即 [0,2k) 的前部分映射到[a,b],這樣雖然可以達到產生整數的概率相等,但不等於1/(b-a+1),還有如果產生[0,2k)后部分的值如何處理.
這個思路,是可行的,如果產生后部分的值,就繼續調用自身,重新random.從結果輸出分析,最終random(a,b)最終輸出的只有[a,b]里的整數,而且每個整數的概率相等,因而其產生的概率值是1/(b-a+1).
具體的實現代碼如下:
int random(int a,int b) { int m = 1; int len = b - a + 1; int k = 0; //計算最小的正整數k,使2^k >= len while(m < len) { k++; m *= 2; } m = 0; for(int i = 0;i < k;i++) { m += random(0,1) * (1<<i); } if(m + 1 > len) { return random(a,b); } else { return m + a; } }
由於冗余的存在,該方法運行時間最壞的情況是無究,就是無限地遞歸調用自身.運行時間的下限是O(log(b-a+1)).
由上述的練習題可擴展出更多類似的問題.
利用rand5()產生rand7().rand5()產生1到5的整數,rand7()產生1到7的整數.
解決思路與上述的練習題是一樣的.利用rand5()產生的一個整數空間,然后將其映射到[1,7]的整數空間上,映射時保證概率相等,且等於1/7.
下面介紹幾個有意思的實現.
1.利用預置數組 該方法簡單,易理解,但是不具擴展性,需要額外存儲空間.
1 int rand7() 2 { 3 int vals[5][5] = { 4 {1,2,3,4,5}, 5 {6,7,1,2,3}, 6 {4,5,6,7,1}, 7 {2,3,4,5,6}, 8 {7,0,0,0,0} 9 }; 10 int result = 0; 11 while(result == 0) 12 { 13 int i = rand5(); 14 int j = rand5(); 15 result = vals[i - 1][j - 1]; 16 } 17 return result; 18 }
2.常規實現方法 可擴展,主要分為三步,構造大的整數區間,限制整數區間,最后映射整數區間.
1 int rand7() 2 { 3 int i; 4 do{ 5 i = 5 * (rand5() - 1) + rand5(); //產生[1,25]的整數區間 6 }while(i > 21); //將[1,25]整數區間控制於[1,21] 7 return i%7 + 1; //將[1,21]映射到[1,7] 8 }
3.看似正確的方法 其實錯誤的方法
1 int rand7() 2 { 3 int i; 4 i = rand5() + rand5() + rand5() + rand5() + rand5() + rand5() + rand5(); 5 return i%7 + 1; 6 }
與方法2的思路一樣,構造新的整數區間,但是方法3中構造的整數區間並不是等概率的.
第4代碼中,將會產生5^7種可能的計算,但最終這些可能映射到[7,35]的整數區間中,但是[7,35]區間內整數的產生的概率並不相等.
例如,通過累加區間[0,1]三次,可以得到[0,3]的區間,但是[0,3]每個整數的概率並不相等,分別為1/8,3/8,3/8,1/8.
參考資料:
算法導論
——