目录
如何描述一个算法的运行时间
输入规模与运行时间
通常算法需要的运行时间与输入规模同步增长. 在介绍如何描述算法运行时间之前,下面先对输入规模( i n p u t   s i z e \mathbf{input\,size} inputsize)和运行时间( r u n n i n g   t i m e \mathbf{running\,time} runningtime)做出更为严谨的定义:
输入规模: 输入规模取决于讨论的问题本身. 对于不同的问题,输入规模的量度不同. 比如排序问题,输入规模的量度指的是输入中的项数. 而如果算法输入的是一个图,那么输入规模需要用顶点数和边数来描述.
运行时间: 一个算法对于一个特定输入的运行时间指的是被执行的基本操作数或步数,与其对应执行时间乘积的总和.
运行时间函数
上面已经提到,通常算法需要的运行时间会与其输入规模同步增长. 所以常常把一个程序的运行时间描述为其输入规模的函数. 对于具有   n   \,n\, n个值的输入,使用   T ( n ) \,T(n) T(n)表示运行时间. 由于运行时间函数要分析到每一条代码行的代价与时间,表示往往十分复杂. 所以不适合作为算法运行时间的评判标准.
渐近运行时间
虽然有时我们可以精确地确定一个算法的运行时间,但其分析过程可能极其复杂,而只是得到了多余的精度. 所以通常并不值得这么做. 当输入规模足够大时,精确运行时间中的倍增常量(系数)和低阶项会被输入规模自身的影响所支配. 而我们真正感兴趣的是运行时间的增长率和增长量级. 因而在输入规模足够大的时候,我们往往只研究算法的渐近( a s y m p t o t i c \mathbf{asymptotic} asymptotic)效率. 即我们只关心在输入规模无限增加时的极限中,算法的运行时间是如何随之增大而增加的.
- 所谓渐近分析,是一种描述函数在极限附近的行为的方法.
通常渐近更有效的某个算法将会是一种最好的选择(除了对于一些非常小的输入规模).
时间复杂度
时间复杂度( T i m e   C o m p l e x i t y \mathbf{Time\,Complexity} TimeComplexity)是分析算法运行时间使用最为广泛的概念. 通常被作为评判一个算法好坏的一个指标. 它定性地描述了一个算法的运行时间,本质上就是使用渐近运行时间来分析并用渐近记号表示.
渐近记号
描述算法的渐近运行时间我们需要使用渐近记号(
a
s
y
m
p
t
o
t
i
c
 
n
o
t
a
t
i
o
n
\mathbf{asymptotic\,notation}
asymptoticnotation).
渐近记号也适用于刻画算法的某个其他方面,例如空间、数量等.
下面对五种渐近符号进行一一介绍.
Θ记号 (Θ-notation)
定义
对于一个给定的函数
 
g
(
n
)
\,g(n)
g(n),用
Θ
(
g
(
n
)
)
Θ(g(n))
Θ(g(n))来表示以下的函数集合:
Θ
(
g
(
n
)
)
=
{
f
(
n
)
:
存
在
正
常
数
 
c
1
,
c
2
,
n
0
,
使
得
当
 
n
⩾
n
0
时
,
总
有
 
0
⩽
c
1
g
(
n
)
⩽
f
(
n
)
⩽
c
2
g
(
n
)
}
.
Θ(g(n)) = \{f(n): 存在正常数\,c_1, c_2, n_0,使得当\,n \geqslant n_0时,总有\,0\leqslant c_1g(n) \leqslant f(n)\leqslant c_2g(n)\}.
Θ(g(n))={f(n):存在正常数c1,c2,n0,使得当n⩾n0时,总有0⩽c1g(n)⩽f(n)⩽c2g(n)}.
解释说明
Θ ( g ( n ) ) Θ(g(n)) Θ(g(n))实质上是一个函数集合. 一个特定算法的运行时间若用   Θ ( g ( n ) ) \,Θ(g(n)) Θ(g(n))表示,则意味着其运行时间函数   f ( n ) ∈ Θ ( g ( n ) ) \,f(n)\inΘ(g(n)) f(n)∈Θ(g(n)). 对于足够大(大于   n 0 \,n_0 n0)的输入规模   n \,n n,必然存在两个正常数   c 1 , c 2 \,c_1, c_2 c1,c2,使得   f ( n )   \,f(n)\, f(n)夹在   c 1 g ( n ) \,c_1g(n) c1g(n)和   c 2 g ( n ) \,c_2g(n) c2g(n)两函数中间. 称   g ( n ) \,g(n) g(n)为   f ( n ) \,f(n) f(n)的一个渐近紧确界( a s y m p t o t i c a l l y   t i g h t   b o u n d \mathbf{asymptotically\,tight\,bound} asymptoticallytightbound). 可理解为 f ( n )   f(n)\, f(n)在一个常数因子范围内与 g ( n )   g(n)\, g(n)相等.
- 通常将   f ( n ) ∈ Θ ( g ( n ) )   \,f(n)\inΘ(g(n))\, f(n)∈Θ(g(n))记为   f ( n ) = Θ ( g ( n ) ) \,f(n)=Θ(g(n)) f(n)=Θ(g(n)).
- 定义要求对于任何   f ( n ) ∈ Θ ( g ( n ) ) \,f(n)\inΘ(g(n)) f(n)∈Θ(g(n))都必须是渐近非负的(无论   n \,n n有多大, f ( n )   f(n)\, f(n)都是非负的). 之后提到的渐近符号也满足这个要求.
- 一般来说,对于任意多项式   p ( n ) = ∑ n → ∞ d a i n i \,p(n) =\sum \limits_{n \to \infty}\limits^{d}a_in^i p(n)=n→∞∑daini,其中   a i \,a_i ai为常量且   a d > 0 \,a_d>0 ad>0,有   p ( n ) = Θ ( n d ) \,p(n) = Θ(n^d) p(n)=Θ(nd). 即扔掉低阶项并忽略常量(包括系数). 因为任意常量都是一个0阶多项式,所以可以将任意常量函数表示成   Θ ( n 0 )   \,Θ(n^0)\, Θ(n0)或   Θ ( 1 )   \,Θ(1)\, Θ(1)(这是一种对 Θ Θ Θ记号的活用).
O记号 (O-notation)
定义
对于一个给定的函数
 
g
(
n
)
\,g(n)
g(n),用
O
(
g
(
n
)
)
O(g(n))
O(g(n))来表示以下的函数集合:
O
(
g
(
n
)
)
=
{
f
(
n
)
:
存
在
正
常
数
 
c
 
和
 
n
0
,
使
得
当
 
n
⩾
n
0
时
,
总
有
 
0
⩽
f
(
n
)
⩽
c
g
(
n
)
}
.
O(g(n)) = \{f(n): 存在正常数\,c\,和\,n_0,使得当\,n \geqslant n_0时,总有\,0 \leqslant f(n)\leqslant cg(n)\}.
O(g(n))={f(n):存在正常数c和n0,使得当n⩾n0时,总有0⩽f(n)⩽cg(n)}.
解释说明
若   f ( n ) ∈ O ( g ( n ) ) \,f(n)\in O(g(n)) f(n)∈O(g(n)),对于足够大的输入规模   n \,n n,必然存在正常数   c \,c c,使得   f ( n )   \,f(n)\, f(n)总在   c g ( n ) \,cg(n) cg(n)下方. Θ Θ Θ记号给出了一个函数的渐近紧确的上界和下界,而使用   O \,O O记号,我们只给出了一个函数的渐近上界( a s y m p t o t i c a l l y   u p p e r   b o u n d \mathbf{asymptotically\,upper\,bound} asymptoticallyupperbound).
- 可以看出   f ( n ) = O ( g ( n ) )   \,f(n)= O(g(n))\, f(n)=O(g(n))蕴含   f ( n ) = Θ ( g ( n ) ) \,f(n)=Θ(g(n)) f(n)=Θ(g(n)). 也就是说 Θ Θ Θ记号是比   O \,O O记号更强的概念( Θ ( g ( n ) ) ⊆ O ( g ( n ) ) Θ(g(n))\subseteq O(g(n)) Θ(g(n))⊆O(g(n))).
- 既然   O \,O O记号描述的是一个函数的上界,那么它就一般用来限制算法最坏情况(worst-case)的运行时间(但不能代表运行时间,因实际运行时间是变化的,依赖于   n   \,n\, n的规模).
- 当我们称一个算法的运行时间为   O ( g ( n ) ) \,O(g(n)) O(g(n))时,意指存在一个属于   O ( g ( n ) ) \,O(g(n)) O(g(n))的函数   f ( n ) \,f(n) f(n),无论选择怎样的规模为   n   \,n\, n的输入(足够大),其运行时间的上界都是   f ( n ) \,f(n) f(n). 即算法最坏情况运行时间是   O ( g ( n ) ) \,O(g(n)) O(g(n))
Ω记号 (Ω-notation)
定义
对于一个给定的函数
 
g
(
n
)
\,g(n)
g(n),用
O
(
g
(
n
)
)
O(g(n))
O(g(n))来表示以下的函数集合:
Ω
(
g
(
n
)
)
=
{
f
(
n
)
:
存
在
正
常
数
 
c
 
和
 
n
0
,
使
得
当
 
n
⩾
n
0
时
,
总
有
 
0
⩽
c
g
(
n
)
⩽
f
(
n
)
}
.
\Omega(g(n)) = \{f(n): 存在正常数\,c\,和\,n_0,使得当\,n \geqslant n_0时,总有\,0 \leqslant cg(n)\leqslant f(n)\}.
Ω(g(n))={f(n):存在正常数c和n0,使得当n⩾n0时,总有0⩽cg(n)⩽f(n)}.
解释说明
和   O \,O O记号相对应,使用   Ω \,\Omega Ω记号,我们只给出了一个函数的渐近下界( a s y m p t o t i c a l l y   l o w e r   b o u n d \mathbf{asymptotically\,lower\,bound} asymptoticallylowerbound).
- 可以看出   f ( n ) = Ω ( g ( n ) )   \,f(n)= \Omega(g(n))\, f(n)=Ω(g(n))蕴含   f ( n ) = Θ ( g ( n ) ) \,f(n)=Θ(g(n)) f(n)=Θ(g(n)). 也就是说 Θ Θ Θ记号是比   Ω \,\Omega Ω记号更强的概念( Θ ( g ( n ) ) ⊆ Ω ( g ( n ) ) Θ(g(n))\subseteq \Omega(g(n)) Θ(g(n))⊆Ω(g(n))).
-   Ω \,\Omega Ω记号描述函数的一个上界,用来限制算法最好情况(best-case)的运行时间(不能代表运行时间,因实际运行时间是变化的,依赖于   n   \,n\, n的规模).
- 当称一个算法的运行时间为   Ω ( g ( n ) ) \,\Omega(g(n)) Ω(g(n))时,意味着无论输入规模   n \,n n选多少(足够大),运行时间至少是一个常数时间   g ( n ) \,g(n) g(n).
三种大写渐近记号对比
图例对比
特别说明
上文定义中 O O O记号和   Ω \,\Omega Ω记号只要求渐近上界和渐近下界(而没有要求是多么紧确的界),因此可能会出现   n = O ( n 2 )   \,n = O(n^2)\, n=O(n2)这样的式子. 而   Θ \,\Theta Θ记号必须是渐近紧确界.
重要定理
对于任意两个函数   g ( n ) \,g(n) g(n)和   f ( n ) \,f(n) f(n),当且仅当   f ( n ) = O ( g ( n ) ) \,f(n) = O(g(n)) f(n)=O(g(n))且   f ( n ) = Ω ( g ( n ) ) \,f(n) = \Omega(g(n)) f(n)=Ω(g(n))时,有   f ( n ) = Θ ( g ( n ) ) \,f(n) = \Theta(g(n)) f(n)=Θ(g(n)).
- 该定理根据三种记号的定义是显而易见的.
等式中渐近符号的含义
当渐近符号出现在等式右边
形如:
n
=
O
(
n
2
)
n = O(n^2)
n=O(n2)、
2
n
2
+
3
n
+
1
=
2
n
2
+
Θ
(
n
)
2n^2+3n+1=2n^2 +\Theta(n)
2n2+3n+1=2n2+Θ(n)这样的式子,我们是这样解释的:
比如
2
n
2
+
3
n
+
1
=
2
n
2
+
Θ
(
n
)
2n^2+3n+1=2n^2 +\Theta(n)
2n2+3n+1=2n2+Θ(n)的含义就是
2
n
2
+
3
n
+
1
=
2
n
2
+
f
(
n
)
2n^2+3n+1=2n^2 +f(n)
2n2+3n+1=2n2+f(n),
f
(
n
)
f(n)
f(n)是属于集合
 
Θ
(
n
)
\,\Theta(n)
Θ(n)的一个渐近函数. 在这种情况下,
f
(
n
)
=
3
n
+
1
f(n)=3n+1
f(n)=3n+1,我们将它视作一个我们并不关注名称的匿名函数
Θ
(
n
)
\Theta(n)
Θ(n). 通过这样的表示方式,可以帮助我们消除等式中一些无关紧要的细节和混乱.
当渐近符号出现在等式左边
形如: 2 n 2 + Θ ( n ) = Θ ( n 2 ) 2n^2+\Theta(n)=\Theta(n^2) 2n2+Θ(n)=Θ(n2),我们使用以下规则来解释这种等式:无论怎样选择等号左边的匿名函数,总有一种方法选择等号右边的匿名函数使得等式成立.
o记号 (o-notation)
定义
o ( g ( n ) ) = { f ( n ) : 对 任 意 正 常 数   c   , 存 在 常 数   n 0 , 使 得 当   n ⩾ n 0 时 , 总 有   0 ⩽ f ( n ) < c g ( n ) } . o(g(n)) = \{f(n): 对任意正常数\,c\,,存在常数\,n_0,使得当\,n \geqslant n_0时,总有\,0 \leqslant f(n)< cg(n)\}. o(g(n))={f(n):对任意正常数c,存在常数n0,使得当n⩾n0时,总有0⩽f(n)<cg(n)}.
解释说明
这里与 O ( g ( n ) ) O(g(n)) O(g(n))对比,前面有解释   O \,O O记号提供的渐近上界可能是也可能不是渐近紧确的,比如   2 n 2 = O ( n 2 ) \,2n^2=O(n^2) 2n2=O(n2)是渐近紧确的,而   2 n = O ( n 2 ) \,2n=O(n^2) 2n=O(n2)却不是. 而   o ( g ( n ) ) \,o(g(n)) o(g(n))只能表示一个非渐近紧确的上界. 比如: 2 n 2 = o ( n 2 ) 2n^2=o(n^2) 2n2=o(n2),而   2 n 2 ≠ o ( n 2 ) \,2n^2 \neq o(n^2) 2n2̸=o(n2).
- 对于   f ( n ) = o ( g ( n ) ) \,f(n)=o(g(n)) f(n)=o(g(n)),有 lim n → ∞ f ( x ) g ( x ) = 0 \lim \limits_{n \to \infty}\frac{f(x)}{g(x)} = 0 n→∞limg(x)f(x)=0. 称   f ( n ) \,f(n) f(n)渐近小于   g ( n ) \,g(n) g(n).
ω记号 (ω-notation)
定义
ω ( g ( n ) ) = { f ( n ) : 对 任 意 正 常 数   c   , 存 在 常 数   n 0 , 使 得 当   n ⩾ n 0 时 , 总 有   0 ⩽ c g ( n ) < f ( n ) } . \omega(g(n)) = \{f(n): 对任意正常数\,c\,,存在常数\,n_0,使得当\,n \geqslant n_0时,总有\,0 \leqslant cg(n)<f(n)\}. ω(g(n))={f(n):对任意正常数c,存在常数n0,使得当n⩾n0时,总有0⩽cg(n)<f(n)}.
解释说明
ω \omega ω记号和 Ω \Omega Ω记号的关系类似于 o o o记号和 O O O记号的关系. ω \omega ω记号表示一个非渐近紧确的下界.
- 对于   f ( n ) = ω ( g ( n ) ) \,f(n)=\omega(g(n)) f(n)=ω(g(n)),有 lim n → ∞ f ( x ) g ( x ) = ∞ \lim \limits_{n \to \infty}\frac{f(x)}{g(x)} = \infty n→∞limg(x)f(x)=∞.称   f ( n ) \,f(n) f(n)渐近大于   g ( n ) \,g(n) g(n).
五种渐近符号的关系与性质
关系
性质
下面假定   f ( n ) \,f(n) f(n)和   g ( n ) \,g(n) g(n)渐近为正.
传递性
f
(
n
)
=
Θ
(
g
(
n
)
)
且
 
g
(
n
)
=
Θ
(
h
(
n
)
)
→
f
(
n
)
=
Θ
(
h
(
n
)
)
f(n) = \Theta(g(n))且\,g(n)=\Theta(h(n)) \to f(n)=\Theta(h(n))
f(n)=Θ(g(n))且g(n)=Θ(h(n))→f(n)=Θ(h(n))
f
(
n
)
=
O
(
g
(
n
)
)
且
 
g
(
n
)
=
O
(
h
(
n
)
)
→
f
(
n
)
=
O
(
h
(
n
)
)
f(n) = O(g(n))且\,g(n)=O(h(n)) \to f(n)=O(h(n))
f(n)=O(g(n))且g(n)=O(h(n))→f(n)=O(h(n))
f
(
n
)
=
Ω
(
g
(
n
)
)
且
 
g
(
n
)
=
Ω
(
h
(
n
)
)
→
f
(
n
)
=
Ω
(
h
(
n
)
)
f(n) = \Omega(g(n))且\,g(n)=\Omega(h(n)) \to f(n)=\Omega(h(n))
f(n)=Ω(g(n))且g(n)=Ω(h(n))→f(n)=Ω(h(n))
f
(
n
)
=
ω
(
g
(
n
)
)
且
 
g
(
n
)
=
ω
(
h
(
n
)
)
→
f
(
n
)
=
ω
(
h
(
n
)
)
f(n) = \omega(g(n))且\,g(n)=\omega(h(n)) \to f(n)=\omega(h(n))
f(n)=ω(g(n))且g(n)=ω(h(n))→f(n)=ω(h(n))
f
(
n
)
=
o
(
g
(
n
)
)
 
且
 
g
(
n
)
=
o
(
h
(
n
)
)
→
f
(
n
)
=
o
(
h
(
n
)
)
f(n) = o(g(n))\,且\,g(n)=o(h(n)) \to f(n)=o(h(n))
f(n)=o(g(n))且g(n)=o(h(n))→f(n)=o(h(n))
自反性
f
(
n
)
=
Θ
(
f
(
n
)
)
f(n) = \Theta(f(n))
f(n)=Θ(f(n))
f
(
n
)
=
O
(
f
(
n
)
)
f(n) = O(f(n))
f(n)=O(f(n))
f
(
n
)
=
Ω
(
f
(
n
)
)
f(n) = \Omega(f(n))
f(n)=Ω(f(n))
∗
*
∗ 注意小写渐近记号不满足自反性(一个非渐紧确界不可能是它自己).
对称性
f ( n ) = Θ ( g ( n ) ) ↔ g ( n ) = Θ ( f ( n ) ) f(n) = \Theta(g(n)) \leftrightarrow g(n) = \Theta(f(n)) f(n)=Θ(g(n))↔g(n)=Θ(f(n))
转置对称性
f
(
n
)
=
O
(
g
(
n
)
)
↔
g
(
n
)
=
Ω
(
f
(
n
)
)
f(n) = O(g(n)) \leftrightarrow g(n) = \Omega(f(n))
f(n)=O(g(n))↔g(n)=Ω(f(n))
f
(
n
)
=
o
(
g
(
n
)
)
↔
g
(
n
)
=
ω
(
f
(
n
)
)
f(n) = o(g(n)) \leftrightarrow g(n) = \omega(f(n))
f(n)=o(g(n))↔g(n)=ω(f(n))
渐近比较类比实数比较
渐近比较 | 实数比较 |
---|---|
f ( n ) = O ( g ( n ) ) f(n) = O(g(n)) f(n)=O(g(n)) | a ⩽ b a\leqslant b a⩽b |
f ( n ) = Ω ( g ( n ) ) f(n) = \Omega(g(n)) f(n)=Ω(g(n)) | a ⩾ b a\geqslant b a⩾b |
f ( n ) = Θ ( g ( n ) ) f(n) = \Theta(g(n)) f(n)=Θ(g(n)) | a = b a = b a=b |
f ( n ) = o ( g ( n ) ) f(n) = o(g(n)) f(n)=o(g(n)) | a < b a<b a<b |
f ( n ) = ω ( g ( n ) ) f(n) = \omega(g(n)) f(n)=ω(g(n)) | a > b a>b a>b |