递归式的渐进分析
分治策略
- 分解(Divide)步骤将问题划分为一些子问题,子问题的形式与原问题一样,只是规模更小。
- 解决(Conquer)步骤递归地求解出子问题。如果子问题的规模足够小,则停止递归,直接求解。
- 合并(Combine)步骤将子问题的解组合成原问题的解。
- 当子问题足够大,需要递归求解时,我们称之为递归情况(recursive case)。
- 当子问题变得足够小,不再需要递归时,我们说递归已经“触底”,进入了基本情况(base case)
递归式求解的三种方法
- 代入法:我们猜测一个界,然后用数学归纳法证明这个界是正确的
- 递归树法:将递归式转换为一棵树,其结点表示不同层次的递归调用产生的代价。然后采用边界和技术来求解递归式。
- 主定理法
代入法求解递归式
- 步骤:
- 1.猜测解的形式。
- 2.用数学归纳法求出解中的常数,并证明解是正确的。
- 例子:归并排序\(T(n) =2T(\frac{n}{2})+n\)
- 猜测\(T(n) = O(n\log n)\),\(T(n) \le cn\log n\)
- \(T(n)=2T(\frac{n}{2})+n \le 2c\frac{n}{2} \log \frac{n}{2}+n=cn(\log n-\log 2)+n=cn\log n-(c-1)n\)
- 若要满足,\(T(n)<=c\log n\),只须满足\(c-1\ge 0,c\ge 1\)即可
递归树求解递归式
-
在递归树中,每个结点表示一个单一子问题的代价,子问题对应某次递归函数调用。我们将树中每层中的代价求和,得到每层代价,然后将所有层的代价求和,得到所有层次的递归调用的总代价。
-
假设递归式为\(T(n) = aT(\frac{n}{b})+f(n)\)
- \(a\):子问题的个数
- \(n/b\):每个子问题的规模
- \(f(n)\):当前层的额外代价 (划分、合并等)
-
画出递归树的结构:根节点为\(f(n)\),引出\(a\)个分支,每个子问题为\(T(\frac{n}{b})\),每个子问题又可以引出\(a\)个分支子问题,留下\(f(\frac{n}{b})\)的合并花费,每个子问题为\(T(\frac{n}{b^2})\),......一直分到最底层子问题为\(T(1)\)
-
然后计算每一层的代价:\(f(n)\),\(af(\frac{n}{b})\),\(a^2f(\frac{n}{b^2})\),...,\(a^kf(\frac{n}{b^k})\),\(\mathrm{LevelCost}(i)=a^i\cdot f\left(\frac n{b^i}\right)\) ,树高为\(H = \log_b n\),最后一层共\(a^H=a^{\log_b n}=n^{\log_b a}\)个节点,因此最后一层代价为\(T(1)\times n^{\log_b a} = \Theta(n^{\log_b a})\)
-
细节问题:\(a^{\log_b n } =n^{\log_b a}\)的证明
先对原式变形:
证明一个结论(利用对数的换底公式):
然后就可以轻易得出
-
总代价为
\[f(n)+\sum_{i=1}^{H-1=\log_b n-1} a^i\cdot f(\frac{n}{b^i}) + \Theta(n^{\log_b a}) \] -
例子: \(T(n) = 3T(\frac{n}{4}) + \Theta (n^2)\)

主定理求解递归式
- 对于递归式:\(T(n)=aT\left(\frac nb\right)+f(n)\quad\forall n>b\)
- 主定理如下:
- 第二种情况还需要满足regularity condition:\(af(n/b)\leq cf(n),c<1\)
- 例子:
- 归并排序:\(T(n) = 2T(\frac{n}{2})+n\)
- \(a=2,b=2,\log_b a = 1\),\(n^{\log_b a}=n\)
- \(f(n) = n = \Theta(n)\)
- \(T(n) = \Theta(n\log n)\)
递归树与主定理三种情况的联系
- 递归式为:\(T(n) = aT(\frac{T}{b})+f(n)\) 时
- 使用递归树分析:

- 我们可以得出总代价
第一种情况
直观理解:
- 当\(f(n)=O(n^{\log_b(a)-\epsilon})\)时:
- 此时\(f(n)\)没有 \(n^{\log_b a}\) 增长速度快,中间项的增长速度随层数增加而增加,因此递归式总代价中\(\Theta(n^{\log_b a})\) 为增长最大的项,其他均为低阶项,因此在渐进分析中可以忽略
- 简记:头轻尾重取尾重
证明:
- 每一中间项
不难发现\(C_i\)的渐进上界是一个随 \(i\) 递增的函数,因此层数越深,所需时间代价越大,直观上来讲,就是最后一层的时间代价占主导地位,证明如下
\(H = \log_b n\),进一步化简
所以
第二种情况
直观理解:
- 当\(f(n)=\Omega(n^{\log_b(a)+\epsilon})\)时:
- 此时\(f(n)\)比 \(n^{\log_b a}\) 增长速度快,中间项的增长速度随层数增加而减小,递归式总代价中\(f(n)\) 为增长最大的项,对于中间项\(a^if(\frac{n}{b^i})\)一定没有\(f(n)\)增长快,因此其他项相对\(f(n)\)低阶,均可以忽略。别忘了满足正则条件 \(af(n/b)\leq cf(n),c<1\)
- 简记:头重尾轻取头重
证明
- 中间项
不难发现,\(C_i\)的渐进下界是一个随 \(i\) 递减的函数,因此层数越深,所需时间代价越小,直观上来讲,就是第一层的时间代价占主导地位,证明如下
等等,我们发现这个符号好像反了,我们需要证明这个和要小于\(f(n)\),而这是大等号,怎么办呢?这时候就要引入正则条件(\(af(n/b)\leq cf(n),c<1\))了
由正则条件,(\(0<c<1\))
可知
因此让我们回到刚刚的式子
所以中间项都比第一项小,再考虑最后一层
因为
所以\(f(n)\),肯定比最后一层增长快,最后一层代价被忽略
所以得到结论
第三种情况
直观理解:
- 当\(f(n)=\Theta(n^{\log_ba}\log^kn)\)时
- 这意味着$ f(n)$ 和 “分治部分的代价” \(n^{\log_b a}\) 增长速度几乎一样,只是多了一层对数因子。
- 在递归树中,每一层的代价不会快速衰减或增长,而是大致相同数量级。
- 此时相当于每一层代价累加后得到
- 简记:头尾均匀取累重
证明
- 对每一层代价
- 总代价
对于比较复杂的求和部分单独分析:
我们发现
而 \(\log n-i\log b\) 部分对 \(i\) 而言是线性增长的,如果令\(m=\log_b n\),\(j=m-i\),则\(i\)从\(0\)到\(m\)变化的同时,\(j\)从\(m\)到\(0\) 变化,它们变化区间一致,所以
于是就化简成了一个简单的形式
通过Faulhaber 公式
可知(才不是懒得证了才直接套结论呢~)
因此

浙公网安备 33010602011771号