標籤:style class blog code color 使用
試題:
random(a,b)是可以產生[a,b]之間一個隨機整數的函數。請使用random(a,b)寫一個演算法將長度為n的整型數組隨機打亂。
void random_shuffle(int a[], int n);
分析:
不考慮數組中元素是否重複,長度為n的數組,其全排列共有n!種。要做到機率上的隨機打亂,打亂後的排列的機率需要是1/n!。
答案一、《演算法導論》中的做法:
void random_shuffle(int a[], int n){ int temp_index, temp_val; for (int i = 0; i < n; i++) { temp_index = random(i, n-1); temp_val = a[temp_index]; a[temp_index] = a[i]; a[i] = temp_val; }}
證明:
(證明時,使用1-n的下標,注意與實現區分)。
使用如下的迴圈不等式:在for迴圈的第k次迭代之後,對每個可能的k排列,子數組a[1,k]包含這個k排列的機率是(n-k)!/n!
初始:當k=1時,第一個元素的機率是1/n,而迴圈不等式(n-1)!/n!=1/n。迴圈不等式成立。
保持:假設當k=i時,迴圈不等式也成立。則當k=i+1時,k排列的機率=(k-1)排列的機率 * 第k個元素的機率。
((n-i)!/n!) * (1/(n-i)) = (n-i-1)!/n! = (n-(i+1))!/n!
迴圈不等式成立。
終止:當k=n時,((n-(n-1))!/n!) * 1 = 1/n!
由此證明演算法random_shuffle產生的數組排列是均勻隨機的。
更多內容可參考《演算法導論》引理5.5
答案二: VC STL中algorithm的做法:
void random_shuffle(int a[], int n){ int temp_index, temp_val; // 注意這裡i是從1開始,i=0時不需要交換 for (int i = 1; i < n; i++) { temp_index = random(0, i); temp_val = a[temp_index]; a[temp_index] = a[i]; a[i] = temp_val; }}
證明:
(證明時,使用1-n的下標,注意與實現區分)。
使用如下的迴圈不等式:在for迴圈的第k次迭代之後,對每個可能的k排列,子數組a[1,k]包含這個k排列的機率是1/k!
初始:當k=2時,第一個元素的機率是1/2,而迴圈不等式1/k!=1/2。迴圈不等式成立。
保持:假設當k=i時,迴圈不等式也成立。則當k=i+1時,k排列的機率=(k-1)排列的機率 * 第k個元素的機率。
(1/i!) * (1/(i+1)) = 1/(i+1)!
迴圈不等式成立。
終止:當k=n時,(1/(n-1)!) * (1/n) = 1/n!
由此證明演算法random_shuffle產生的數組排列是均勻隨機的。
小結:
《演算法導論》是本很經典的書,給出了均勻排列的證明。而vc的stl演算法給出了另外一種實現。對比之下,stl的演算法更工程一些,因為i是1開始,而不是從0開始,當i=0時,random(0,0)還是自己,不需要交換,stl直接省卻了這一步。同樣《演算法導論》的實現,for迴圈中的i<n,也可以改為i<n-1。