斗地主之類的游戲大家都玩過,有沒有想過,游戲是如何給我們發牌的呢?
我們先將問題做一下抽象:我們將撲克牌抽象為數字,那么洗牌的問題就轉化為
給定一個長度為54的整型數列,請將其順序隨機打亂,保證每個數出現在任意一個位置的概率相同。
朴素的想法——抽牌
每次隨機從牌堆中選一個位置抽牌,如果該位置的牌已被抽走,則繼續隨機選取位置,直到將所有牌抽完。
顯然,這種做法時間復雜度很高。那么不妨稍微優化下。
每次隨機從牌堆中選一個位置抽牌,然后將該位置后面的牌依次向前移動一個位置,下一次從新牌堆(數量-1)中抽取,循環到抽完所有牌為止。
這樣也能完成要求,單這樣的做法,對於數據規模比較小的數據可以接受,但數據量一旦變大,反而不如不優化的算法效率高。我們還得繼續想辦法優化。
每次隨機從牌堆中選一個位置抽牌,然后將最后一張牌移動到被選擇的位置,下一次從新牌堆(數量-1)中抽取,循環到抽完所有牌為止。
這樣優化完以后,時間復雜度降低到了O(N),應該能滿足要求了。
經典的洗牌算法——交換
上面的算法需要額外的輔助空間,那么能不能就地洗牌呢?答案當然是可以的。
還是每次隨機一個位置,然后將該位置上的牌與 i 位置上的牌交換,直到 i 遍歷玩所有位置為止。

1 void MySwap(int &x, int &y) 2 { 3 int temp = x; 4 x = y; 5 y = temp; 6 } 7 8 void Shuffle(int n) 9 { 10 for(int i=n-1; i>=1; i--) 11 { 12 MySwap(num[i], num[rand()%(i+1)]); 13 } 14 }
該洗牌算法的時間復雜度為O(N),空間復雜度為O(N)。