『数学杂谈』递归式复杂度求解/渐进式小记

  关于递归式复杂度求解的一些想法。

  虽然说具体数学有一整章讲渐进式,但鉴于学这个性价比太低了,基本也用不到,所以很多有关渐进式的实用知识会在本文总结。

  接下来进入正题。

渐进式

  很多时候,我们并不能很好的得知一个式子的封闭形式,但是对于这个式子知道他的增长速度也是极好的。这里用来表示这种渐进的符号最常用的符号有两种: \(O\)\(\Theta\) 。在算法竞赛,一般直接使用 \(O\) 即可,但是在实际数学使用中两种符号其实是有区别的: \(O\) 表示的是 渐进上界(asymptotically upper bound) ,表示式子的量级不会超过这个数;而 \(\Theta\) 表示的是 渐近紧确界(asymptotically tight bound) ,也就是说记号 \(O\) 并不含式子渐进下界的信息,不过在关心时间复杂度上界的算法竞赛中下界并不重要,因此直接用 \(O\) 是没有问题的。当然还有记号 \(\Omega\) 表示 渐进下界(asymptotically lower bound) ,只不过它不太常用,我们就不对其多加讨论。

  计算渐进式的方法有很多,下面会一一介绍。

主定理及其局限性

  这是本文十分重要的一个点。这里先介绍一下主定理,这是一个非常好的求解递归式渐进的方法(证明略):

Theorem:

\[\begin{align*} & 设\ a\geq1,b\geq1\ ,\ f(n)\ 为一定义在非负整数上的函数,\\ & T(n)=aT(\frac nb)+f(n)\ (当\ \frac nb\ 不为整数时代表\ \lfloor\frac nb\rfloor\ 或\ \lceil\frac nb\rceil\ ),则有:\\ \\ & 1.若存在\ \varepsilon>0\ ,\ 使得\ f(n)=O\left(n^{\log_ba-\varepsilon}\right)\ ,则\ T(n)=\Theta\left(n^{\log_ba}\right) \\ & 2.若存在\ k\geq0\ ,\ 使得\ f(n)=O\left(n^{\log_ba}\lg^kn\right)\ ,则\ T(n)=\Theta\left(n^{\log_ba}\lg^{k+1}n\right) \\ & 3.若存在\ \varepsilon>0\ ,\ 使得\ f(n)=O\left(n^{\log_ba+\varepsilon}\right)\ ,且存在\ 0<c<1\ 及正整数\ N\ ,\\&使得当\ n>N\ 时,有\ af\left(\frac nb\right)\leq cf(n)\ ,则\ T(n)=\Theta(f(n)) \end{align*} \]

  主定理的适用范围是非常广的,但是它对下面这个式子束手无策:

\[\begin{flalign} \label{1} T(n) = 4\sqrt nT(\sqrt n)+n \end{flalign} \]

  发现主定理用不了,因为子问题里的 \(\sqrt n = \frac{n}{\sqrt n}\) 得到 \(b=\sqrt n\)​ ,这并不是一个常数,此时主定理就用不了了。

  除了上式外,看到下面这个递归式:

\[\begin{flalign} \label{2} T(n) = T(\frac n2)+T(\frac n4)+T(\frac n8)+n \end{flalign} \]

这个式子的子问题并不是平均划分的,因此也就不能用主定理。

  我们再看一个式子:

\[\begin{flalign} \label{3} T(n)=4T(\frac n4)+\frac n{\lg n} \end{flalign} \]

此时该式子不符合主定理的三种情况,也是不能用主定理的。

不那么暴力的暴力

  对于不能用主定理解决的递归式,《算法导论》中推荐使用递归树+数学归纳法解决,但是这样工作量过于巨大了,会显得很蠢,还不如乱猜再证明来的快,此时就十分的难受,不知道怎么整。
  为了让我们能更简洁与优美的暴力,采用更好的方法是必要的。我们尝试在递归树的基础上进行优化。就像记忆化搜索可以优化深度优先搜索一样,我们可以对每一个可能出现的子问题考虑它对答案的贡献。
  拿 \((1)\) 式举例,我们无需列出一整棵递归树,只需对每个子问题统计贡献即可。用 \(w(x)\) 表示子问题 \(T(x)\) 的贡献,就可以有下面的表:

\(x\) \(w(x)\)
\(n\) \(n\)
\(\sqrt n\) \(4\sqrt n \times \sqrt n = 4n\)
\(\sqrt {\sqrt n}\) \((4\sqrt {\sqrt n})(4\sqrt n )(\sqrt {\sqrt n}) = 16n\)

  相信对数字敏感的人无需列表即可感性地证明规律,即使不太敏感,表的前三项也足够找到规律:

\[w(n^{0.5^x}) = 4^xn \]

那么设 \(n=2^k\) 那么层数就应该是:

\[\log_2 k = \log_2 \log_2n \]

总的复杂度就会是:

\[\sum\limits_{i=0}^{\log_2 \log_2n}4^in \]

\[\sum\limits_{i=0}^{\log_2 \log_2n}4^i \sim 4^{\log_2 \log_2n} \]

所以答案为:

\[\begin{align*} 4^{\log_2 \log_2n}n &= 2^{2\log_2 \log_2n}n\\ &= (2^{\log_2 \log_2n})^2n\\ &= \mathcal O(n(\log^2n)) \end{align*} \]

  如果具有较好的和式计算能力的话,这种做法也是可以得出和主定理一样的结果的。下面是 Codeforces Round 960 F题 的一种错误做法最劣时间复杂度分析,它是一个基于递归的分治算法。递归树这一步就省略了,得出的式子是这样的:

\[\begin{align*} T(n) &= \underbrace{\log_2n+2\log_2\left(\frac n2\right)+4\log_2\left(\frac n4\right)+\cdots}_{\log_2n项} \\ &= \log_2n+\sum_{k=1}^{\log_2n}2^k\log_2\left(\frac n{2^k}\right) \end{align*} \]

我们把后面的和式拎出来处理:

\[\begin{align*} \sum_{k=1}^{\log_2n}2^k\log_2\left(\frac n{2^k}\right) &= \sum_{k=1}^{\log_2n}2^k(\log_2n-k)\\ &= \log_2n\sum_{k=1}^{\log_2n}2^k-\sum_{k=1}^{\log_2n}k2^k \end{align*} \]

具体数学一书中有对和式后面一个和式的化简,这里不赘述化简过程: \(\sum_{k=1}^{m}k2^k = (m-1)2^{m+1}+2\) ,所以:

\[\begin{align*} \log_2n\sum_{k=1}^{\log_2n}2^k-\sum_{k=1}^{\log_2n}k2^k &= \log_2n(2^{\log_2n+1}-2)-(\log_2n-1)2^{\log_2n+1}+2 \\ &= \log_2n(2n-2)-(\log_2n-1)2n+2 \\ &= 2n\log_2n-2\log_2n-2n\log_2n+2n+2 \\ &= 2n-2\log_2n+2 \end{align*} \]

带回到原式就有 \(2n-\log_2n+2\) ,即单次询问时间复杂度是 \(\mathcal O(n)\) 的,并不能通过。

  不妨再来一组。我们来解决式 \((2)\) ,而这甚至表都可以不列出来。不难发现层数最为 \(\log_2n\) ,而层贡献为 \(层值 \times 层出现次数\) ,假设当前层为 \(n\frac 1{2^k}\) ,相当于一次能走一步、两步或三步,走 \(k\) 步的方案数, \(OGF\) 搞起,但是没搞出来(?),但是可以从组合的角度近似地看作把 \(k\) 个数分成三段,那么这个系数就应该是 \(\mathcal O(k^2)\) 的,那么答案就是:

\[\sum\limits_{i=0}^{\log_2n}i^2\frac 1{2^i}n = \mathcal O(n) \]

  最后我们来搞定式 \((3)\) ,依然是把层数算出来,是 \(\log_4n\)​​ ,那么答案就是:

\[\sum\limits_{i=0}^{log_4n}4^i\frac {\frac n{4^i}}{\lg \frac n{4^i}} = \sum\limits_{i=0}^{log_4n} \frac {n}{\lg \frac n{4^i}} \]

然后发现式子并不能化简到封闭形式,此时我们就需要下面的定理。

Akra-Bazzi定理

Theorem:

\[设\ g(x)\ 为一非负函数,\ T(x)= \left\{\begin{array}{ll} \Theta(1) &,1\leq x\leq X_0 \\ \sum\limits_{i=1}^ka_iT(b_ix)+g(x) &,n>X_0 \end{array}\right.\\ (其中\ k\geq 1,a_i>0,0<b_i<1),\ X_0\ 满足对任意\ 1\leq i\leq k\ 有\ X_0>\frac 1{b_i}\ 且\ X_0>\frac 1{1-b_i})\\ 若\ g(x)\ 满足多项式增长条件,\ p\ 为方程\ \sum_{i=1}^ka_ib_i^p=1\ 的实数解,则\ T(x)=\Theta\left(x^p\left(1+\int_1^x\frac{g(u)}{u^{p+1}}du\right)\right) \]

  其中的多项式增长条件可以简易地理解为 \(\exists c>0,|g'(x)|\in \mathcal O(x^c)\)
  开了之后我们再回到式 \((3)\)\(p\) 显然为 \(1\)​ ,那么代入定理得到:

\[\begin{array}{ll} T(n) &= \Theta\left(n\left(1+\int^n_1\frac {\frac x{\lg x}}{x^2}dx\right)\right)\\ &= \Theta\left(n\left(1+\int^n_1\frac {1}{x\lg x}dx\right)\right)\\ &= \Theta(n+(1+\lg\lg n))\\ &= \Theta(n\lg\lg n) \end{array} \]

  Akra-Bazzi 定理原论文里的证明用了一种称为 阶变换(Order Transform) 的技巧,还涉及泛函分析,这些笔者还在学习中。当然粗暴一点的证明有数学归纳法,这里受限于本人能力不足,不做证明。

总结

  总结一下,对于一般的递归式,可通过主定理求渐进解。而对于 \(b\) 不是常数的情形,可以分层分析贡献。最后对于 \(g(n)\) 满足多项式增长的式子,可以尝试使用 Akra-Bazzi 定理。

posted @ 2023-09-15 22:16  Black_Crow  阅读(25)  评论(0编辑  收藏  举报