8.14.6 ACM-ICPC 组合数学:错位排列
一、错位排列的定义
错位排列(Derangement)是指在一个排列中,没有任何元素出现在其原来的位置上。换句话说,对于 1 到 nnn 的排列 PPP,如果对所有 iii 都满足 Pi≠iP_i \ne iPi=i,则称 PPP 是 nnn 的错位排列。
错位排列是没有不动点的排列,即没有长度为 1 的循环。
二、错位排列的容斥原理计算
全集 UUU 即为 1 到 nnn 的排列,∣U∣=n!|U|=n!∣U∣=n!;属性就是 Pi≠iP_i \ne iPi=i。套用补集的公式,问题变成求:
可以知道:
Si‾\overline{S_i}Si 的含义是满足 Pi=iP_i = iPi=i 的排列的数量。用容斥原理把问题式子展开,需要对若干个特定的集合的交集求大小,即:
其中省略了 ai<ai+1a_i < a_{i+1}ai<ai+1 的条件以方便表示。上述 kkk 个集合的交集表示有 kkk 个变量满足 Pai=aiP_{a_i} = a_iPai=ai 的排列数,而剩下 n−kn-kn−k 个数的位置任意,因此排列数为:
那么选择 kkk 个元素的方案数为 (nk)\binom{n}{k}(kn),因此有:
因此 nnn 的错位排列数为:
错位排列数列的前几项为 0, 1, 2, 9, 44, 265(OEIS A000166)。
三、错位排列的递推计算
把错位排列问题具体化,考虑这样一个问题:
有 nnn 封不同的信,编号分别是 1, 2, 3, 4, 5,现在要把这五封信放在编号 1, 2, 3, 4, 5 的信封中,要求信封的编号与信的编号不一样。问有多少种不同的放置方法?
假设考虑到第 nnn 个信封,初始时暂时把第 nnn 封信放在第 nnn 个信封中,然后考虑两种情况的递推:
- 前面 n−1n-1n−1 个信封全部装错;
- 前面 n−1n-1n−1 个信封有一个没有装错其余全部装错。
对于第一种情况,前面 n−1n-1n−1 个信封全部装错:因为前面 n−1n-1n−1 个已经全部装错了,所以第 nnn 封只需要与前面任一一个位置交换即可,总共有 Dn−1×(n−1)D_{n-1} \times (n-1)Dn−1×(n−1) 种情况。
对于第二种情况,前面 n−1n-1n−1 个信封有一个没有装错其余全部装错:考虑这种情况的目的在于,若 n−1n-1n−1 个信封中如果有一个没装错,那么把那个没装错的与 nnn 交换,即可得到一个全错位排列情况。
其他情况,不可能通过一次操作来把它变成一个长度为 nnn 的错排。
于是可得,错位排列数满足递推关系:
这里也给出另一个递推关系:
四、错位排列的其他性质
错位排列数有一个向下取整的简单表达式,增长速度与阶乘仅相差常数:
随着元素数量的增加,形成错位排列的概率 PPP 接近:
五、错位排列的计算方法
1. 递归法
def derangement_recursive(n):
if n == 0:
return 1
if n == 1:
return 0
return (n - 1) * (derangement_recursive(n - 1) + derangement_recursive(n - 2))
2. 动态规划法
def derangement_dp(n):
if n == 0:
return 1
if n == 1:
return 0
dp = [0] * (n + 1)
dp[0] = 1
dp[1] = 0
for i in range(2, n + 1):
dp[i] = (i - 1) * (dp[i - 1] + dp[i - 2])
return dp[n]
3. 显式公式法
import math
def derangement_formula(n):
return int(math.factorial(n) * sum((-1)**i / math.factorial(i) for i in range(n + 1)))
六、错位排列的应用
- 密码学:错位排列可以用于设计抗碰撞的哈希函数和安全的密钥交换协议。
- 算法设计:在模拟、随机算法和组合生成算法中,错位排列可以用于生成随机排列。
- 统计学:错位排列在随机排列和抽样问题中有重要应用。
- 游戏和谜题:许多益智游戏和谜题可以归结为错位排列问题,如“秘密朋友”游戏和“人类轮盘赌”等。
七、例题与习题
例题
例1:计算 4 个元素的错位排列数
n = 4
print(derangement_dp(n)) # 输出 9
习题
- 计算 !5!5!5 和 !6!6!6 的值。
- 编写一个函数,生成 nnn 个元素的所有错位排列。
- 使用递归法和动态规划法分别计算 !10!10!10 的值,并比较两种方法的运行时间。
- 利用显式公式法,证明 !0=1!0 = 1!0=1 和 !1=0!1 = 0!1=0。
通过这些习题,可以更好地理解和应用错位排列的计算方法和性质。
希望这篇博客能够帮助读者更好地掌握错位排列在组合数学和算法竞赛中的应用。如有任何问题或建议,欢迎留言交流。