我们定义错排数 dn 为满足下条件的排列的数目:排列的长度为 n 且不存在 i 使得 pi=i。在此避开对 d0 的讨论。它的递推式是 trivial 的:
dn=(n−1)(dn−1+dn−2).(n>2)
并且显然有 d1=0,d2=1。
直接通过递推式求解通项公式是困难的(对笔者来说),《具体数学》中给出了另一个办法如下。
根据容斥原理,我们可以直接写出 dn 的表达式:
dn=n∑k=0(−1)k(nk)(n−k)!
其中第 k 项表示:从 n 个位置中选出 k 个,钦定它们排列正确;剩下 (n−k) 个可以随便乱排;最后乘上容斥系数 (−1)k。(个人习惯:用 (mn) 表示 Cnm。)(当然,这个表达式也可以通过二项式反演得到。)
化简这个式子:
dn=n∑k=0(−1)k(nk)(n−k)!=n!n∑k=0(−1)kk!(1)
观察后面的式子,可以联想到 ex 的 Maclaurin 展开式,即:
ex=∑k≥0xkk!(2)
在 (2) 式中取 x=−1 ,得到:
e−1=∑k≥0(−1)kk!(3)
这与 (1) 的形式类似,于是我们希望用 (3) 来表示 (1) 。这引导我们分析两个式子的差的大小:
n!∑k>n(−1)kk!=n!∑k≥0(−1)k+n+1(k+n+1)!=(−1)n+1n+1∑k≥0(−1)k(n+1)!(k+n+1)!=(−1)n+1n+1(1−1n+2+1(n+2)(n+3)−⋯)(4)
括号中的量可以轻易地用裂项的方法得到:它介于 1−1n+2=n+1n+2 和 1 之间。于是, n!/e 和 dn 的差大约是 1/n;更精确地,它介于 1/(n+1) 和 1/(n+2) 之间。
然而, dn 是一个整数。所以,如果 n>0,那么 dn 必定就是我们将 n!/e 四舍五入之后的整数。而一个简单的表示四舍五入的方法是加上 0.5 之后向下取整;于是,这样就求出了我们想要的封闭形式:
dn=⌊n!e+12⌋.
然而,这个公式并没有很大的实用价值,因为一般情况下 n! 的求值依然有着至少 O(n) 的复杂度。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!