前置知识——渐进复杂度记号
本文假设你已经掌握了渐进复杂度的含义,并且熟悉大O记号表示复杂度。除了大O记号外,这里再简单介绍一下其他的复杂度记号。如果你掌握了这些符号,可以跳过本段。
渐进复杂度的记号有Θ \Theta Θ 、O O O 、Ω \Omega Ω 、o o o 、ω \omega ω ,主定理用到了前三个,但为了完整性这里全部介绍。
Θ \Theta Θ
若∃ c 1 , c 2 , n 0 > 0 \exists c_1,c_2,n_0 \gt 0 ∃ c 1 , c 2 , n 0 > 0 ,使得∀ n ≥ n 0 \forall n \geq n_0 ∀ n ≥ n 0 有c 1 ⋅ g ( n ) ≤ f ( n ) ≤ c 2 ⋅ g ( n ) c_1 \cdot g(n) \leq f(n) \leq c_2 \cdot g(n) c 1 ⋅ g ( n ) ≤ f ( n ) ≤ c 2 ⋅ g ( n ) ,则称f ( n ) = Θ ( g ( n ) ) f(n) = \Theta(g(n)) f ( n ) = Θ ( g ( n ))
Θ \Theta Θ 可以精确的表示算法的渐进复杂度,相应地,使用时也要注意严谨性。
用表示大小的符号类比的话,Θ \Theta Θ 就相当于= = = 等于号。
O O O
若∃ c , n 0 > 0 \exists c,n_0 \gt 0 ∃ c , n 0 > 0 ,使得∀ n ≥ n 0 \forall n \geq n_0 ∀ n ≥ n 0 有f ( n ) ≤ c ⋅ g ( n ) f(n) \leq c \cdot g(n) f ( n ) ≤ c ⋅ g ( n ) ,则称f ( n ) = Θ ( g ( n ) ) f(n) = \Theta(g(n)) f ( n ) = Θ ( g ( n ))
虽然大O记号不如Θ \Theta Θ 记号来的精确,但它是讨论复杂度的时候最常用的符号。主要是因为比较好用键盘输入,并且它指出了我们关心的运行时间上界(尤其是当一些算法的下界不太好计算得出时)
用表示大小的符号类比的话,O O O 就相当于≤ \leq ≤ 小于等于号。
Ω \Omega Ω
若∃ c , n 0 > 0 \exists c,n_0 \gt 0 ∃ c , n 0 > 0 ,使得∀ n ≥ n 0 \forall n \geq n_0 ∀ n ≥ n 0 有f ( n ) ≥ c ⋅ g ( n ) f(n) \geq c \cdot g(n) f ( n ) ≥ c ⋅ g ( n ) ,则称f ( n ) = Ω ( g ( n ) ) f(n) = \Omega(g(n)) f ( n ) = Ω ( g ( n ))
用表示大小的符号类比的话,O O O 就相当于≥ \geq ≥ 大于等于号。
o o o
若 ∀ c > 0 , ∃ n 0 > 0 \forall c>0,\;\exists n_0>0 ∀ c > 0 , ∃ n 0 > 0 ,使得 ∀ n ≥ n 0 \forall n \geq n_0 ∀ n ≥ n 0 有 f ( n ) < c ⋅ g ( n ) f(n)<c\cdot g(n) f ( n ) < c ⋅ g ( n ) ,则称f ( n ) = o ( g ( n ) ) f(n) = o(g(n)) f ( n ) = o ( g ( n ))
用表示大小的符号类比的话,o o o 就相当于< < < 小于号。
ω \omega ω
若 ∀ c > 0 , ∃ n 0 > 0 \forall c>0,\;\exists n_0>0 ∀ c > 0 , ∃ n 0 > 0 ,使得 ∀ n ≥ n 0 \forall n \geq n_0 ∀ n ≥ n 0 有 f ( n ) > c ⋅ g ( n ) f(n)>c\cdot g(n) f ( n ) > c ⋅ g ( n ) ,则称f ( n ) = ω ( g ( n ) ) f(n) = \omega(g(n)) f ( n ) = ω ( g ( n ))
用表示大小的符号类比的话,ω \omega ω 就相当于> > > 大于号。
整理
为了便于记忆,可以整理成下面的表格。
复杂度记号
帮助记忆
Θ \Theta Θ
= = =
O O O
≤ \leq ≤
Ω \Omega Ω
≥ \geq ≥
o o o
< \lt <
ω \omega ω
> \gt >
介绍
主定理(Master Theorem)是用于分析分治 算法复杂度的常用方法,给定算法运行时间与输入规模的函数的递推关系式,就可以计算得到算法的渐进时间复杂度。
若T ( n ) = a T ( n b ) + f ( n ) T(n) = aT(\frac{n}{b}) + f(n) T ( n ) = a T ( b n ) + f ( n ) 其中a ≥ 1 , b > 1 a \geq 1, b \gt 1 a ≥ 1 , b > 1
其中:
n n n 是问题规模
a a a 是分治的子问题数量
n b \frac{n}{b} b n 是每个子问题的规模(b>1)
f ( n ) f(n) f ( n ) 是分治算法需要的额外时间(即拆分成子问题的用时,以及将子问题的答案合并成最终结果的用时)
有:
T ( x ) = { Θ ( n log b a ) , if ∃ ε > 0 , f ( n ) = O ( n log b ( a ) − ε ) Θ ( n log b a log ε + 1 n ) , if ∃ ε ≥ 0 , f ( n ) = Θ ( n log b a log ε n ) Θ ( f ( n ) ) , if ∃ ε > 0 , f ( n ) = Ω ( n log b ( a ) + ε ) meanwhile ∃ c < 1 , for sufficiently large n , f ( n b ) ≤ c f ( n ) T(x)=\begin{cases}
\Theta(n^{\log _b a}), & \text{if $\exists \varepsilon \gt 0, f(n)=O(n^{\log _b (a) - \varepsilon})$}\\
\Theta(n^{\log _b a}\log ^{\varepsilon + 1} n), & \text{if $\exists \varepsilon \geq 0, f(n)=\Theta(n^{\log _b a}\log ^\varepsilon n)$}\\
\Theta(f(n)), & \text{if $\exists \varepsilon \gt 0, f(n)=\Omega(n^{\log _b (a) + \varepsilon})$}\\
&\text{meanwhile $\exists c \lt 1$, for sufficiently large $n$, $f(\frac{n}{b}) \leq cf(n)$}
\end{cases}
T ( x ) = ⎩ ⎨ ⎧ Θ ( n l o g b a ) , Θ ( n l o g b a log ε + 1 n ) , Θ ( f ( n )) , if ∃ ε > 0 , f ( n ) = O ( n l o g b ( a ) − ε ) if ∃ ε ≥ 0 , f ( n ) = Θ ( n l o g b a log ε n ) if ∃ ε > 0 , f ( n ) = Ω ( n l o g b ( a ) + ε ) meanwhile ∃ c < 1, for sufficiently large n , f ( b n ) ≤ c f ( n )
为了方便理解和记忆,也可以不严谨地写成这样
T ( x ) = { Θ ( n log b a ) , if f ( n ) < n log b a Θ ( f ( n ) log n ) , if f ( n ) = n log b a log k n ( k ≥ 0 ) Θ ( f ( n ) ) , if f ( n ) > n log b a T(x) = \begin{cases}
\Theta(n^{\log _b a}), & \text{if $f(n) \lt n^{\log _b a}$}\\
\Theta(f(n) \log n), & \text{if $f(n) = n^{\log _b a} \log ^k n \quad (k \geq 0)$}\\
\Theta(f(n)), & \text{if $f(n) \gt n^{\log _b a}$}\\
\end{cases}
T ( x ) = ⎩ ⎨ ⎧ Θ ( n l o g b a ) , Θ ( f ( n ) log n ) , Θ ( f ( n )) , if f ( n ) < n l o g b a if f ( n ) = n l o g b a log k n ( k ≥ 0 ) if f ( n ) > n l o g b a
注意,这里的< \lt < 、= = = 、> \gt > 并不是字面上的意思,只是为了方便理解记忆。真正的含义请看原式子
也就是说,只要比较f ( n ) f(n) f ( n ) 和n log b a n^{\log _b a} n l o g b a 的大小(多项式比大小)。哪一个大,T ( n ) T(n) T ( n ) 就等于Θ ( 哪个 ) \Theta(\text{哪个}) Θ ( 哪个 ) ,如果一样大,或者相差log k n \log ^k n log k n ,那么T(n)就等于Θ ( f ( n ) log n ) \Theta(f(n)\log n) Θ ( f ( n ) log n ) ,是不是很简单。
如果只考虑f ( n ) = n d f(n)=n^d f ( n ) = n d 的情况,那么就更简单了
T ( x ) = { Θ ( n d log n ) , if d = log b a Θ ( n max { d , l o g b a } ) , otherwise T(x) = \begin{cases}
\Theta(n^d \log n), & \text{if $d = \log _b a$}\\
\Theta(n^{\max \{ d, log _b a \}}), & \text{otherwise}\\
\end{cases}
T ( x ) = { Θ ( n d log n ) , Θ ( n m a x { d , l o g b a } ) , if d = log b a otherwise
实例
这几个例子的复杂度其实不使用主定理也能很容易看出来,这里只是用做例子,方便理解主定理具体是怎么应用的。对于更复杂问题的分治,相对来说就没那么容易看出来了,这时候主定理也就能派上用场了。
二分查找
在二分查找的例子中,n n n 表示查找范围的大小,每一次执行二分查找时,查找范围减半,b = 2 b=2 b = 2 ,子问题数量仍为1,a = 1 a=1 a = 1 ,每一次二分用时是O ( 1 ) O(1) O ( 1 ) 的
n log b a = 1 n^{\log _b a} = 1 n l o g b a = 1 与f(n)同阶,则T ( n ) = Θ ( f ( n ) log n ) = Θ ( log n ) T(n)=\Theta(f(n)\log n)=\Theta(\log n) T ( n ) = Θ ( f ( n ) log n ) = Θ ( log n )
遍历二叉树
a = 2 b = 2 f ( n ) = O ( 1 ) n log b a = n \begin{align*}
a &= 2\\
b &= 2\\
f(n) &= O(1)\\
n^{\log _b a} &= n
\end{align*} a b f ( n ) n l o g b a = 2 = 2 = O ( 1 ) = n
n n n 比f ( n ) f(n) f ( n ) 大,因此T ( n ) = f ( n ) = Θ ( n ) T(n)=f(n)=\Theta(n) T ( n ) = f ( n ) = Θ ( n )
归并排序
归并排序中,a = b = 2 a=b=2 a = b = 2 ,而f ( n ) f(n) f ( n ) 等于多少呢?我们考察归并的过程,两个已经排序好的数组合并成一个时,比较操作的次数是 O ( n ) O(n) O ( n ) 的,而将元素转移到临时数组再转移回来所用的时间也是O ( n ) O(n) O ( n ) 的,显然f ( n ) = O ( n ) f(n)=O(n) f ( n ) = O ( n )
n log b a = n n^{\log _b a} = n n l o g b a = n 与f ( n ) f(n) f ( n ) 同阶,则T ( n ) = n log n T(n)=n \log n T ( n ) = n log n