文章目录
前言
求解递归式的时间复杂度是学习算法很重要的一部分
本文主要讲解了利用“主方法”来求解递归式的时间复杂度,并对其进行了一定程度上的逻辑证明
一、用主方法求解递归式
(1)递归式的一般形式
T
(
n
)
=
a
T
(
n
/
b
)
+
f
(
n
)
,其中
a
⩾
1
和
b
⩾
1
是常数
T(n)=aT(n/b)+f(n), 其中a\geqslant1和b\geqslant1是常数
T(n)=aT(n/b)+f(n),其中a⩾1和b⩾1是常数
该一般形式描述的是分治策略算法的运行时间:
该算法将规模为n的问题分解为a个子问题,每个子问题规模为n/b,其中a和b都是常数。a个子问题递归地进行求解,每个花费时间 T ( n / b ) T(n/b) T(n/b)。函数 f ( n ) f(n) f(n)包含了问题分解和子问题解合并的代价。
(2)主方法
令
a
⩾
1
和
b
⩾
1
是常数,
f
(
n
)
是一个函数,
T
(
n
)
是定义在非负整数上的递归式:
令a\geqslant1和b\geqslant1是常数,f(n)是一个函数,T(n)是定义在非负整数上的递归式:
令a⩾1和b⩾1是常数,f(n)是一个函数,T(n)是定义在非负整数上的递归式:
T
(
n
)
=
a
T
(
n
/
b
)
+
f
(
n
)
T(n)=aT(n/b)+f(n)
T(n)=aT(n/b)+f(n)
其中我们将
n
/
b
解释为
⌊
n
/
b
⌋
或者
⌈
n
/
b
⌉
。那么
T
(
n
)
有如下渐进界:
其中我们将n/b解释为\lfloor n/b\rfloor或者 \lceil n/b\rceil。那么T(n)有如下渐进界:
其中我们将n/b解释为⌊n/b⌋或者⌈n/b⌉。那么T(n)有如下渐进界:
1.
若对某个常数
ε
>
0
有
f
(
n
)
=
O
(
n
l
o
g
b
a
−
ε
)
,则
T
(
n
)
=
Θ
(
n
l
o
g
b
a
)
1.若对某个常数\varepsilon>0有f(n)=O(n^{{log_{b}a}-\varepsilon}),则T(n)=\Theta(n^{log_ba})
1.若对某个常数ε>0有f(n)=O(nlogba−ε),则T(n)=Θ(nlogba)
2.
若
f
(
n
)
=
Θ
(
n
l
o
g
b
a
)
,则
T
(
n
)
=
Θ
(
n
l
o
g
b
a
l
g
n
)
2.若f(n)=\Theta(n^{log_{b}a}),则T(n)=\Theta(n^{log_{b}a}lgn)
2.若f(n)=Θ(nlogba),则T(n)=Θ(nlogbalgn)
3.
若对某个常数
ε
>
0
有
f
(
n
)
=
Ω
(
n
l
o
g
b
a
+
ε
)
,
且对某个常数
c
<
1
和所有足够大的
n
有
a
f
(
n
/
b
)
⩽
c
f
(
n
)
,则
T
(
n
)
=
Θ
(
f
(
n
)
)
3.若对某个常数\varepsilon>0有f(n)=\Omega(n^{log_b{a}+\varepsilon}),且对某个常数c<1和所有足够大的n有 af(n/b)\leqslant cf(n),则T(n)=\Theta(f(n))
3.若对某个常数ε>0有f(n)=Ω(nlogba+ε),且对某个常数c<1和所有足够大的n有af(n/b)⩽cf(n),则T(n)=Θ(f(n))
(3)理解主方法
对于三种情况的每一种,我们将函数 f ( n ) f(n) f(n)与函数 n l o g b a n^{log_ba} nlogba进行比较。直觉上,两个函数较大者决定了递归式的解。若函数 n l o g b a n^{log_ba} nlogba更大,如情况1,则解为 T ( n ) = Θ ( n l o g b a ) T(n)=\Theta(n^{log_ba}) T(n)=Θ(nlogba)。如函数 f ( n ) f(n) f(n)更大,如情况3,则解为 T ( n ) = Θ ( f ( n ) ) T(n)=\Theta(f(n)) T(n)=Θ(f(n))。若两个函数大小相当,如情况2,则乘上一个对数因子,解为 T ( n ) = Θ ( n l o g b a l g n ) = Θ ( f ( n ) l g n ) T(n)=\Theta(n^{{log_{b}a }}lgn)=\Theta(f(n)lgn) T(n)=Θ(nlogbalgn)=Θ(f(n)lgn)
再此直觉之外,我们需要了解: f ( n ) 与 n l o g b a 的比较,是在多项式意义上的比较,需要渐进小于,大于 f(n)与n^{log_ba}的比较,是在多项式意义上的比较,需要渐进小于,大于 f(n)与nlogba的比较,是在多项式意义上的比较,需要渐进小于,大于
那么我们可以发现,这三种情况并未覆盖 f ( n ) f(n) f(n)的所有可能性。情况1与情况2,情况2与情况3之间都有间隙,这归根结底是多项式层面的小于大于导致的。这时就不能通过主方法来求解递归式。
二、利用主方法求解一些实例
(1)例子1
T
(
n
)
=
9
T
(
n
/
3
)
+
n
T(n)=9T(n/3)+n
T(n)=9T(n/3)+n
对于递归式,我们有:
a
=
9
,
b
=
3
,
f
(
n
)
=
n
,
因此
n
l
o
g
b
a
=
n
l
o
g
3
9
=
Θ
(
n
2
)
a=9,b=3,f(n)=n,因此n^{log_ba}=n^{log_39}=\Theta(n^2)
a=9,b=3,f(n)=n,因此nlogba=nlog39=Θ(n2),又因为有
f
(
n
)
=
n
=
O
(
n
l
o
g
3
9
−
ε
)
f(n)=n=O(n^{{log_39}-\varepsilon})
f(n)=n=O(nlog39−ε),所以满足主方法的情况1,因此
T
(
n
)
=
Θ
(
n
2
)
T(n)=\Theta(n^2)
T(n)=Θ(n2)
(2)例子2
T
(
n
)
=
T
(
2
n
/
3
)
+
1
T(n)=T(2n/3)+1
T(n)=T(2n/3)+1
对于递归式,我们有:
a
=
1
,
b
=
3
/
2
,
f
(
n
)
=
1
,
因此
n
l
o
g
b
a
=
n
l
o
g
3
2
1
=
Θ
(
1
)
a=1,b=3/2,f(n)=1,因此n^{log_ba}=n^{log_{\frac{3}{2}}1}=\Theta(1)
a=1,b=3/2,f(n)=1,因此nlogba=nlog231=Θ(1),又因为有
f
(
n
)
=
1
=
Θ
(
1
)
f(n)=1=\Theta(1)
f(n)=1=Θ(1),所以满足主方法的情况2,因此
T
(
n
)
=
Θ
(
l
g
n
)
T(n)=\Theta(lgn)
T(n)=Θ(lgn)
(3)例子3
T
(
n
)
=
3
T
(
n
/
4
)
+
n
l
g
n
T(n)=3T(n/4)+nlgn
T(n)=3T(n/4)+nlgn
对于递归式,我们有:
a
=
3
,
b
=
4
,
f
(
n
)
=
n
l
g
n
,
因此
n
l
o
g
b
a
=
n
l
o
g
4
3
=
Θ
(
n
0.793
)
a=3,b=4,f(n)=nlgn,因此n^{log_ba}=n^{log_43}=\Theta(n^{0.793})
a=3,b=4,f(n)=nlgn,因此nlogba=nlog43=Θ(n0.793),又因为有
f
(
n
)
=
n
l
g
n
=
Ω
(
n
l
o
g
4
3
+
ε
)
f(n)=nlgn=\Omega(n^{log_43+\varepsilon})
f(n)=nlgn=Ω(nlog43+ε),如果满足正则条件成立,就可以满足主方法的情况3。当n足够大的时候,对于
c
=
3
/
4
,
a
f
(
n
/
b
)
=
3
(
n
/
4
)
l
g
(
n
/
4
)
⩽
(
3
/
4
)
n
l
g
n
=
c
f
(
n
)
c=3/4,af(n/b)=3(n/4)lg(n/4)\leqslant(3/4)nlgn=cf(n)
c=3/4,af(n/b)=3(n/4)lg(n/4)⩽(3/4)nlgn=cf(n)。因此
T
(
n
)
=
Θ
(
n
l
g
n
)
T(n)=\Theta(nlgn)
T(n)=Θ(nlgn)
(4)例子4
T
(
n
)
=
2
T
(
n
/
2
)
+
n
l
g
n
T(n)=2T(n/2)+nlgn
T(n)=2T(n/2)+nlgn
对于递归式,我们有:
a
=
2
,
b
=
2
,
f
(
n
)
=
n
l
g
n
,
因此
n
l
o
g
b
a
=
n
l
o
g
2
2
=
Θ
(
n
)
a=2,b=2,f(n)=nlgn,因此n^{log_ba}=n^{log_22}=\Theta(n)
a=2,b=2,f(n)=nlgn,因此nlogba=nlog22=Θ(n)。但是因为
f
(
n
)
=
n
l
g
n
f(n)=nlgn
f(n)=nlgn并不是渐进大于n。因为
f
(
n
)
n
l
o
g
b
a
=
l
g
n
,
都渐进小于
n
ε
\frac{f(n)}{n^{log_ba}}=lgn,都渐进小于n^{\varepsilon}
nlogbaf(n)=lgn,都渐进小于nε。因此递归式落入了情况2和情况3之间的间隙,无法用主方法解决。
(5)例子5
T
(
n
)
=
2
T
(
n
/
2
)
+
Θ
(
n
)
T(n)=2T(n/2)+\Theta(n)
T(n)=2T(n/2)+Θ(n)
这个递归式是最大和子数组问题和归并排序的算法运行时间描述。这里我们运用主方法求解:
a
=
2
,
b
=
2
,
f
(
n
)
=
Θ
(
n
)
,
n
l
o
g
b
a
=
n
l
o
g
2
2
=
Θ
(
n
)
=
f
(
n
)
a=2,b=2,f(n)=\Theta(n),n^{log_ba}=n^{log_22}=\Theta(n)=f(n)
a=2,b=2,f(n)=Θ(n),nlogba=nlog22=Θ(n)=f(n),因此满足情况2,因此:
T
(
n
)
=
Θ
(
n
l
g
n
)
T(n)=\Theta(nlgn)
T(n)=Θ(nlgn)
(6)例子6
T
(
n
)
=
8
T
(
n
/
2
)
+
Θ
(
n
2
)
T(n)=8T(n/2)+\Theta(n^2)
T(n)=8T(n/2)+Θ(n2)
这个是矩阵乘法的普通递归算法的时间描述:
a
=
8
,
b
=
2
,
f
(
n
)
=
Θ
(
n
2
)
,
那么
n
l
o
g
b
a
=
n
l
o
g
2
8
=
Θ
(
n
3
)
,
又因为
f
(
n
)
=
O
(
n
l
o
g
2
8
−
ε
)
a=8,b=2,f(n)=\Theta(n^2),那么n^{log_ba}=n^{log_28}=\Theta(n^3),又因为f(n)=O(n^{log_28-\varepsilon})
a=8,b=2,f(n)=Θ(n2),那么nlogba=nlog28=Θ(n3),又因为f(n)=O(nlog28−ε),因此,
T
(
n
)
=
Θ
(
n
3
)
T(n)=\Theta(n^3)
T(n)=Θ(n3)
(7)例子7
T
(
n
)
=
7
T
(
n
/
2
)
+
Θ
(
n
2
)
T(n)=7T(n/2)+\Theta(n^2)
T(n)=7T(n/2)+Θ(n2)
这个是矩阵乘法的Strassen(斯特拉森)算法的时间描述:
a
=
7
,
b
=
2
,
f
(
n
)
=
Θ
(
n
2
)
,
那么
n
l
o
g
b
a
=
n
l
o
g
2
7
,
又因为
2.80
<
l
g
7
<
2.81
,
因此若
ε
=
0.8
,
那么有
f
(
n
)
=
O
(
n
l
o
g
2
7
−
ε
)
a=7,b=2,f(n)=\Theta(n^2),那么n^{log_ba}=n^{log_27},又因为2.80<lg7<2.81,因此若\varepsilon=0.8,那么有f(n)=O(n^{log_27-\varepsilon})
a=7,b=2,f(n)=Θ(n2),那么nlogba=nlog27,又因为2.80<lg7<2.81,因此若ε=0.8,那么有f(n)=O(nlog27−ε),因此,
T
(
n
)
=
Θ
(
n
l
g
7
)
T(n)=\Theta(n^{lg7})
T(n)=Θ(nlg7)
总结
除了“主方法”之外,还有代入法,递归树两大方法。
例子5,6,7涉及的算法可以参考文章:
链接: 《算法导论》学习(三)---- 最大和子数组问题的分治方法
链接: 《算法导论》学习(四)---- 矩阵乘法的Strassen(斯特拉森)算法