隨機概率相關的面試題

WalkingInTheWind發表於2013-04-11
1. 已知有個rand7()的函式,返回1到7隨機自然數,讓利用這個rand7()構造rand10() 隨機1~10。
只呼叫一次rand7()肯定無法達到目的。我們呼叫兩次rand7(),這樣我們可以隨機的得到1~49中的一個數,為什麼呢?
我們將49分成7段,1~7,8~14,15~21,22~28,29~35,36~42,43~49,第一次rand7()隨機選擇其中一段,第二次rand7(),隨機選擇段內的一個數,這樣我們得到的1~49中的數都是等概率的。
然後我們想辦法將1~49對映到1~10,顯然無法直接對映,我們只取1~40,對於41~49我們拋棄,這樣並沒有影響其隨機特性,因為1~40中的每一個數都是等概率出現的。
然後(1~4)→1,(5~8)→2,……。這樣就完成了rand10的功能。

int rand10()
{
	int x;
	do
	{
		x = (rand7()-1)*7+rand7();
	}while(x > 40)
	return (x-1)%4+1;
}
2. 有一個隨機生成器randA(),以p的概率返回0,1-p的概率返回1,利用這個randA()構造randB(),使randB()等概率的返回0和1,即0.5的概率返回0,0.5的概率返回1。
只呼叫一次randA(),我們也無法實現randB(),所以也要呼叫兩次。
呼叫兩次共有4種結果,得到00,概率為p*p;得到01,概率為p*(1-p);得到10,概率為(1-p)*p;得到11,概率為(1-p)*(1-p)。
容易發現有兩種情形概率相等,01和10,那麼我們可以把這兩種情況對映為返回0或1,如果得到的是其它兩種情況,那麼我們就繼續再呼叫兩次randA();
int randB()
{
	int x1, x2;
	do
	{
		x1 = randA();
		x2 = randA();
	}while(x1+x2 != 1)
	return x1;
}
3. 程式的輸入包含兩個整數m和n,其中m<n。輸出是0~n-1範圍內m個隨機整數的有序列表,不允許重複。從概率的角度來說,我們希望得到沒有重複的選擇,其中每個選擇出現的概率相等。
void generate(int m,int n)
{
	int t = m;
	for(int i = 0; i < n; i++)
		if(Rand(0,n-1-i) < t) //即以t/(n-i)的概率執行下面的語句
		{
			printf("%d\n",i);
			t--;
		}
}

其中,Rand(a,b)隨機產生[a,b]範圍內的一個整數。
上述演算法源自Knuth的《計算機程式設計藝術 第2卷 半數值演算法》。Knuth 給出了概率上的證明,每個數選中的概率都是m/n,而且恰好選中m個數。

簡單驗證一下:

第0個數,被選中的概率是

第1個數,被選中的概率是

第2個數,被選中的概率是

4. 給出一個n個元素的陣列,對所有元素隨機中排,也就是說,n!種可能的元素排列中隨機選出一種。
void random_shuffle(int A[], int n)
{
	for(int i = n-1; i > 0; i--)
	{
		swap(a[i], a[Rand(0, i)]);//即隨機選擇0~i中的一個元素
	}
}
其中,Rand(a,b)隨機產生[a,b]範圍內的一個整數。
簡單驗證一下,詳細證明請參考Knuth的《計算機程式設計藝術 第2卷 半數值演算法》。
當對於第n-1個位置,swap
(a[n-1], a[Rand(0, n-1)]),使得每一個元素出現在位置n-1的概率都是1/n
再考慮第n-2個位置,每個元素出現在第n-2個位置上的概率是(n-1)/n * 1/(n-1) = 1/n,即在第n-1個位置沒有被選中,然後在第n-2個位置被選中;
再考慮第n-3個位置,每個元素出現在第n-3個位置上的概率是(n-1)/n * (n-2)/(n-1) * 1/(n-2) = 1/n,在第n-1和第n-2個位置都沒有被選中,然後在第n-3個位置被選中。
。。。




相關文章