一、思路:ios
蓄水池問題:當n≤k時,序號爲n的這個數放入容量爲k的容器中,表示該數確定會被選中;當n>k時,要使得前n個數每一個數被選中的機率都是k⁄n。dom
證實:假設n≥k,前n個數每一個數被選中的機率都是k⁄n,則須要概括證實前n+1個數每一個數被選中的機率是k⁄(n+1)。函數
分兩個角度去思考這個問題:一是第n+1個被選中的機率,很顯然是k⁄(n+1)。二是第一、二、...、n個數被選中的機率也要是k⁄(n+1)。spa
後者分析:以第m個數爲例(k<m<n+1),它被選中的機率爲」m被選中的機率*[m後面元素沒有被選中+m後面的元素被選中*可是m沒被替換的機率]「。code
1 #include <iostream> 2 #include <algorithm> 3 #include <ctime> 4 5 using namespace std; 6 7 void SamplePool(int *data, int n, int m) 8 { 9 int i, s; 10 srand(time(0)); 11 for (i = m; i < n; i++) 12 { 13 s = rand() % i; 14 if (s < m) 15 swap(data[i], data[s]); 16 } 17 for (i = 0; i < m; i++) 18 cout << data[i] << ' '; 19 cout << endl; 20 } 21 22 int main() 23 { 24 int data[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 25 SamplePool(data, 10, 5); 26 return 0; 27 }
二、思路:blog
假設要求[1,5]隨機一個[1,7]。則先用[1,5]隨機生成[1,25],再經過拒絕採樣定理選取[1,21],其中選21而不選7的緣由是提升隨機生成的效率。事件
首先,將(1,5)之間的隨機發生器使用兩次,按照五進制進行使用,拼成一個(1,25)的隨即發生器既:([gen][gen]),每一[]爲一個5進制上的位,換算爲十進制爲:x=gen*5+gen。在十進制上的範圍爲:6-30,進行一個簡單的左移動,可換算成1-25範圍上的值;而後將(1,25)平均分配到7中狀況上面,考慮21是7的倍數,所以能夠每三個作一個映射(固然,也能夠無論,直接截斷7後面的數字,可是範圍過小,效率不高),ƒ(1~3) = 1,ƒ(4~6) = 2,此時就是等機率的,若是產生了22-25之間的數字,則拒絕採樣。資源
1 int Rand_M2N(int m, int n) 2 { 3 srand(time(0)); 4 int i, s, t, k; 5 //k是n的最大倍數,可是小於m*m;t是倍數的量。 6 for (i = 1; i <= MAX; i++) 7 { 8 if (i * n < m * m) 9 { 10 k = i * n; 11 t = i; 12 } 13 else 14 break; 15 } 16 cout << "k: " << k << endl; 17 //s是被隨機選中的數。 18 do 19 { 20 s = m * (rand() % m) + (rand() % m + 1); 21 }while(s > k); 22 if (s % t == 0) 23 return s / t; 24 else 25 return s / t + 1; 26 }
三、思路:it
思考角度1:生成1,2,…n的機率分別是1/n,也就是均等的。那麼咱們能夠想怎麼生成一個序列,裏面有n個獨立事件,機率是相等。並且咱們可以猜想到這些機率的形式爲px(1-p)y,若是要相等,那麼x必須等於y。這樣就說明了序列中0和1的個數是相等的。並且這樣的狀況必須有多於n個狀況才行。io
思考角度2:而後是1/n的狀況了,咱們以5爲例,此時咱們取x=2,由於C(2x,x)=C(4,2)=6是比5大的最小的x,此時咱們就是一次性生成4位二進制,把1出現個數不是2的都丟棄,這時候剩下六個:0011,0101,0110,1001,1010,1100,取最小的5個,即丟棄1100,那麼咱們對於前5個分別編號1到5,這時候他們的機率都是p*p*(1-p)*(1-p)相等。關鍵是找那個最小的x,使得C(2x,x)>=n這樣能提高查找效率。我之因此取C(2x,x)是爲了讓一樣的序列長度下可用的資源儘可能多,由於C(n,i)最大是在i接近n/2的地方取得,此時我有更大比率的序列用於生成,換句話說被拋掉的更少了,這樣作是爲了不大量生成了丟棄序列而使得生成速率減慢,其它的沒什麼特別的意思。