主定理

鉴于先前排版的简陋和内容的缺失,现重新整理一番,在重温初赛知识的同时也方便大家学习。

——更新于2022/9/15

下面进入正文。


不论是在 CSP 初赛题还是在分治算法分析中,经常需要计算递推算法的时间复杂度。 有时画递归树求和会比较麻烦,这时

可以用主定理来秒杀。


定理内容——简化版

我们要处理一个规模为 \(n\) 的问题,通过分治得到 \(a\) 个规模为 \(\frac{n}{b}\) 的问题「假设每个子问题的规模基本一样」,\(f(n)\)

为递推以外的计算工作「分解和合并子问题的时间」,则其时间复杂度的递推式为:

\[T(n)=aT(\frac{n}{b})+f(n) \]

如果 \(a,b\) 为常数且 \(a≥1\)\(b>1\)\(b=1\)时递推无意义」,\(f(n)\) 为关于 \(n\) 的函数且在渐进意义上 \(f(n)>0\),且 \(T(1)=\Theta(1)\),那么:

  1. \(f(n)<\Theta(n^{\log_ba})\),则\(T(n)=\Theta(n^{\log_ba})\)

  2. \(f(n)>\Theta(n^{\log_ba})\),则\(T(n)=f(n)\)

  3. \(f(n)=\Theta(n^{\log_ba})\),则\(T(n)=\Theta(n^{\log_ba}\log n)\)

大者为王,等大乘\(\log\)

以上仅仅是本人为简化记忆而整理而成的,不甚严谨但很实用,若想知道严谨的表述,参见 百度百科 或《算法导论》。

「后面会附上《算法导论》中的严谨定理内容」


简单证明

使用主定理时无需考虑递归树,但证明还是要用到的。

简要分析一下:

递归层数 问题个数 问题规模 该层时间开销
\(0\) \(1\) \(n\) \(f(n)\)
\(1\) \(a\) \(\frac nb\) \(af(\frac nb)\)
\(2\) \(a^2\) \(\frac{n}{b^2}\) \(a^2f(\frac n{b^2})\)
\(...\)
\(k\) \(a^k\) \(\frac{n}{b^k}\) \(a^kf(\frac n{b^k})\)

\(k\) 为递归的最后一层,则:

\[f(\frac n{b^k})=\Theta(1)=f(1) \\ \frac n{b^k}=1 \\ n=b^k \\ k=\log_bn \]

于是:

\[\begin{aligned} T(n)&=f(n)+af(\frac nb)+a^2f(\frac n{b^2})+...+a^kf(\frac n{b^k}) \\ &=f(n)+af(\frac nb)+a^2f(\frac n{b^2})+...+a^{\log_bn}f(\frac n{b^{\log_bn}}) \\ &=f(n)+af(\frac nb)+a^2f(\frac n{b^2})+...+a^{\log_bn}f(\frac n{n}) \\ &=f(n)+af(\frac nb)+a^2f(\frac n{b^2})+...+a^{\log_bn}f(1) \end{aligned} \]

观察 \(a\) 的指数容易发现,一共有 \(\log_bn-0+1\) 项,\(n\) 很大时近似于有 \(\log_bn\)

考虑其中最大的一项「不考虑其他项是因为在时间复杂度分析中忽略较小项」,进行分类讨论:

  1. \(f(n)\) 最大,\(T(n)=f(n)\)

    此时 \(f(n)>a^{\log_bn}f(1)\),即 \(f(n)>\Theta(n^{log_ba})\) ,是定理中的第2种情况

  2. \(a^{\log_bn}f(1)\) 最大,\(T(n)=\Theta(a^{\log_bn})\)

    诶,似乎与定理中第1种情况的 \(T(n)=\Theta(n^{log_ba})\) 不太一样?

    其实是一样的:

    首先,设 \(n^x=a\) ①,则 \(x=\log_na\) ,将 \(x\) 代入①,则有:\(n^{\log_na}=a\)

    同时,对①的两边同时对 \(b\) 取对数,则有:\(\log_b{n^x}=x\log_bn=\log_ba\)

    因此 \(x=\log_na=\frac{\log_ba}{\log_bn}\)

    因此 \(\log_na\cdot\log_bn=\frac{\log_ba}{\log_bn}\cdot\log_bn=\log_ba\)

    于是,由②、③可得:

    \[a^{\log_bn}=(n^{\log_na})^{\log_bn}=n^{\log_na\cdot\log_bn}=n^{\log_ba} \]

    于是,

    \[T(n)=\Theta(a^{\log_bn})=\Theta(n^{log_ba}) \]

    嘿嘿,这不就一样了吗?

  3. 没有最大!

    每一项一样大,\(f(n)=a^{\log_bn}f(1)=\Theta(a^{\log_bn})=\Theta(n^{log_ba})\)

    此时就不能忽略最小的项了

    于是每一项都要加起来「前面讲过,一共有近似于 \(\log_bn\) 项」

    于是

    \[T(n)=\Theta(n^{log_ba})\cdot\log_bn=\Theta(n^{log_ba}\log_bn) \]

    时间复杂度中,\(n\) 的对数忽略底数,故

    \[T(n)=\Theta(n^{log_ba}\log n) \]

    这便是定理中的第3种情况。


例题

是不是感觉很简单?

下面来练习一下吧!


  • 例题 \(1\)

    假设某算法的计算时间表示为递推关系式

    \[T(n)=3T(\frac{n}{2})+\Theta(n),T(1)=\Theta(1) \]

    则算法的时间复杂度为( )

    A. \(\Theta(n)\)

    B. \(\Theta(n^{\log_23})\)

    C. \(\Theta(n\log n)\)

    D. \(\Theta(n^{\log_23}\log n)\)


    分析:

    显然,在这里 \(a=3,b=2,f(n)=\Theta(n)\)

    于是 \(\Theta(n^{\log_ba})=\Theta(n^{\log_23})>\Theta(n^{\log_22})=\Theta(n)=f(n)\)

    \(f(n)<\Theta(n^{\log_ba})\),为定理的第一种情况。

    \(T(n)=\Theta(n^{\log_ba})=\Theta(n^{\log_23})\),选B。


选择题肯定难不倒你,下面给你递推关系式,你自己求时间复杂度吧~

「其实是懒得写了哈哈」


  • 例题 \(2\)

    「二分查找」

    \(T(n)=T(\frac n2)+\Theta(1)\)


    分析:

    \(a=1,b=2,f(n)=\Theta(1)\)

    \(\Theta(n^{\log_ba})=\Theta(n^{\log_21})=\Theta(n^0)=\Theta(1)=f(n)\)

    等大,属于情况三。

    \(T(n)=\Theta(n^{\log_21}\log_2n)=\Theta(\log n)\)


  • 例题 \(3\)

    「归并排序」

    \(T(n)=2T(\frac n2)+\Theta(n)\)


    分析:

    \(a=2,b=2,f(n)=\Theta(n)\)

    \(\Theta(n^{\log_ba})=\Theta(n^{\log_22})=\Theta(n)=f(n)\)

    等大,属于情况三。

    \(T(n)=\Theta(n^{\log_22}\log_2n)=\Theta(n\log n)\)


  • 例题 \(4\)

    「地毯填补问题【洛谷P1228】

    \(T(n)=4T(\frac n4)+\Theta(1)\)


    分析:

    \(a=4,b=4,f(n)=\Theta(1)\)

    \(\Theta(n^{\log_ba})=\Theta(n^{\log_44})=\Theta(n)>f(n)\)

    大者为王,故 \(T(n)=\Theta(n)\)


  • 例题 \(5\)

    「Strassen 算法【百度百科】

    \(T(n)=7T(\frac n2)+\Theta(n^2)\)


    分析:

    \(a=7,b=2,f(n)=\Theta(n^2)\)

    \(\Theta(n^{\log_ba})=\Theta(n^{\log_27})>\Theta(n^{\log_24})=\Theta(n^2)=f(n)\)

    大者为王,故 \(T(n)=\Theta(n^{\log_27})\)

    注:

    众所周知,普通的矩阵乘法时间复杂度为 \(\Theta(n^3)\)

    可以通过填 \(0\) 的方法使参与运算的矩阵成为 \(2^n\times2^n\) 矩阵,再将其分为 \(4\)\(2^{n-1}\times2^{n-1}\) 矩阵

    最后可以将原先的矩阵乘法转化为 \(8\) 个规模为原先一半的矩阵乘法,以及 \(4\) 个矩阵加法

    因此有递归式:

    \[T(n)= \begin{cases} 8T(\frac n2)+\Theta(n^2) &n>1 \\ \Theta(1) &n=1 \end{cases} \]

    用主定理计算一下:

    \(a=8,b=2,f(n)=\Theta(n^2)\)

    \(\Theta(n^{\log_ba})=\Theta(n^{\log_28})=\Theta(n^3)>f(n)\)

    \(T(n)=\Theta(n^3)\)

    注意到瓶颈在于 \(a\),即矩阵乘法的运算次数。

    而 Strassen 算法则通过构造 \(7\) 个矩阵,通过它们的不同组合进行加减运算,从而减少递归次数进而降低时间复杂度。上面推过,其时间复杂度为 \(T(n)=\Theta(n^{\log_27})\)


  • 例题 \(6\)

    「摘自《算法导论》」

    \(T(n)=3T(\frac n4)+\Theta(n\log n)\)


    分析:

    \(a=3,b=4,f(n)=\Theta(n\log n)\)

    \(\Theta(n^{\log_ba})=\Theta(n^{\log_43})<\Theta(n^{\log_44})=\Theta(n)<\Theta(n\log n)=f(n)\)

    大者为王,故 \(T(n)=\Theta(n\log n)\)

    实际上,这样结果虽然正确,但过程并不严谨,参见下面严谨版的定理内容。


定理内容——严谨版「《算法导论》」

定理 \(4.1\)(主定理)令 \(a\ge1\)\(b>1\) 是常数,\(f(n)\) 是一个函数,\(T(n)\) 是定义在非负整数上的递归式:

\[T(n)=aT(\frac nb)+f(n) \]

其中我们将 \(\frac nb\) 解释为 \(\lfloor\frac nb\rfloor\)\(\lceil\frac nb\rceil\)。那么 \(T(n)\) 有如下渐进界:

  1. 若对某个常数 \(\epsilon>0\)\(f(n)=O(n^{\log_ba-\epsilon})\),则 \(T(n)=\Theta(n^{\log_ba})\)
  2. \(f(n)=\Theta(n^{\log_ba})\),则 \(T(n)=\Theta(n^{\log_ba}\lg n)\)
  3. 若对某个常数 \(\epsilon>0\)\(f(n)=\Omega(n^{\log_ba+\epsilon})\),且对某个常数 \(c<1\) 和所有足够大的 \(n\)\(af(\frac nb)\le cf(n)\),则 \(T(n)=\Theta(f(n))\)

在第一种情况中,不是 \(f(n)<n^{\log_ba}\) 就够了,而是要多项式意义上的小于。也就是说,\(f(n)\) 必须渐进小于 \(n^{\log_ba}\),要相差一个因子 \(n^\epsilon\),其中 \(\epsilon\) 是大于 \(0\) 的常数。

在第三种情况中,不是 \(f(n)>n^{\log_ba}\) 就够了,而是要多项式意义上的大于,而且还要满足“正则”条件 \(af(\frac nb)\le cf(n)\)我们将会遇到的多项式界的函数中,多数都满足此条件。

注意,这三种情况并未覆盖 \(f(n)\) 的所有可能性。情况 \(1\) 和情况 \(2\) 之间有一定间隙,\(f(n)\) 可能小于 \(n^{\log_ba}\) 但不是多项式意义上的小于。类似地,情况 \(2\) 和情况 \(3\) 之间也有一定间隙,\(f(n)\) 可能大于 \(n^{\log_ba}\) 但不是多项式意义上的大于。如果函数 \(f(n)\) 落在这两个间隙中,或者情况 \(3\) 中要求的正则条件不成立,就不能用主方法来求解递归式。


参考资料

posted @ 2021-09-04 18:12  凌云_void  阅读(613)  评论(0编辑  收藏  举报