六年级时,我曾在《数字——破解万物的钥匙》中看到了这么一个有趣的事实:
一群小伙子去游泳池游泳,他们的泳裤在同一个箱子里。当他们进入更衣室时,房间停电了。于是他们每个人随手抓了一件泳裤穿在身上,那么他们全部穿错自己的泳裤的概率是 e1,即 36.79% 左右。
当时年少无知,并不知道其中的原理。而且最神奇也是最令人不解的是这里面居然出现了一个 e!
如今高二学习了排列组合,便研究了一番,却发现这里面大有其奥妙,并且这么一个概率问题竟涉及到排列组合、数列和导数,甚至跟集合有关!
本文假设读者理解大部分高中数学知识,那就让我们开始吧!
从错排开始讲起
事实上,我们要从一个高中常见的组合问题讲起:
四个信封和四封信一一对应,现在将信随便装入信封内,四封信全部装错一共有几种装法?
很显然,经过简单的分类讨论可以知道有 9 种方法。事实上,这类问题叫做「错排问题」,下面给出一般定义。
定义
错排(错位全排列)问题,又称为伯努利欧拉装错信封问题,是指如下问题:
n 个信封和信,将信随便装入信封内,所有信全部装错信封的方案数有多少种?
找出递推式
我们设 n 封信的方案数为 Dn,那么显然 D1=0,D2=1。
上面我们已经知道 D4=9。但是,如果有更多的信封,问题的难度会大幅上升,单靠分类讨论难以解决,因此我们需要一些更高级的做法,而建立递推式便是一个好方法,在这里我们就用这个方法。
那我们如何找到 Dn 的递推式呢,我们可以这样想:
- 先考虑第一封信,它不放在自己的信封里显然有 (n−1) 种放法。
- 如果上一步第一封信放在了第 k 封信的信封里了,那么我们考虑第 k 封信的位置,又有两种情况:
- 它在第一封信的信封里,那么此时剩下的 (n−2) 封信要满足错排的性质,方案数就是 Dn−2 种。
- 它不在第一封信的信封里,此时我们假装将第一封信装进第 k 封信的信封并将信封扔掉,由于第 k 封信不在第一封信的信封里,那么此时如果我们将第一封信的信封重命名为新的第 k 封信的信封,事实上就是 (n−1) 封信的错排,有 Dn−1 种方案。
根据加法原理和乘法原理,我们便得到了 Dn 的递推式:
Dn=(n−1)(Dn−1+Dn−2)(n≥2)
向通项式出发
在这里我给出百度百科找到的由递推式变形得到通项式的方法,其中涉及到了一些“天眼”(即难以想到的步骤)和常用的数列处理方法。首先,第一步,要把递推式变形成这个样子:
n!n!Dn=(n−1)(n−1)!(n−1)!Dn−1+(n−1)(n−2)!(n−2)!Dn−2
为什么这么变?因为这样能做出来。
实际上我也不知道为什么要这么变,这一步是最难的,我自己也没想到。实际上,我在解通项式是用的是生成函数的方法(详见下一节),期间还碰了壁。
两边可以同时约去一个 (n−1)!,并且作一些变形:
nn!Dnn(n!Dn−(n−1)!Dn−1)n!Dn−(n−1)!Dn−1=(n−1)(n−1)!Dn−1+(n−2)!Dn−2=−(n−1)!Dn−1+(n−2)!Dn−2=−n1((n−1)!Dn−1−(n−2)!Dn−2)
这里使用累乘的方法,有:
n!Dn−(n−1)!Dn−1=−n1((n−1)!Dn−1−(n−2)!Dn−2)=(−n1)(−n−11)((n−2)!Dn−2−(n−3)!Dn−3)=(−n1)(−n−11)⋯(−31)(2!D2−1!D1)=(−n1)(−n−11)⋯(−31)(−21)(−11)=n!(−1)n
再使用累加的方法,于是:
==n!Dn−1!D1(n!Dn−(n−1)!Dn−1)+((n−1))!Dn−1−(n−2)!Dn−2)+⋯+(2!D2−1!D1)n!(−1)n+(n−1)!(−1)n−1+⋯+2!(−1)2
移项后我们便能得到错排问题的通项式了:
Dn=n!k=2∑nk!(−1)k
可能这个式子看着比较别扭,但是后面的求和号我们无法再精确地化简了。
当然,下标从 2 开始可能有些许怪,由于 0!(−1)0+1!(−1)1=0,因此我们也可以写成:
Dn=n!k=0∑nk!(−1)k
如何让错排更好算
如果你将 n 代入通项公式,你会发现 Dn 算起来比较麻烦,这主要是其通项式中求和号造成的。
事实上,根据通项公式也可以得到一条比较简单的递推公式:
Dn=nDn−1+(−1)n
但递推式用起来还是没有通项式那么爽,因此接下来,我将带领你利用 ex 的泰勒展开式,得到错排通项公式的一个近似公式。
泰勒展开
泰勒展开就是利用求导构建一个多项式来逼近一个函数,它的优点就在于可以将复杂的函数化为多项式。用泰勒展开进行估算也是一个不错的选择。
那么具体怎么展开呢?这里有一个公式(这里我就直接写无穷和而非多项式估计了):
f(x)=k=0∑∞k!f(k)(a)(x−a)k
其中 f(k)(x) 为 f(x) 的 k 阶导数(前提是要存在,否则不能展开)。
等号右边的式子就被称为 f(x) 在 x=a 处的泰勒展开。这个展开式会在 x=a 处附近比较贴合原函数,估计的效果也越好。f(x) 在 x=0 处的泰勒展开也称为麦克劳林展开。
这里我给出一种比较直观的观点来证明它(但是并不严谨),假设展开式是长这样的:
f(x)=k=0∑∞tk(x−a)k
其中 {tn} 是待定系数。那么我们接下来反复进行以下操作:
- 代入 x=a 得到 tk 的值。
- 两边同时求导。
不难可以发现 tk=k!f(k)(a),其中 k! 是多次求导累积形成的。
这里我们用到的是一个带有拉格朗日余项的泰勒展开,它的严格证明要用到拉格朗日中值定理,这里就不再补充了,感兴趣的读者可以自行查阅资料:
f(x)=k=0∑nk!f(k)(a)(x−a)k+Rn(x)
其中 Rn(x)=(n+1)!f(n+1)(ξ)(x−a)n+1 为余项,并且 ξ 在 x 与 a 之间。
错排公式的近似公式
熟悉泰勒展开的同学当见到错排公式时一定有着一种熟悉的感觉,那便是 ex 的泰勒展开。
我们对 ex 进行展开,由于 (ex)(n)=ex,因此:
ex=k=0∑nk!xk+(n+1)!eξxn+1
接下来,我们让 x=−1,就有:
e1=k=0∑nk!(−1)k+(n+1)!eξ(−1)n+1ξ∈(−1,0)
两边乘上 n! 并移项取绝对值,得到:
∣∣∣∣en!−Dn∣∣∣∣=n+1eξ<n+11≤21
因此 en! 与 Dn 的误差不会超过 21,也就意味着 en! 四舍五入的结果便是 Dn,即:
Dn=⌊en!+21⌋
其中,⌊x+21⌋ 表示 x 四舍五入得到的结果。
回到开头
那么接下来就可以回到开头,解决开头关于泳裤的疑问了。
由于 n 个人各取一条泳裤是全排列问题,一共有 n! 种取法。全部拿错自己泳裤的方案数又有 Dn 种,那么全部拿错的概率便是:
n!Dn=n!⌊en!+21⌋≈n!en!=e1≈36.7%
补充:错排通项式的一些其它解法
这些解法是非常自然能够想到的,比上面逆天的数列处理方法好很多。
生成函数
生成函数就是一列用来展示一串数字的挂衣架。——赫伯特·维尔夫
生成函数(又称母函数)是求无穷和以及求数列通项的一个利器,我打算在此挖个坑,科普一下生成函数,因为这玩意很神奇,很好玩,还非常有用,基本可以做到指哪打哪,多难的问题都可以解决。
在我之前的文章 [研究] “复杂”的相遇问题 的文末,我提到了这么一个式子:
1+x+x2+⋯=1−x1
等号左边事实上就是常数列 an=1 的生成函数。我们通常可以通过递推将这么一个无穷和化为像等号右边的这个浓缩的形式。
接下来,跟着我的思路一步一步得到通项。
我们现在有 S1=0,S2=1,Sn=(n−1)(Sn−1+Sn−2),假设递推式对 n=2 成立,那么可以设 S0=1,这是为了方便计算。我们构建 Sn 的生成函数:
f(x)=k=0∑∞Skxk=S0+S1x+S2x2+S3x3+⋯
那么:
xf(x)(x+1)f(x)((x+1)f(x))′=S0x+S1x2+S2x3+S3x4+⋯=S0+(S1+S0)x+(S2+S1)x2+(S3+S2)x3+(S4+S3)x4+=(S1+S0)+2(S2+S1)x+3(S3+S2)x2+4(S4+S3)x3+⋯=S2+S3x+S4x2+⋯=x2f(x)−S0−S1x
得到关系式后,我们再变下形:
((x+1)f(x))′x2(x+1)f′(x)+x2f(x)f′(x)+x2x−1f(x)=x2f(x)−S0−S1x=f(x)−1=−x2(x+1)1
接下来便是求一个微分方程的事。式子两边乘上待定函数 β(x),其中满足 β′(x)=x2x−1β(x)。
那么很显然 β(x)=eI,其中 I=∫x2x−1dx=lnx+x1,即 β(x)=xex1。因此:
(β(x)f(x))′(xex1f(x))′=−x2(x+1)β(x)=−x(x+1)ex1
当我做到这一步时,原本下一步应该是两边积分的,但是我发现右边这玩意积不出来啊!!没办法,这条路最终以失败告终。
但是我突然想起还有个很类似的东西叫做指数型母函数。我们上面用的是普通型母函数,这两种母函数形式非常相似,但是又有区别:
形式上说,普通型生成函数用于解决多重集的组合问题,而指数型母函数用于解决多重集的排列问题。
我们这种属于排列问题,因此指数型母函数也许能做出来。我们建立 Dn 的指数型母函数:
g(x)=k=0∑∞Skk!xk=S0+S1x+S22x2+S33!x3+⋯
那么:
g′(x)g′(x)+g(x)=S1+S2x+S32x2+S43!x3+⋯=(S1+S0)+(S2+S1)x+(S3+S2)2x2+(S4+S3)3!x3+⋯=S2+S32x+S43!x2+S54!x3=xg′(x)−S1=xg′(x)
因此:
g′(x)+g(x)(x−1)g′(x)+xg(x)=xg′(x)=0
这个式子可顺眼太多了,炮制之前解微分方程的方法(即凑导数),我们可以得到:
((x−1)exg(x))′g(x)=0=(x−1)exC
其中 C 为积分常量。根据 g(0)=S0=1 解得 C=−1。因此 g(x)=1−x1⋅e−x
还记得上面讲到 ex 的泰勒展开吗,代入 −x 得到:
e−x=k=0∑∞(−1)kk!xk
以及再用上面提及到的 1+x+x2+⋯=1−x1,可以展开 g(x):
g(x)=(1+x+x2+x3+⋯)(1−x+2x2−3!x3+⋯)=k=0∑∞(xki=0∑ki!(−1)i)
对比每一个 xn 的系数,可以得到:
n!DnDn=k=0∑nk!(−1)k=n!k=0∑nk!(−1)k
容斥原理
容斥原理在我们学集合时就已经学过:
∣A∪B∣∣A∪B∪C∣=∣A∣+∣B∣−∣A∩B∣=∣A∣+∣B∣+∣C∣−∣A∩B∣−∣A∩C∣−∣B∩C∣+∣A∩B∩C∣
根据这两条式子很容易可以想到它拓展到 n 的形式:
∣∣∣∣k=1∪nSk∣∣∣∣=k=1∑n(−1)k−11≤i1<⋯<ik≤n∑∣∣∣∣j=0∩kSij∣∣∣∣
其中 ∣∣∣∣k=1∪nSk∣∣∣∣=S1∪⋯∪Sn,∣∣∣∣k=1∩nSk∣∣∣∣=S1∩⋯∩Sn。
再搭配上德 · 摩根律:
∣∣∣∣k=1∩nSk∣∣∣∣=∣∣∣∣k=1∪nSk∣∣∣∣=∣S∣−∣∣∣∣k=1∪nSk∣∣∣∣
得到这个能秒杀错排问题的公式(逐步淘汰原理):
∣∣∣∣k=1∩nSk∣∣∣∣=∣S∣−k=1∑n(−1)k−11≤i1<⋯<ik≤n∑∣∣∣∣j=0∩kSij∣∣∣∣
接下来,考虑这么 n 个集合 Sn,其中 ∣Sk∣ 表示第 k 封信在自己信封里时,其它信封排列的方案数。那么:
Dn=∣∣∣∣k=1∩nSk∣∣∣∣=∣S∣−k=1∑n(−1)k−11≤i1<⋯<ik≤n∑∣∣∣∣j=0∩kSij∣∣∣∣=n!−k=1∑n(−1)k−11≤i1<⋯<ik≤n∑(n−k)!=n!−k=1∑n(−1)k−1Cnk(n−k)!=n!−k=1∑nk!n!(−1)k−1=n!k=0∑nk!(−1)k
当然了,错排公式也可以用二项式反演推得,本质上与容斥原理一样,这里就不再赘述了。