递归算法是我们经常遇到的算法,如何分析其时间复杂度是个问题。本文介绍4种求解方法。


一、递归函数复杂度求解

1. 迭代方法

迭代地展开递归式,将其转化为和式,然后求解。

举个例子:
T ( n ) = 2 T ( n 2 ) + c n T(n)=2T(\frac{n}{2} )+cn T(n)=2T(2n)+cn

首先将其迭代展开:
T ( n ) = 2 T ( n 2 ) + c n = 2 2 T ( n 2 2 ) + c n + c n = 2 3 T ( n 2 3 ) + c n + c n + c n = … = 2 k T ( n 2 k ) + k c n \begin{aligned} T(n)&=2T(\frac{n}{2} )+cn\\ &=2^2T(\frac{n}{2^2} )+cn+cn\\ &=2^3T(\frac{n}{2^3} )+cn+cn+cn\\ &=\dots \\ &=2^kT(\frac{n}{2^k} )+kcn\\ \end{aligned} T(n)=2T(2n)+cn=22T(22n)+cn+cn=23T(23n)+cn+cn+cn==2kT(2kn)+kcn

接下来是关键一步,我们假设当展开到第 k k k次时,递归结束,即 n 2 k = 1 \frac{n}{2^k} =1 2kn=1,接下来要将未知数 k k k全部替换成 n n n,则有:
n 2 k = 1 ⇒ n = 2 k T ( n ) = 2 k T ( n 2 k ) + k c n = 2 k T ( 1 ) + k c n = n T ( 1 ) + c n log ⁡ n = Θ ( n log ⁡ n ) \frac{n}{2^k} =1 \Rightarrow n = 2^k\\ \begin{aligned} T(n)&=2^kT(\frac{n}{2^k} )+kcn\\ &=2^kT(1)+kcn \\ &=nT(1)+cn \log n\\ &=\Theta(n\log n)\\ \end{aligned} 2kn=1n=2kT(n)=2kT(2kn)+kcn=2kT(1)+kcn=nT(1)+cnlogn=Θ(nlogn)
至此求解完成, T ( n ) T(n) T(n)的时间复杂度为 Θ ( n log ⁡ n ) \Theta(n\log n) Θ(nlogn),如果要求不高的话,可以写低阶函数 O O O

需要说明的一点是,我们通常将 log ⁡ 2 n \log _{2}{n} log2n写作 log ⁡ n \log n logn


总结一下:

迭代法求解递归函数复杂度:

  1. 迭代展开,将递归函数展成具有规律的和式。
  2. 假设第 k k k次展开递归结束,达到递归终点 T ( 1 ) T(1) T(1)
  3. 利用 k k k n n n的关系,将和式里的 k k k全部替换为 n n n的表达式。
  4. 求解时间复杂度。当式子复杂不易求解时,可以采用放缩的方式来求上界\下界。

2. 变量代换法

可能有某些递归方程,通过变量代换后可以变成我们熟悉的样子,此时可以用这种方法。

举个例子:
T ( n ) = 2 T ( n 2 + 17 ) + n T(n)=2T(\frac{n}{2} +17)+n T(n)=2T(2n+17)+n
我们发现,这个式子好像和我们刚才求结果的式子的形式差不多,我们做一下处理,令 n = m + 34 n=m+34 n=m+34
n = m + 34 T ( n ) = 2 T ( n 2 + 17 ) + n ⇒ T ( m + 34 ) = 2 T ( m 2 + 34 ) + m + 34 n=m+34\\ \begin{aligned} T(n)&=2T(\frac{n}{2} +17)+n\\ \Rightarrow T(m+34)&=2T(\frac{m}{2} +34)+m+34\\ \end{aligned} n=m+34T(n)T(m+34)=2T(2n+17)+n=2T(2m+34)+m+34
T ( m + 34 ) = S ( m ) T(m+34) = S(m) T(m+34)=S(m),则有:
S ( m ) = 2 S ( m 2 ) + m + 34 S(m)=2S(\frac{m}{2})+m+34 S(m)=2S(2m)+m+34
此时我们便可以依照上个例子,直接给出结论: S ( m ) = Θ ( m log ⁡ m ) S(m)=\Theta (m\log m) S(m)=Θ(mlogm),由 n = m + 34 n=m+34 n=m+34可知, T ( n ) = Θ ( n log ⁡ n ) T(n)=\Theta (n\log n) T(n)=Θ(nlogn)


总结一下:

变量代换求解递归函数复杂度:

  1. 观察递归函数形式特点,发现与已经做过的类似。
  2. 找到合适的变量进行代换。
  3. 求解代换后的时间复杂度。
  4. 转化为原式的时间复杂度

此方法的缺点很明显:当看不出来合适的变量代换时便不能使用;但是对于有些题目而言,不使用此方法的话,很难求解,举一个例子:
求 解 : T ( n ) = 2 T ( n 1 2 ) + log ⁡ n 令 n = 2 m , 则 有 : T ( 2 m ) = 2 T ( 2 m 2 ) + m 令 T ( 2 m ) = S ( m ) , 则 : S ( m ) = 2 S ( m 2 ) + m ⇒ S ( m ) = Θ ( m log ⁡ m ) ⇒ T ( n ) = Θ ( log ⁡ n log ⁡ log ⁡ m ) 求解:T(n)=2T(n^{\frac{1}{2}})+\log n \\ 令n=2^m,则有:\\ T(2^m)=2T(2^{\frac{m}{2}})+m \\ 令T(2^m)=S(m),则:\\ S(m)=2S(\frac{m}{2})+m\\ \Rightarrow S(m)=\Theta(m\log m)\\ \Rightarrow T(n)=\Theta(\log n \log\log m) T(n)=2T(n21)+lognn=2mT(2m)=2T(22m)+mT(2m)=S(m)S(m)=2S(2m)+mS(m)=Θ(mlogm)T(n)=Θ(lognloglogm)


3. Master定理

该定理用于求解 T ( n ) = a T ( n b ) + f ( n ) T(n)=aT(\frac{n}{b})+f(n) T(n)=aT(bn)+f(n)型函数,其中 a ≥ 1 , b > 0 a\ge 1,b>0 a1,b>0 f ( n ) f(n) f(n)为正函数。

Master定理

当满足以上条件时,有以下结论:

  1. f ( n ) = O ( n log ⁡ b a − ε ) , ε > 0 f(n)=O(n^{\log_ba-\varepsilon}),\varepsilon>0 f(n)=O(nlogbaε),ε>0,则 T ( n ) = Θ ( n l o g b a ) T(n)=\Theta(n^{log_ba}) T(n)=Θ(nlogba)
  2. f ( n ) = Θ ( n log ⁡ b a ) f(n)=\Theta(n^{\log_ba}) f(n)=Θ(nlogba),则 T ( n ) = Θ ( n l o g b a log ⁡ n ) T(n)=\Theta(n^{log_ba}\log n) T(n)=Θ(nlogbalogn)
  3. f ( n ) = Ω ( n log ⁡ b a + ε ) , ε > 0 f(n)=\Omega(n^{\log_ba+\varepsilon}),\varepsilon>0 f(n)=Ω(nlogba+ε),ε>0,且对于所有充分大的 n n n a f ( n b ) ≤ C f ( n ) , C < 1 af(\frac{n}{b})\leq Cf(n),C<1 af(bn)Cf(n),C<1,则 T ( n ) = Θ ( f ( n ) ) T(n)=\Theta(f(n)) T(n)=Θ(f(n))

想必大家看到这一大堆必然头疼,我们直观地来看,上述定理描述了以下含义:

比较 f ( n ) f(n) f(n) n log ⁡ b a n^{\log_b a} nlogba阶数的大小:

  1. f ( n ) f(n) f(n)小于 n log ⁡ b a n^{\log_b a} nlogba,且是多项式地小于,即对于某个常数 ε > 0 , f ( n ) = O ( n log ⁡ b a − ε ) \varepsilon >0,f(n)=O(n^{\log_ba-\varepsilon}) ε>0,f(n)=O(nlogbaε),则有 T ( n ) = Θ ( n log ⁡ b a ) T(n)=\Theta (n^{\log_b a}) T(n)=Θ(nlogba)
  2. f ( n ) f(n) f(n)大于 n log ⁡ b a n^{\log_b a} nlogba,且是多项式地大于,即对于某个常数 ε > 0 , f ( n ) = Ω ( n log ⁡ b a + ε ) \varepsilon >0,f(n)=\Omega(n^{\log_ba+\varepsilon}) ε>0,f(n)=Ω(nlogba+ε),则有 T ( n ) = Θ ( f ( n ) ) T(n)=\Theta (f(n)) T(n)=Θ(f(n))
  3. f ( n ) f(n) f(n) n log ⁡ b a n^{\log_b a} nlogba同阶,则 T ( n ) = Θ ( n log ⁡ b a lg ⁡ n ) = Θ ( f ( n ) log ⁡ n ) T(n)=\Theta (n^{\log_b a}\lg n)=\Theta (f(n)\log n) T(n)=Θ(nlogbalgn)=Θ(f(n)logn)

这样一来,我们便可以简单记为这俩谁大就和谁同阶(同时指出是多项式地大\小与),它们同阶时乘上个 log ⁡ n \log n logn即可。

举个例子:
求 解 : T ( n ) = 9 T ( n 3 ) + n a = 9 , b = 3 , f ( n ) = n , n l o g b a = n 2 故 f ( n ) = n = O ( n 2 − 1 ) , ε = 1 则 有 T ( n ) = Θ ( n 2 ) 求解:T(n)=9T(\frac{n}{3})+n \\ a=9,b=3,f(n)=n,n^{log_ba}=n^2\\ 故f(n)=n=O(n^{2-1}),\varepsilon=1\\ 则有T(n)=\Theta(n^2) T(n)=9T(3n)+na=9,b=3,f(n)=n,nlogba=n2f(n)=n=O(n21),ε=1T(n)=Θ(n2)

总结一下:

Master定理求解递归函数复杂度

  1. 判断函数类型是否符合Master定理条件
  2. 写出 a , b , f ( n ) , n log ⁡ b a a,b,f(n),n^{\log_ba} a,b,f(n),nlogba,比较 f ( n ) , n log ⁡ b a f(n),n^{\log_ba} f(n),nlogba阶数的大小
  3. 若不同阶,需同时指出 ε \varepsilon ε(这一点很重要,因为并不是满足条件的所有式子都使用,若不能指出 ε \varepsilon ε,即并不是多项式地大小与,则定理不适用);若同阶,直接写答案

可以看到,使用Master定理极大地简化了运算。但同时,Master定理的局限性较大,一方面是对于递归函数形式的限制,另一方面必须是多项式地大于小于,非多项式大小与的也不适用。

目前不断有学者对Master定理进行拓展,以扩大它的适用范围,但因其较为复杂,本文给出的形式已经满足绝大部分需求,感兴趣的读者可以自行查阅相关文献了解。


4. 先猜后证

顾名思义,首先猜测答案应该是多少,然后适用数学归纳法进行证明。

举个例子:
求 解 : T ( n ) = 2 T ( n 2 + 17 ) + n 求解:T(n)=2T(\frac{n}{2}+17)+n T(n)=2T(2n+17)+n
这个题我们在前面已经求解过,但还请读者暂时忘记答案。

由于 n 2 \frac{n}{2} 2n n 2 + 17 \frac{n}{2}+17 2n+17阶数相同,在 n n n充分大时近似相等,所以我们有理由猜测,当 n n n充分大时, T ( n 2 ) ≈ T ( n 2 + 17 ) T(\frac{n}{2})\approx T(\frac{n}{2}+17) T(2n)T(2n+17),故 T ( n ) = 2 T ( n 2 ) + n T(n)=2T(\frac{n}{2})+n T(n)=2T(2n)+n,易知 T ( n ) = Θ ( n log ⁡ n ) T(n)=\Theta(n\log n) T(n)=Θ(nlogn),接下来再用数学归纳法进行证明。

这种方法我个人不是很推荐使用,缺点很明显:如果猜的稍微有偏差,则可能造成解不出来;可能猜对了,但数学归纳法证明不出来。总的来说就是适用于数学能力较强的人,我个人还是更建议前三种方法。

但是,我们不能抛弃这种方法,因为在某些极端情况下,猜测答案反而是最优选择,可以通过先猜大范围,然后不断压缩上界,提高下界的方法来达到”逼近“正确答案的目的。


二、总结

本文介绍了4种递归函数的求解方法,在解题的过程中,我们应该考虑以下顺序:Master定理 > 变量代换 > 迭代求解 > 先猜后证

同时,对于常见的递归形式,应该记住其复杂度,如 T ( n ) = 2 T ( n 2 ) + c n ⇒ Θ ( n log ⁡ n ) T(n)=2T(\frac{n}{2} )+cn \Rightarrow \Theta (n\log n) T(n)=2T(2n)+cnΘ(nlogn)等。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐