【算法】--递归
To iterate is human, to recurse, divine.
人理解迭代,神理解递归。
递归的基本思想是把规模大的问题转化为规模小的相似的子问题来解决。在函数实现时,因为解决大问题的方法和解决小问题的方法往往是同一个方法,所以就产生了函数调用它自身的情况。另外这个解决问题的函数必须有明显的结束条件,这样就不会产生无限递归的情况了。
一、递归的处理技巧
- 在求解递归方程时,通常会忽略一些技术细节,比如,假定自变量是整数,忽略上下取整符号;
- 通常会忽略递归方程的边界条件,对于足够小的n,将T(n)作是一个常数,令T(n)=Θ(1);
- 在有些特殊情况下,技术细节非常重要,需要重视上下取整符合和边界条件,期望得到更好的结论。
二、解递归的方法
代入法:
步骤:
- 猜测解的形式
- 数学归纳法证明
- 解出常数c,n0
例子:
T(n) = 4T(n/2) + 100n
- 认定 T(1) = Θ(1)
- 猜测解为 O(n^{3})
- 当 k < n时,假定 T(k) ≤ck3
- 数学归纳法证明 T(n) ≤cn3
当,时,
如何获得好的猜解:
- 熟能生巧,猜测需要经验;
- 通过构建递归树获得猜测解;
- 如果某个递归方程和以往的某方程相似,则有理由猜测其解也相似;
- 没有通用的有效方法获得递归方程正确的猜测解。
代入法需要注意的问题:
- 有时,我们也许猜出递归式的正确渐进界,但却会在归纳证明时出现一些问题;
- 归纳假设不够强,无法证明其准确的界;
- 通过去掉一个比猜测解低一阶的项来修改所猜的界。
递归树:
- 递归树来模型化一个算法递归执行的代价;
- 递归树有助于获得一个好的猜测,然后用代入法去证明。
- 在递归树中,每个结点代表一个代价
- 递归树中每行节点的代价之和(每行代价)称为行和
- 对所有的行和求和,可得总的代价
例子:
递归树小结
- 由于递归树方法通常用来获得好的猜测解,其解的正确性可以用代入法来证明。因此,在构造递归树时通常省略一些无关紧要的细节,例如上取整和下取整等;
- 如果在构造递归树以及求解和时就非常认真仔细,也可以直接用递归树方法来求解递归方程;
- 在递归树方法中,关键是求出行和、总的代。为求得行和,一个有效的办法是根据递归方程本身,推导出下一行的和与上一行的和之间的关系,由此,可使得求和过程变得有章可循,从而也更为简单。
主方法:
主方法给出求解形如T(n) = aT(n/b) + f(n)的递归表达式的定理性方法,这里常数a>=1,b>1,f(n)是一个渐进趋正的函数(存在n0,当n>n0,有f(n)>0)。
递归式T(n) = aT(n/b) + f(n)描述了将规模为n的问题划分为a个子问题的算法的运行时间,每个子问题规模为n/b,a和b是正整数。a个子问题被分别递归地求解,时间各为T(n/b)。划分原问题和合并答案的代价由函数f(n)描述。
**主定理:**设a>=1,b>1为常数,f(n)是一个函数,T(n)由递归式T(n) = aT(n/b) + f(n)对非负整数定义。那么T(n)可能有如下的渐进界:
例子:
注意事项:
三种情况没有覆盖所有可能的f(n);
case 1和case 2之间存在一条“沟”,即f(n)小于但不是多项式地小于nlogba;