文章目录
前言
递归的小结,优缺点,并且如何分析递归的时间复杂度:猜测数学归纳发,而猜测数学归纳又包括:直接猜测,递归树猜测,其中又包含着各种特殊情况要减去一点低阶项或者要合并上下取整,而除了猜测数学归纳法,还有主定理,主定理第三个还得注意前提条件
一、递归小结
- 优点:结构清晰,可读性强,而且容易用数学归纳法来证明算法的正确性,因此它为设计算法、调试程序带来很大方便。
- 缺点:递归算法的运行效率较低,无论是耗费的计算时间还是占用的存储空间都比非递归算法要多
- 解决方法:在递归算法中消除递归调用,使其转化为非递归算法。
1、采用一个用户定义的栈来模拟系统的递归调用工作栈。该方法通用性强,但本质上还是递归,只不过人工做了本来由编译器做的事情,优化效果不明显。
2、用递推来实现递归函数。
3、通过变换能将一些递归转化为尾递归,从而迭代求出结果。
后两种方法在时空复杂度上均有较大改善,但其适用范围有限。
二、递归分析
递归分析的目的:得到递归算法对应递归方程的渐进界Θ或者O
递归分析技巧:在求解递归方程时,我们可以忽视一些技术细节,它不会影响到最终的渐进结果。
- 假设T(n)中n是一个非负整数。
- 忽略掉上取整或者下取整
- 例如T(n)=2T(⌈n∕2⌉)和T(n)=2T(⌊n∕2⌋)都与T(n)=2T(n∕2)等价。
- 当n充分小时,我们认为T(n)=T(0),可以简单设T(1)=1, T(0)=0。
替换法
- 替换法(Substitution method)
- 猜测一个界(直接猜或者根据递归树猜)
- 用数学归纳法证明这个猜测是正确的
直接猜测
例子1
考虑选择排序比较次数的递归方程
T(n)=T(n−1)+n−1
解答
- 猜测T(n)=O(n^2)。
- 证明T(n)≤cn^2:
- 边界条件:取n=1, T(1)=1≤c1^2,因此选择c≥1。
- 归纳步骤:假设T(n−1)≤c(n−1)^2,
- T(n)≤c(n−1)^2+n−1
-
=cn^2−2cn+c+n−1
-
≤cn^2−2cn+2c+n−1
-
=cn^2−(2c−1)(n−1)≤cn^2 (c≥1∕2)
减去低价项的例子
考虑Hanoi问题所需移动次数的递归方程
T(n)=2T(n−1)+1
- 猜测T(n)=O(2^n)。
- 证明T(n)≤c2^n:
- 边界条件:取n=1, T(1)=1≤c2^1,因此选择c≥1/2。
- 归纳步骤:假设T(n−1)≤c2^n−1,
- T(n)≤2c2^n−1+1
- =c2n+1≤c2n
- 怎么办?
有时猜测是正确的,但数学归纳法不能奏效。
问题在于归纳假设的强度不够,不足以证明更确切的界。
通过减去低阶项来修正猜测的方法通常会使数学证明通过。
- (续)考虑Hanoi问题所需移动次数的递归方程
- T(n)=2T(n−1)+1
- 猜测T(n)=O(2^n)。
- 证明T(n)≤c2^n−b:
- 边界条件:取n=1, T(1)=1≤c2^1−b,选择c≥1+b/2。
- 归纳步骤:假设T(n−1)≤c2^n−1−b,
- T(n)≤2(c2^n−1−b)+1
-
=c2^n−2b+1≤c2^n−b (对于b≥1)
- T(n)≤c2n−b能推导出T(n)≤c2n,因此猜测成立。
去掉取整数的例子
使用替换法给出下列递归方程的渐进界。
T(n)=T(⌊n/2⌋)+T(⌈n/2⌉)+1
- 猜测T(n)=O(n)。
- 证明T(n)≤cn−b:
- 边界条件:取n=1, T(1)=1≤c−b,选择c≥1+b。
- 归纳步骤:假设T(⌊n/2⌋)≤c⌊n/2⌋−b, T(⌈n/2⌉)≤c⌈n/2⌉−b
- T(n)≤c⌊n/2⌋−b+c⌈n/2⌉−b+1
-
=cn−2b+1≤cn−b (对于b≥1)
- T(n)≤cn−b能推导出T(n)≤cn,因此猜测成立
考虑递归方程
T(n)=8T(n/2)+5n^2
猜测T(n)=O(n^3)。
证明T(n)≤cn^3:
边界条件:取n=1, T(1)=1≤c,因此选择c≥1。
归纳步骤:假设T(n/2)≤c(n/2)^3,
T(n)≤8c(n/2)3+5n2
=cn3+5n2 <cn^3
应该减去一个低阶项。
(续)考虑递归方程
T(n)=8T(n/2)+5n^2
猜测T(n)=O(n^3)。
证明T(n)≤cn3−bn2:
边界条件:取n=1, T(1)=1≤c−b,因此选择c≥b+1。
归纳步骤:假设T(n/2)≤c(n/2)3−b(n/2)2,
T(n)≤8c(n/2)3−8b(n/2)2+5n^2
=cn3−2bn2+5n2≤cn3−bn^2(b≥5)
T(n)≤cn3可由T(n)≤cn3−bn^2导出。证毕!
变量替换法
递归树猜测
注意高度的选择,以及求和
注意每一层都是一样
主定理
主定理有三种情形
每种情形的结果都可以由主定理直接推导,无需繁琐计算
令 𝑎 ≥ 1 和 𝑏 > 1 是常数, 𝑓(𝑛) 是一个函数,𝑇(𝑛)定义在非负整数上且满足递归
T(n)=aT(n/b)+f(n)
其中𝑛/𝑏 可以是⌊n/b⌋或者⌈n/b⌉。那么𝑇(𝑛) 的渐进界有下述三种情形:
如果对于某个ε>0有f(n)=O(nlog_ba−ε),则T(n)=Θ(nlog_ba)。
如果f(n)=Θ(nlog_ba),则T(n)=Θ(nlog_balg n)。
如果对于某个ε>0有f(n)=Ω(n^log_ba+ε),并且对某个常数c<1和所有充分大的n都有af(n/b)≤cf(n),则T(n)=Θ(f(n))。
主方法用于递归式
T(n)=aT(n/b)+f(n)
这个递归描述的是将一个规模是n的问题划分成a个规模是n/b的子问题。
通过比较n^log_ba和f(n)可知,要想减少递归算法的开销,我们可以:
减小f(n)
减小a
增大b
注意的事,前提条件的满足
总结
怎样做出一个好的猜测?
坏消息:
- 没有总能猜对的通用方法
- 好的猜测=经验+创造力+运气
好消息: - 递归树通常能帮助我们得到正确的猜测。
- 递归树不是严格的证明,猜测到一个界之后还需要替代法去证明
递归树需要巧妙的计算