「笔记」递归算法复杂度分析
写在前面
可恶的算法分析与设计!!!
递归算法形式
对于一个输入规模为 \(n\) 的递归算法,每次均为将整个问题划分为 \(a\) 个规模为 \(\frac{n}{b}\) 的子问题,回溯时将所有子问题合并需要 \(f(n)\) 的时间,定义函数 \(T(n)\) 代表该递归算法所需时间,则有递推式:
递归树大力求和
根据递推式画出递归树。对于上述递归式其递归树形如:
- 根节点代表规模为 \(n\) 的原问题。
- 对于规模为 \(x\) 的节点,其子节点数为 \(a\),代表该问题分治后的所有规模为 \(\frac{x}{b}\) 的子问题。
- 对于规模为 \(x\) 的问题对应的子节点,节点上的值为 \(f(x)\)。
- 每层的右侧标出当前层中所有节点的和。
对递归树逐层右侧的值求和求和即得 \(T(n)\) 的值。
一个实例:
主定理 Master Theorem
对于上述函数 \(T(n)\),有:
特别地,情况(2)中还需满足存在常数 \(c<1\),当 \(n\rightarrow +\infin\) 时 \(af\left(\frac{n}{b}\right)\le cf(n)\) 恒成立。
证明即考虑将上述时间含义的递归树并逐层求和,上述公式中莫名奇妙的 \(n^{\log_b a}\) 实际上即为递归树最底层的所需时间为 \(\Theta(1)\) 的子问题数量。具体证明过程详见 OI-wiki。
根据证明过程可以得到上述三种情况分别代表的实际情况:
- (1)瓶颈在于处理所有叶节点所需时间 \(\Theta(1)\times O(n^{\log_b a})\)。
- (2)瓶颈在于分割与合并过程 \(f(n)\)。
- (3)分割与合并过程与处理所有叶节点所需时间同级,上述额外限制 \(af\left(\frac{n}{b}\right)\le cf(n)\) 即限制了原问题分割后子问题的增长速率不会超过其本身。
典题
大部分情况下可以直接套主定理秒了,如果套不上公式只能大力求和。
1
有 \(a = 2, b = 2, \log_b a = \log_2 2 = 1\)。当取 \(\epsilon \in (0, 1]\) 时,有 \(f(n) = O(n^{\log_b (a) - \epsilon}) = O(n^{1 - \epsilon})\),则由主定理(1)有:
2
有 \(a = 1, b = 2, \log_b a = \log_2 1 = 0\)。当取 \(\epsilon \in (0, 1]\) 时,有 \(f(n) = \Omega(n^{\log_b (a) + \epsilon}) = O(n^{\epsilon})\),则由主定理(2)有:
3
即为归并排序的复杂度。递归树见上述图片实例。
有 \(a = 2, b = 2, \log_b a = \log_2 2 = 1\)。当取 \(k = 0\) 时,有 \(f(n) = \Theta(n^{\log_b a}\log^k n) = \Theta(n)\),则由主定理(3)有:
4
CDQ 分治的复杂度。
有 \(a = 2, b = 2, \log_b a = \log_2 2 = 1\)。当取 \(k = 1\) 时,有 \(f(n) = \Theta(n^{\log_b a}\log^k n) = \Theta(n\log n)\),则由主定理(3)有:
写在最后
奇怪的是网络上搜到的主定理的定义都是不完全的,比如以下参考中指出的那篇,在他们的定义下会出现明明可以适用主定理但是不存在于定义中的情况。为什么会有这种残疾版的定义?莫名奇妙!
要不是我现在妈的忙着傻逼期末我一定去溯源妈的傻逼期末
反转了,真正的主定理其实是:
参考: