裝配腦袋兄在某個文章中指出了一種有意思的洗牌演算法,博主按照他的思路寫了另外一種洗牌演算法。下面是該洗牌演算法的思路:
我們先看一下紙牌遊戲。一幅紙牌由 52 張不同的紙牌組成,發牌時必須產生不重複的紙牌,而且洗牌過程必須公平,即 52! 中紙牌順序應該等機率出現。很明顯這種隨機排列所產生的隨機數必須均勻分布且獨立。由此代碼如下:
using System;<br />using System.Diagnostics;</p><p>namespace Lucifer.CSharp.Sample<br />{<br /> class Program<br /> {<br /> static void Main(string[] args)<br /> {<br /> //初始化牌局<br /> int[] array = new int[52];<br /> for (int i = 0; i < array.Length; i++)<br /> {<br /> array[i] = i;<br /> }</p><p> //洗牌<br /> Permute<int>(array);</p><p> //驗證牌局<br /> for (int i = 0; i < array.Length; i++)<br /> {<br /> var value = array[i];<br /> for (int j = 0; j < array.Length; j++)<br /> {<br /> if (j == i) continue;<br /> Debug.Assert(array[j] != value);<br /> }<br /> }<br /> }</p><p> static void Permute<T>(T[] array)<br /> {<br /> Random random = new Random();<br /> for (int i = 1; i < array.Length; i++)<br /> {<br /> Swap<T>(array, i, random.Next(0, i));<br /> }<br /> }</p><p> static void Swap<T>(T[] array, int indexA, int indexB)<br /> {<br /> T temp = array[indexA];<br /> array[indexA] = array[indexB];<br /> array[indexB] = temp;<br /> }<br /> }<br />}
程式碼範例中的 Permute<T>(T[] array) 方法產生一個隨機序列。第一個迴圈用 1, 2, 3, …, N 初始化該序列。第二個迴圈完成一次隨機洗牌。在該迴圈的每次迭代中,我們講 array[j] 的值於數組位置在區間[0, j)之間的某個元素相交換(也可能不交換)。
然而,我們要問的是 Permute<T>(T[] array) 方法產生的所有排列等機率嗎?
若根據該演算法,答案為是。因為一共有 N! 種可能的排列,而 Swap<T>(array, i, random.Next(0, i)); 這一句 N-1 次調用 Next 方法出現的不同結果也是 N! 種。但是,事實上答案為否,並非所有排列都是等機率。問題就出在可愛的偽隨機數數產生器(Pseudo-Random Number Generator)上。PRNG 的隨機性很大程度上限制了隨機序列的隨機性。所以,上述代碼需要一個更好的偽隨機數數產生器以使實際與理論更加相符。但是好的 PRNG 往往伴隨著效能的下降,比如 Mt19937 隨機化演算法。
題外話:博主在實際代碼運行中,發現上述代碼能很好的完成洗牌演算法要求,但不保證其能在商業化項目中得以正確實踐。我記得雲風兄在他的部落格上也曾經討論過洗牌演算法,有興趣的兄弟可以搜尋一番。
轉自:http://www.cnblogs.com/lucifer1982/archive/2009/03/05/1404106.html