这是离散数学的第四篇,讨论高级计数技术。同步发布与个人博客,上一篇(【离散数学】计数/排列组合)讨论了计数以及排列组合,二项式定理等。但是仅凭排列组合等手段依然无法解决许多计数问题。这里首先讨论通过递推关系来求解计数问题,并介绍有递推关系引出的两个算法范式:动态规划和分治。这两种算法均是通过将问题分割为一系列的子问题来求解的,区别就是前者分割出来的子问题互相重叠,后者的子问题不重叠。这是两种很重要的算法,加上贪心、回溯、分支定界为五种很常用的算法。这里仅仅简单讨论思路,并分析其复杂度。关于具体的算法分析以及算法设计以后应该会讨论。而后介绍了求解一类很常见的特定递推关系——常系数线性齐次与非齐次递推关系的形式解法。并且还介绍了一种求解计数问题的很重要的手段——生成函数,这是幂级数的应用。最后介绍容斥原理,对,就是集合的容斥原理。以上内容以前均有涉及,这里再次探讨。
递推关系的应用
经典问题——汉诺塔
这是一个及其经典的问题,见汉诺塔(Tower of Hanoi),三根柱子,n个盘子,从上到下盘子从小到大。将所有n个盘子从一根柱子移动到另一根柱子,移动过程中小的盘子不能放在大的盘子下面。可以求解出所需要的最小步数,还可以编写算法打印出所有步数。
n=3的汉诺塔移动方法:
令移动n个盘子到另一个柱子所需最少次数为 ,考虑最下面一个最大的盘子,由于小的盘子不能放在大的盘子下面,所以必须首先将上面n-1个移动到另一个柱子,再将最下面的最大的一个移动到另一根柱子,再将n-1个移到其上,则完成了n个盘子的移动。则得到递推关系 。初始条件很容易知道: , 。
求解递推关系:容易看出 ,可以得到 。可以证明这便是移动n个盘子所需的最少次数。
卡特兰数
考虑一个在n+1个数
的乘积中插入括号来规定乘法次序的方式数,令其为
。
这里注意到一定会有一个乘法是在括号外面的(不需要加括号),假设其在
和
之间,则存在
种方式,考虑最后一个乘号可能取n个位置,则有
初始条件则为
,
。
利用生成函数的方法可以证明:
,被称作第n个卡特兰(Catalan)数,序列
被称为卡特兰数的序列。参见 OEIS A000108,Catalan number - Wikipedia。
动态规划与递推关系
动态规划(Dynamic programming,DP)是一种算法范式,遵循动态规划范式的算法是将原问题分解为更简单的重叠的子问题,通过子问题的求解来求解原问题。常用于求解最短路线、库存管理、资源分配、设备更新、排序、装载等问题。此类问题若用分治法来解,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。所以DP通过保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,从而降低复杂度。以后单独讨论,这里推荐阅读:动态规划解决01背包问题,什么是动态规划?动态规划的意义是什么?。
求解线性递推关系
这部分给人的感觉相当熟悉,在高等数学中有线性微分方程的求解,线性代数中有线性方程组的求解,与这里的线性递推关系的求解十分类似,可以说是如出一辙。
求解常系数线性齐次递推关系
一个常系数的 阶线性齐次递推关系指的是形如 ,的递推关系,其中 为实数, 。
为了求解 阶常系数线性齐次递推关系,这里的基本方法是寻找形如 的解,其中r是常数。递推关系要有形如 的解,则当且仅当 是方程
将上述方程称为该递推关系的 特征方程。方程的解称为 特征根。(与求解常系数线性微分方程如出一辙)。特征根有可能是复数,但这里仅考虑特征根为实数的情况。
定理:假设特征方程
是常数。
另外,对其中的每一个特征根
,如果并非一重而是
重时,则用
替代 上述解中的
即可。
例:斐波那契数列(OEIS A000045)
递推关系
,初始条件
。
特征方程
根为
,
。
则
则得到斐波那契数列的显式公式为
求解常系数线性非齐次递推关系
常系数线性非齐次递推关系:形如 ,与其相伴的齐次递推关系为 ,很显然 通解 = 特解+齐次解。
齐次解即对应的常系数线性齐次递推关系的解,记作 ,特解记作 。
不同形式的F(n)具有不同形式的特解
特解形式 | |
---|---|
若
形如
.
则当
不是特征根时,特解形如
.
当
是
重特征根时,特解形如
.
分治算法与递推关系
与动态规划相似,分治算法(Divide and conquer algorithm)范式也会将问题划分为一个或者多个小问题,不过这些小问题是不重叠的。连续使用这种划分直到可以快速找到这些小问题的解,然后将小问题的解合并为原问题的解。即三个步骤:分割原问题,解决子问题,合并得到最终解。常见简单分治算法:归并排序、二分查找。这里将说明怎样用递关系来分析分治算法的复杂度。
分治递推关系
假设一个递归算法将规模为n的问题划分为a个子问题,每个子问题规模为n/b,并且需要g(n)的额外运算来合并这些子问题。用f(n)表示求解问题规模为n的问题所需运算数,则
令
,多次迭代后可以得到:
很容易知道,
二分查找的分治递推关系:
归并排序的分治递推关系:
分治算法的复杂度分析
定理1:设
是满足
的增函数,
被
整除,
,
是大于1的整数,
是正实数。那么
很容易得出二分查找复杂度为 。
主定理:若 ,则
同理,令 即可证得。(ps:上面的 表示大于号,但其实这个符号并不是这个意思。这里有一个bug,hexo自带一个功能的会把一段内连续的<>之间的内容注释掉,于是就只好将就一下。)
根据主定理,很容易得出归并排序复杂度为
。
可以看到,定理1只是主定理的特殊情况。
这里仅简单分析分治算法,并解决了其复杂度的问题,并未涉及分治算法的设计及具体实现。
生成函数
表示序列的一个有效方法是生成函数,把序列的项作为形式幂级数的变量x的幂的系数。可以用生成函数解决许多类型的计数问题。拓展阅读:Generating function - Wikipedia,什么是生成函数? | Matrix67: The Aha Moments。
定义
实数序列 的(普通)生成函数是无穷级数
例:序列 的生成函数即是 .
使用生成函数求解计数问题时,通常考虑形式幂级数,即不需要考虑其收敛域(对发散或收敛并不感兴趣)
广义二项式定理
广义二项式系数:
为实数,
为非负整数,广义二项式系数定义为
当u为负整数时,展开即可有下列式子成立
广义二项式定理:
是实数,
,
是实数,那么
其实这就是 的幂级数展开,使用麦克劳林级数(即在x=0处泰勒展开)即可证明。
常用生成函数
这里给出一些最常用生成函数,以及其对应的序列一般项。
(1).
,
,二项式定理得到
(2).
,
;否则为0,几何级数求和得到
(3).
,
,对
的几何级数求和取极限得到
(4).
,
,对
求导得到
(5).
,
,由广义二项式定理得到
(6).
,
,泰勒展开即可得到
(7).
,
,同上泰勒展开得到
生成函数可以用来求解计数问题,还可以用来求解递推关系和证明组合恒等式。这里不想写了,略过吧!(笑)
容斥原理及其应用
两个集合的容斥原理是很熟悉的, ,那么对于多个几个呢?很容易想到,但可能并不容易写出来。Inclusion–exclusion principle。
容斥原理
设 是有穷集,则
可以看到加号和减号是交替出现的,保证了没有遗漏也没有重复。
三个集合的容斥原理:
应用很多,此处愉快地略过。
结语
其实我只想快速看完这本书,写完了之后,好好想一想应该怎样去写。还有慢慢肝算法导论,重新回顾数据结构,一堆技术书等着去肝呢!所有省略了很多内容,对自己还未掌握的东西抄上来就没有多大意义了。给了很多链接,但其实大部分链接我都未曾去认真看过(笑)。
逐步积累,随性记录,知道自己要去的地方、要走的路,一直写着就好。——201.3.7
参考资料:《离散数学及其应用》(本科教学版,Kenneth H.Rosen著,原书第七版)