對一個列表(一維數組)的完全性洗牌(shuffle)操作,是要讓其元素每種排列模式等概率出現。
列表元素個數爲n,那麼其全排列的數目就是n的階乘“n!”。
僞隨機數是有周期的,設週期爲m。那基於僞隨機數映射的元素排列模式也只會有m種。
當列表元素很多,n! 會是個天文數字,大於僞隨機數週期,那樣永遠有一部分排列模式無法出現。
python標準庫函數random.shuffle(x)就說明了——“請注意,即使對於小的len(x),x的排列總數也可以快速增長,大於大多數隨機數生成器的週期。 這意味着長序列的大多數排列永遠不會產生。 例如,長度爲2080的序列是可以在 Mersenne Twister 隨機數生成器的週期內擬合的最大序列。”
要對大於僞隨機數週期的大列表完全性shuffle,怎麼辦呢
我採用的辦法是多輪洗牌,每輪用不同質數週期的僞隨機數序列。這些週期的乘積大於列表元素全排列數目“n!”,就保證了完全性shuffle的完成。
我寫了這個算法的實現python包,源碼放在GitHub上,complete_shuffle
已經發布到了PyPI上,可以很方便的安裝分發:
pip install complete-shuffle
此包還包括了Sattolo算法實現的對列表隨機循環排列。算法介紹見Sattolo算法
和
等概率生成列表的“全錯位排列”函數,功能實現了可以用於含重複元素的列表。我使用的是優化的接受-拒絕採樣算法,時間複雜度爲O(n)。生成均勻分佈“全錯位排列”還有一種優化算法,見Generating Random Derangements。此算法時間複雜度也是O(n),但聲稱其時間複雜度的常數項較低。我看意義不大。
順便說python 3.9版已聲明 random.shuffle() 的 random 形參將被棄用
我寫這個包中函數就有等同於 random.shuffle() 的 random 的形參。可以替代標準庫random.shuffle()函數了