隨機排序演算法實作
簡介
說到隨機排序(shuffle)演算法,最著名的肯定是Fisher–Yates,它是一個$O(N)$時間的演算法,偽碼如下
1 | Shuffle(Array a){ |
簡單來說就是每個元素都和之後(包含自身)的隨機一個元素交換
實作
1 | import random |
執行1000000次Shuffle,各數字的分布
1 | 數字0分佈 |
但為何只要這樣交換,就是隨機排序? 🤔
不嚴謹的證明
已知N=1或2時,使用此法皆可使陣列為隨機排序。
若N=n時成立,考慮N=n+1時的情況:
Without loss of generality,令陣列為[0, 1, 2, ..., n]
第一個位置的數0會和後面的隨機一個元素交換,此時第一個位置是0到n的數的機率是平均的 $P_0(v)=\frac{1}{n+1}, v \in {0, 1, 2, .. n}$
此時子陣列([1:]
)即為N=n的情況,依照假設,使用此法後,其餘數字會是隨機排序,
所以N=n+1時也成立。
依數學歸納法,N=1,2時成立,且N=n成立時,N=n+1也成立;所以N為自然數時,使用此法皆可使陣列為隨機排序。$\blacksquare$
一個更直觀的解釋是,每一次交換都是在隨機選擇剩下還沒選的值。
錯誤版本
1 | WrongShuffle(Array a){ |
此版本的所有元素對陣列中的一個元素交換
雖然接近,但不是隨機排序
1 | 數字0分佈 |
為何不是隨機排序? 🤔 目前想不到直觀解釋
參考維基的證明:
總共有$n^n$種選法,但可能的排列只有$n!$種,在$n>2$時並不能整除
更簡單的實作
對每個index設一隨機數,對隨機數排序
時間複雜度為$O(n\log n)$
各語言Shuffle實作
都是Fisher-Yates
- Python
- LLVM C++
Reference
- Wiki
- https://people.cs.umass.edu/~phaas/CS590M/handouts/Fisher-Yates-proof.pdf
- https://cs.stackexchange.com/questions/2152/how-to-prove-correctness-of-a-shuffle-algorithm