解題說明
Shuffle an Array
題目描述:設計一個類別,支援兩個操作:reset() 將陣列恢復為原始順序;shuffle() 回傳陣列的一個隨機排列,每種排列被回傳的機率應相等。
解題思路:使用 Fisher-Yates 洗牌演算法(又稱 Knuth Shuffle)。從陣列最後一個元素開始,每次將當前元素與前面(含自己)的隨機位置交換。這保證每種排列出現的機率均為 1/n!。需要保存一份原始陣列的副本以供 reset 使用。
C++ 解法
複雜度分析
時間複雜度:reset O(n) — 複製陣列;shuffle O(n) — 遍歷一次並做交換。
空間複雜度:O(n) — 儲存原始陣列和當前陣列各一份。
虛擬碼
1. Constructor: store a copy of nums as original and current
2. Reset(): copy original to current, return current
3. Shuffle() — Fisher-Yates algorithm:
a. For i from n-1 down to 1:
- Pick random j in [0, i]
- Swap current[i] and current[j]
b. Return current其他解法
排序洗牌法 O(n log n):為每個元素生成一個隨機數,然後按隨機數排序。簡單但效率較差且不保證均勻分佈(理論上隨機數可能相同)。
Inside-Out Fisher-Yates:從前往後遍歷,每次將位置 i 的元素與 [0, i] 中的隨機位置交換。結果等價於標準 Fisher-Yates,但可以邊讀取邊洗牌,適合串流資料。
延伸思考
- 如何證明 Fisher-Yates 洗牌演算法確實產生均勻分佈?
- 若只需要陣列的前 k 個元素的隨機排列,如何優化?
rand() % (i+1)在 i+1 不整除 RAND_MAX+1 時會有微小偏差,如何消除?