您的位置:首页 > 理论基础

【离散数学】高级计数技术

2018-03-08 19:13 916 查看
这是离散数学的第四篇,讨论高级计数技术。同步发布与个人博客,上一篇(【离散数学】计数/排列组合)讨论了计数以及排列组合,二项式定理等。但是仅凭排列组合等手段依然无法解决许多计数问题。这里首先讨论通过递推关系来求解计数问题,并介绍有递推关系引出的两个算法范式:动态规划和分治。这两种算法均是通过将问题分割为一系列的子问题来求解的,区别就是前者分割出来的子问题互相重叠,后者的子问题不重叠。这是两种很重要的算法,加上贪心、回溯、分支定界为五种很常用的算法。这里仅仅简单讨论思路,并分析其复杂度。关于具体的算法分析以及算法设计以后应该会讨论。而后介绍了求解一类很常见的特定递推关系——常系数线性齐次与非齐次递推关系的形式解法。并且还介绍了一种求解计数问题的很重要的手段——生成函数,这是幂级数的应用。最后介绍容斥原理,对,就是集合的容斥原理。以上内容以前均有涉及,这里再次探讨。

递推关系的应用

经典问题——汉诺塔

这是一个及其经典的问题,见汉诺塔(Tower of Hanoi),三根柱子,n个盘子,从上到下盘子从小到大。将所有n个盘子从一根柱子移动到另一根柱子,移动过程中小的盘子不能放在大的盘子下面。可以求解出所需要的最小步数,还可以编写算法打印出所有步数。



n=3的汉诺塔移动方法:



令移动n个盘子到另一个柱子所需最少次数为 HnHn ,考虑最下面一个最大的盘子,由于小的盘子不能放在大的盘子下面,所以必须首先将上面n-1个移动到另一个柱子,再将最下面的最大的一个移动到另一根柱子,再将n-1个移到其上,则完成了n个盘子的移动。则得到递推关系 Hn=2Hn−1+1Hn=2Hn−1+1。初始条件很容易知道:H0=0H0=0,H1=1H1=1。

求解递推关系:容易看出 Hn+1=2(Hn−1+1)Hn+1=2(Hn−1+1),可以得到 Hn=2n−1Hn=2n−1。可以证明这便是移动n个盘子所需的最少次数。

卡特兰数

考虑一个在n+1个数 x0⋅x1⋅x2⋯xn<
20000
/span>x0⋅x1⋅x2⋯xn 的乘积中插入括号来规定乘法次序的方式数,令其为 CnCn。

这里注意到一定会有一个乘法是在括号外面的(不需要加括号),假设其在xkxk和xk+1xk+1之间,则存在CkCn−k−1CkCn−k−1种方式,考虑最后一个乘号可能取n个位置,则有Cn=∑k=0n−1CkCn−k−1Cn=∑k=0n−1CkCn−k−1

初始条件则为C0=1C0=1,C1=1C1=1。

利用生成函数的方法可以证明:Cn=C(2n,n)n+1Cn=C(2n,n)n+1,被称作第n个卡特兰(Catalan)数,序列{Cn}{Cn} 被称为卡特兰数的序列。参见 OEIS A000108Catalan number - Wikipedia

动态规划与递推关系

动态规划(Dynamic programming,DP)是一种算法范式,遵循动态规划范式的算法是将原问题分解为更简单的重叠的子问题,通过子问题的求解来求解原问题。常用于求解最短路线、库存管理、资源分配、设备更新、排序、装载等问题。此类问题若用分治法来解,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。所以DP通过保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,从而降低复杂度。以后单独讨论,这里推荐阅读:动态规划解决01背包问题什么是动态规划?动态规划的意义是什么?

求解线性递推关系

这部分给人的感觉相当熟悉,在高等数学中有线性微分方程的求解,线性代数中有线性方程组的求解,与这里的线性递推关系的求解十分类似,可以说是如出一辙。

求解常系数线性齐次递推关系

一个常系数的kk阶线性齐次递推关系指的是形如 an=c1an−1+c2an−2+⋯+ckan−kan=c1an−1+c2an−2+⋯+ckan−k,的递推关系,其中cici为实数,ck≠0ck≠0。

为了求解kk阶常系数线性齐次递推关系,这里的基本方法是寻找形如an=rnan=rn的解,其中r是常数。递推关系要有形如an=rnan=rn的解,则当且仅当rr是方程rk−c1rk−1−c2rk−2−⋯−ck−1r−ck=0rk−c1rk−1−c2rk−2−⋯−ck−1r−ck=0的解。

将上述方程称为该递推关系的特征方程。方程的解称为特征根。(与求解常系数线性微分方程如出一辙)。特征根有可能是复数,但这里仅考虑特征根为实数的情况。

定理:假设特征方程rk−c1rk−1−⋯−ck=0rk−c1rk−1−⋯−ck=0有k个不相等的根r1,r2,...,rkr1,r2,...,rk。那么递推关系an=c1an−1+c2an−2+⋯+ckan−kan=c1an−1+c2an−2+⋯+ckan−k的解为an=α1rn1+α2rn2+⋯+αkrnkan=α1r1n+α2r2n+⋯+αkrkn

n∈N,α1,α2,...,αkn∈N,α1,α2,...,αk是常数。

另外,对其中的每一个特征根riri,如果并非一重而是mimi重时,则用 (αi,0+αi,1n+⋯+αi,mi−1nmi−1)rni(αi,0+αi,1n+⋯+αi,mi−1nmi−1)rin 替代 上述解中的αirniαirin即可。

例:斐波那契数列(OEIS A000045

递推关系fn=fn−1+fn−2fn=fn−1+fn−2,初始条件f0=0,f1=1f0=0,f1=1。

特征方程r2−r−1=0r2−r−1=0 根为 r1=(1+5–√)/2r1=(1+5)/2,r2=(1−5–√)/2r2=(1−5)/2。

则fn=α1(1+5–√2)n+α2(1−5–√2)nfn=α1(1+52)n+α2(1−52)n代入f0,f1f0,f1解出α1=15–√α1=15,α2=−15–√α2=−15

则得到斐波那契数列的显式公式为fn=15–√(1+5–√2)n−15–√(1−5–√2)nfn=15(1+52)n−15(1−52)n

求解常系数线性非齐次递推关系

常系数线性非齐次递推关系:形如 an=c1an−1+c2an−2+⋯+ckan−k+F(n)an=c1an−1+c2an−2+⋯+ckan−k+F(n),与其相伴的齐次递推关系为 an=c1an−1+c2an−2+⋯+ckan−kan=c1an−1+c2an−2+⋯+ckan−k,很显然 通解 = 特解+齐次解

齐次解即对应的常系数线性齐次递推关系的解,记作a(h)nan(h),特解记作a(p)nan(p)。

不同形式的F(n)具有不同形式的特解

F(n)F(n)特解形式
an+ban+bcn+dcn+d
α⋅cnα⋅cnβ⋅cnβ⋅cn
若 F(n)F(n) 形如 (btnt+bt−1nt−1+⋯+b1n+b0)sn(btnt+bt−1nt−1+⋯+b1n+b0)sn.

则当 ss 不是特征根时,特解形如 (ptnt+pt−1nt−1+⋯+p1n+p0)sn(ptnt+pt−1nt−1+⋯+p1n+p0)sn.

当 ss 是mm重特征根时,特解形如 nm(ptnt+pt−1nt−1+⋯+p1n+p0)snnm(ptnt+pt−1nt−1+⋯+p1n+p0)sn.

分治算法与递推关系

与动态规划相似,分治算法(Divide and conquer algorithm)范式也会将问题划分为一个或者多个小问题,不过这些小问题是不重叠的。连续使用这种划分直到可以快速找到这些小问题的解,然后将小问题的解合并为原问题的解。即三个步骤:分割原问题,解决子问题,合并得到最终解。常见简单分治算法:归并排序二分查找。这里将说明怎样用递关系来分析分治算法的复杂度。

分治递推关系

假设一个递归算法将规模为n的问题划分为a个子问题,每个子问题规模为n/b,并且需要g(n)的额外运算来合并这些子问题。用f(n)表示求解问题规模为n的问题所需运算数,则f(n)=af(n/b)+g(n)f(n)=af(n/b)+g(n)

令n=bkn=bk,多次迭代后可以得到:

f(n)=akf(1)+∑j=0k−1ajg(n/bj)f(n)=akf(1)+∑j=0k−1ajg(n/bj)

很容易知道,

二分查找的分治递推关系:f(n)=f(n/2)+2f(n)=f(n/2)+2

归并排序的分治递推关系:M(n)=2M(n/2)+nM(n)=2M(n/2)+n

分治算法的复杂度分析

定理1:设f(n)f(n)是满足f(n)=af(n/b)+cf(n)=af(n/b)+c 的增函数,nn被bb整除,a⩾1a⩾1,

bb是大于1的整数,cc是正实数。那么

f(n)={O(nlogba)O(logn)a>1a=1f(n)={O(nlogba)a>1O(log⁡n)a=1证明:令n=bkn=bk即可证得,当n≠bkn≠bk时,依然成立。

很容易得出二分查找复杂度为O(logn)O(log⁡n)。

主定理:若f(n)=af(n/b)+cndf(n)=af(n/b)+cnd,则f(n)=⎧⎩⎨⎪⎪O(nd)O(ndlogn)O(nlogba)a<bda=bda≻bdf(n)={O(nd)a<bdO(ndlog⁡n)a=bdO(nlogba)a≻bd

同理,令n=bkn=bk即可证得。(ps:上面的≻≻表示大于号,但其实这个符号并不是这个意思。这里有一个bug,hexo自带一个功能的会把一段内连续的<>之间的内容注释掉,于是就只好将就一下。)

根据主定理,很容易得出归并排序复杂度为O(nlogn)O(nlog⁡n)。

可以看到,定理1只是主定理的特殊情况。

这里仅简单分析分治算法,并解决了其复杂度的问题,并未涉及分治算法的设计及具体实现。

生成函数

表示序列的一个有效方法是生成函数,把序列的项作为形式幂级数的变量x的幂的系数。可以用生成函数解决许多类型的计数问题。拓展阅读:Generating function - Wikipedia什么是生成函数? | Matrix67: The Aha Moments

定义

实数序列a0,a1,...,ak,...a0,a1,...,ak,...的(普通)生成函数是无穷级数G(x)=∑k=0∞akxkG(x)=∑k=0∞akxk

例:序列{C(n,k)}{C(n,k)}的生成函数即是G(x)=(1+x)nG(x)=(1+x)n.

使用生成函数求解计数问题时,通常考虑形式幂级数,即不需要考虑其收敛域(对发散或收敛并不感兴趣)

广义二项式定理

广义二项式系数:

uu为实数,kk为非负整数,广义二项式系数定义为

(uk)={u(u−1)⋯(u−k−1)/k!1k>0k=0(ku)={u(u−1)⋯(u−k−1)/k!k>01k=0

当u为负整数时,展开即可有下列式子成立(−nk)=(−1)rC(n+r−1,r)(k−n)=(−1)rC(n+r−1,r)

广义二项式定理:

xx是实数,|x|<1|x|<1,uu 是实数,那么(1+x)u=∑k=0∞(uk)xk(1+x)u=∑k=0∞(ku)xk

其实这就是(1+x)u(1+x)u的幂级数展开,使用麦克劳林级数(即在x=0处泰勒展开)即可证明。

常用生成函数

这里给出一些最常用生成函数,以及其对应的序列一般项。

(1). (1+ax)n=∑nk=0C(n,k)akxk(1+ax)n=∑k=0nC(n,k)akxk,ak=C(n,k)akak=C(n,k)ak,二项式定理得到

(2). 1−xr+11−x=∑nk=0xk1−xr+11−x=∑k=0nxk,ak=1,k⩽nak=1,k⩽n;否则为0,几何级数求和得到

(3). 11−ax=∑∞k=0akxk11−ax=∑k=0∞akxk,ak=akak=ak,对|x|<1|x|<1的几何级数求和取极限得到

(4). 1(1−x)2=∑∞k=0(k+1)xk1(1−x)2=∑k=0∞(k+1)xk,ak=k+1ak=k+1,对11−x11−x求导得到

(5). 1(1−x)n=∑∞k=0C(n+k−1,k)xk1(1−x)n=∑k=0∞C(n+k−1,k)xk,ak=C(n+k−1,k)=C(n+k−1,n−1)ak=C(n+k−1,k)=C(n+k−1,n−1),由广义二项式定理得到

(6). ex=∑∞k=0xkk!ex=∑k=0∞xkk!,ak=1/k!ak=1/k!,泰勒展开即可得到

(7). ln(1+x)=∑∞k=0(−1)k+1kxkln⁡(1+x)=∑k=0∞(−1)k+1kxk,ak=(−1)k+1/kak=(−1)k+1/k,同上泰勒展开得到

生成函数可以用来求解计数问题,还可以用来求解递推关系和证明组合恒等式。这里不想写了,略过吧!(笑)

容斥原理及其应用

两个集合的容斥原理是很熟悉的,|A∪B|=|A|+|B|−|A∩B||A∪B|=|A|+|B|−|A∩B|,那么对于多个几个呢?很容易想到,但可能并不容易写出来。Inclusion–exclusion principle

容斥原理

设A1,A2....,AnA1,A2....,An是有穷集,则|A1∪A2∪⋯∪An|=∑1⩽i⩽n|Ai|−∑1⩽i<j⩽n|Ai∩Aj|+∑1⩽i<k<j⩽n|Ai∩Aj∩Ak|+⋯+(−1)n|A1∩A2∩⋯∩An||A1∪A2∪⋯∪An|=∑1⩽i⩽n|Ai|−∑1⩽i<j⩽n|Ai∩Aj|+∑1⩽i<k<j⩽n|Ai∩Aj∩Ak|+⋯+(−1)n|A1∩A2∩⋯∩An|

可以看到加号和减号是交替出现的,保证了没有遗漏也没有重复。

三个集合的容斥原理:





应用很多,此处愉快地略过。

结语

其实我只想快速看完这本书,写完了之后,好好想一想应该怎样去写。还有慢慢肝算法导论,重新回顾数据结构,一堆技术书等着去肝呢!所有省略了很多内容,对自己还未掌握的东西抄上来就没有多大意义了。给了很多链接,但其实大部分链接我都未曾去认真看过(笑)。

逐步积累,随性记录,知道自己要去的地方、要走的路,一直写着就好。——201.3.7

参考资料:《离散数学及其应用》(本科教学版,Kenneth H.Rosen著,原书第七版)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息