關於錯排問題


錯排問題,這個問題的背景可能有多種表述方式,將其形式化,可表為:將 1 至 n 這 n 個數字排列,使得每個數字不出現在其所在序號的位置上,問所有可能的排列數。詳見 wiki

對於這一問題的「官方」解法是這樣的:考慮編號為 1 的數字,顯然它有 \(n-1\) 個可能的位置。假定它出現在 i 位置上,那么分兩種情況考慮:

  1. \(i\) 號數字出現在 1 位置上,則問題縮減為 \(n-2\) 個數字錯排的情況;
  2. \(i\) 號數字出現在非 1 位置上。這里出現了一個精妙的想法:對於 \(i\) 號數字來說,它「不允許」出現在 1 位置上,而其他的各元素 \(2, 3, ..., i-1, i+1, ..., n\) 不允許出現在各自的編號位置上——所以,\(i\) 號元素和其他元素的地位是等價的——問題化歸為 \(n-2\) 個數字錯排的情況。

綜上,可以得到遞推公式,\(D(n)=(n-1)(D(n-1)+D(n-2)), n\ge 3\)。當然,初始條件為 \(D(1)=0, D(2)=1\)

另一種也比較容易想到的方法是這樣的:對於所有可能的排列,減去其中出現矛盾的情況。

  • 若只有 1 個數字出現了矛盾(在它的位置上),共有 \(C_n^1\) 種可能,對於剩下的每個元素都處於非自己的位置上,即划歸為 \(n-1\) 時的問題;
  • 若有 2 個數字出現矛盾,共有 \(C_n^2\) 種可能,對其他元素進行錯排,即 \(n-2\) 時的情況;...
  • 若有 \(n-2\) 個數字出現矛盾,共有 \(C_n^{n-2}\) 種可能,對其他元素進行錯排,即 \(2\) 時的情況;
  • 注意到,不可能恰好只有 \(n-2\) 個數字出現矛盾(最后那個數字只能在它的位置上),因此最后再減去 1,即所有元素在它的位置上的情況。

綜上,遞推公式為 \(D(n)=n!-C_n^1D(n-1)-C_n^2D(n-2)-...-C_n^{n-2}D(2)-1, n\ge 3\),初始條件和上種解法一致。

from scipy.special import comb
import math


def derangement_1(n):
    if n == 1:
        return 0
    if n == 2:
        return 1
    return (n-1) * (derangement_1(n-1)+derangement_1(n-2))


def derangement_2(n):
    if n == 1:
        return 0
    if n == 2:
        return 1
    der = math.factorial(n)
    for i in range(n-1, 1, -1):
        der -= comb(n, n-i) * derangement_2(i)
    return int(der-1)


m = 15
for i in range(2, m):
    print(derangement_1(i), end=" ")
print()
for i in range(2, m):
    print(derangement_1(i), end=" ")

結果為

1 2 9 44 265 1854 14833 133496 1334961 14684570 176214841 2290792932 32071101049 
1 2 9 44 265 1854 14833 133496 1334961 14684570 176214841 2290792932 32071101049 

可見,兩種計算方法是一致的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM