Knuth隨機洗牌算法:譬如現在有54張牌,如何洗牌才能保證隨機性。可以這么考慮,從最末尾一張牌開始洗,對於每一張牌,編號在該牌前面的牌中任意一張選一張和當前牌進行交換,直至洗到第一張牌為止。參考代碼如下:
void knuth() { for (int i = 54; i > 1; i--) { int id = rand() % (i - 1) + 1; swap(a[i], a[id]); } }
由上述方法可知,每一張牌經過洗牌之后一定不會出現在原來位置,那么一共會有多少情況呢,這其實就是錯排的定義,n個數的錯排數有如下遞推公式:
f(n)=(n-1)(f(n-1)+f(n-2))
公式的推導:首先讓我們假設已知n-1個數和n-2個數的錯排數,這時又在原來基礎上加了一個數字,那么如果這n個數要構成錯排,新加入的數字一定不能出現在自己的位置上,所以它只能選擇其余的n-1個位置,不妨設選擇了第k個位置,那么原本在第k個位置上的數又會跑到哪里去呢,這里有兩種情況,原本的第k個數跑到第n個數的位置上去了,這時這兩個數只是相互交換了位置,其余的n-2個數怎么排列完全不受影響,故此時有f(n-2)種情況;再考慮原本第k個數不在第n個數的位置上,那么除去第n個數,其余的n-1個數任然構成錯排,錯排數為f(n-1)。至此就可得遞推式f(n)=(n-1)(f(n-1)+f(n-2));
