排列与组合

排列与组合

排列与组合是组合数学中最基础的东西了。在此做一点记录。

阶乘

在组合数学中,阶乘随处可见,它极大的简化了公式的形式。
我们将n!n!记作nn的阶乘,其定义如下:
(1.1)n!=i=1ni n! = \prod^n_{i=1} i \tag{1.1}

特别的,0!=10! = 1。之所以00的阶乘这样定义,是为了使后面的公式更具有普遍性。

我们来看一下阶乘函数的增长:
factorial-function
上图中,红色的阶乘函数f(x)=x! f(x) = x! ,蓝色的是g(x)=x2g(x) = x^2 ,而绿色的是h(x)=x3 h(x) = x^3
由此可见阶乘函数的增长比平方快。与三次方相比,阶乘函数在之后也将赶超。
因此如果时间复杂度里面出现了阶乘,那将是一件可怕的事情。

排列

对于一个集合SS,其排列是SS中的元素的有序放置
例如集合S={a,b,c}S = \{ a, b, c \},它的1排列为:
a,b,c a, b, c

2排列为:
ab,ac,ba,bc,ca,cb ab, ac, ba, bc, ca, cb

3排列为:
abc,acb,bac,bca,cab,cba abc, acb, bac, bca, cab, cba

由于S<4|S| < 4,所以SS没有4排列

为了方便计数,我们将一个含有nn个元素的集合的rr排列个数记为P(n,r) P(n, r) 1
如:P(3,1)=3,  P(3,2)=P(3,3)=6P(3,1)=3, \; P(3,2)=P(3,3)=6
特别的,我们定义P(n,0)=1P(n,0)=1

如何计算这个值呢?当然我们是有公式的:
定理 对于任意的n,rN+n, r \in N_+,并且rn r \le n,有:
(2.1)P(n,r)=n!(nr)! P(n, r) = {n! \over (n-r)!} \tag{2.1}

证明 我们有rr个空位来摆放元素。在第一个空位我们有nn种选择,在第二个空位我们有n1n - 1中选择......由此可以得知:
P(n,r)=i=nr+1ni=n!(nr)! P(n, r) = \prod^n_{i=n-r+1} i = {n! \over (n-r)!}

现在我们来运用一下排列。
首先来计数1199的数构成的每一位均不同的77位数有多少个。
这个非常简单,就是P(9,7)=181440P(9, 7) = 181440

如果现在要求5566不能连续出现,那么这样的数又有多少个?
我们考虑将连续出现的数给计数出来,这样就可以得到答案。
我们发现,通过向一个55位数中插入两个数就可以得到一个77位数,因此我们来考虑将5566连续的插入一个55位数中。我们只会插入56566565,它们在一个55位数中一共有66个位置可以插入。因此5566连续出现的数共有2×6×P(7,5)=30240 2 \times 6 \times P(7, 5) = 30240 个。
因此最终的答案为18144030240=151200 181440 - 30240 = 151200

组合

与排列相对,一个集合的组合2是其元素的无序放置
其含义就是从集合中”选取”出几个元素。由于不考虑位置关系,所以像abcabcbacbac3是等价的。

组合这一概念在整个组合数学中用处十分巨大。接下来我们先看到如何计数它。
首先我们将组合数记为(nr){n \choose r}4,表示一个大小为nn的集合的rr组合的个数。例如,一个大小为44的集合S={a,b,c,d} S = \{ a,b,c,d\}3组合为:
{a,b,c},{a,b,d},{a,c,d},{b,c,d} \{a,b,c\}, \{a,b,d\}, \{a,c,d\}, \{b,c,d\}

因此(43)=4{4 \choose 3} = 4

同样,为了能够更加方便的计数它,我们当然也是有公式的。
我们发现,任意一个rr组合,都会有P(r,r)=r!P(r,r) = r! 种排列。而每一种排列,都会对应到一个组合。因此,一个集合的rr组合与排列之间存在以下关系:
(3.1)(nr)r!=P(n,r) {n \choose r}r! = P(n,r) \tag{3.1}

将排列数展开,我们可以得到下面的公式:
(3.2)(nr)=n!(nr)!r! {n \choose r} = {n! \over (n-r)!r! } \tag{3.2}

其中,n,rN+n,r \in N_+,且rnr\le n

组合数的基本性质

组合数有着许多非常有用的性质。首先一个非常基本的等式就是:
(4.1)(nr)=(nnr) {n \choose r} = {n \choose n-r} \tag{4.1}

直接利用组合数的公式可以得证这一性质。

通过对组合数进行求和,我们可以得知一个集合的子集数量:
(4.2)i=0n(ni)=2n \sum^n_{i=0} {n\choose i} = 2^n \tag{4.2}

这个等式当然可以用求和来得出。我们可以换个角度来考量它。一个集合的子集中,某些元素要么没有出现,要么就出现了。因此对于每个元素一共有两种选择,因此子集的总数为2n2^n个。

接下来是比较重要的帕斯卡公式
定理 对于所有的1kn1 1 \le k \le n-1的整数nnkk
(4.3)(nk)=(n1k)+(n1k1) {n\choose k} = {n-1\choose k} + {n-1\choose k-1} \tag{4.3}

证明 尝试将右式化为左式即可。
(n1k)+(n1k1)=(n1)!k!(nk1)!+(n1)!(k1)!(nk)!=(n1)!(nk)k!(nk)!+(n1)!kk!(nk)!=(n1)!nk!(nk)!=n!k!(nk)!=(nk) \begin{aligned} {n-1\choose k} + {n-1\choose k-1} &= {(n-1)! \over k!(n-k-1)!} + {(n-1)! \over (k-1)!(n-k)!} \\ &= {(n-1)!(n-k) \over k!(n-k)!} + {(n-1)!k \over k!(n-k)!} \\ &= {(n-1)!n \over k!(n-k)!} \\ &= {n! \over k!(n-k)!} \\ &= {n \choose k} \end{aligned}

这样就完成了证明。注意,在上面的证明中,利用到了阶乘的性质:(n1)!n=n!(n-1)!\cdot n=n!

不难发现,帕斯卡公式实际上是组合数的一个递推公式。
pascal-dp
上图是由帕斯卡公式得到的一个递推的关系图,这里以(43){4 \choose 3}为例。其中灰色的节点是无效的值。为了方便,直接将其设为00。除了(10)=(11)=1{1 \choose 0} = {1 \choose 1} = 1为初始值外,其它的值都是由其所指向的节点的值求和而来。
上面的步骤就是动态规划中的状态转移的过程。利用这个公式,我们可以在Θ(nr)\Theta(nr)的时间来计算大量的组合数。因此可以将此作为一个预处理。
在具体实现的时候只需注意一些无效的特殊情况,其中包括r>n r > n n<0n < 0r<0r < 0。这些情况下的值均设为00。然后通过(10){1 \choose 0}(11){1 \choose 1}这两个初始值就可以完成递推。


  1. 当然也可记作nPr_nP_rPrnP^n_r。 

  2. 《组合数学》中称之为”子集”,也许更为恰当。 

  3. 下面将使用集合的方式表示组合,如{a,b,c}\{a,b,c\}。 

  4. 也可记作nCr_nC_rC(n,r)C(n,r)CrnC^n_r。 


# NULL #