前言
在第二章中提出过,当算法的输入n非常大的时候,虽然有时我们能通过一定的方法得到较为精确的运行时间,但是很多时候我们并不值得去花精力求得多余的精度,因为精确运行时间中的倍增常量和低阶项已经被输入规模本身的影响所支配。所以当规模n足够大且运行时间只与增长量级有关系时,我们要研究算法的渐近效率。 本章主要介绍几中标准的方法来简化算法的渐近分析。
渐近记号
‘渐近’一词意味着无限接近一个曲线或者一个值(在某种限制下)我们来举一个例子:
如果我们有两个算法,下面的表达式表示他们执行所需要的时间,那么:
- 表达式1:(20n^ 2 + 3n – 4)
- 表达式2:(n ^3 + 100n – 2)
现在,我们应该观察函数如何随着n(输入)的值的增长而增长,这完全取决于
- 表达式1 渐近:n^2
- 表达式2渐近:n^3。
以“表达式2” 为例子,描述渐近记号,运行时间,函数的关系
渐近记号(n^3)=运行时间T(n) = n ^3 + 100n – 2
渐近紧确界 :Big Theta (Θ)
Big Theta 通常表示算法的实际执行时间所在的平均值的或者范围的时间复杂度。对于一个已知的函数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≥n_0,有0≤c_1g(n)≤f(n)≤c_2g(n)} Θ(g(n))=f(n):存在正常量c1、c2和n0,使得对所有n≥n0,有0≤c1g(n)≤f(n)≤c2g(n)
这里称g(n)是f(n)的一个渐进紧确界,也就是说 Θ 记号渐近地给出一个函数的上界和下界:
渐近上限: Big Oh(O)
这种表示法被称为算法的上界,或者算法的最坏情况.对于一个已知的函数g(n),我们定义O(g(n))如下:
O
(
g
(
n
)
)
=
f
(
n
)
:
存
在
正
常
量
c
和
n
0
,
使
得
对
所
有
n
≥
n
0
,
有
0
≤
f
(
n
)
≤
c
g
(
n
)
Ο(g(n) ) = {f(n): 存在正常量c和n_0,使得对所有n≥n_0,有0≤f(n)≤cg(n)}
O(g(n))=f(n):存在正常量c和n0,使得对所有n≥n0,有0≤f(n)≤cg(n)
O记号只给出一个渐进上界:
渐近下限:Big Omega (Ω)
其来表示任何算法的下限,或者最佳情况。对于一个已知的函数g(n),我们定义Ω(g(n))如下:
Ω
(
g
(
n
)
)
=
f
(
n
)
:
存
在
正
常
量
c
和
n
0
,
使
得
对
所
有
n
≥
n
0
,
有
0
≤
c
g
(
n
)
≤
f
(
n
)
Ω( g(n) ) = {f(n): 存在正常量c和n_0,使得对所有n≥n_0,有0≤cg(n)≤f(n)}
Ω(g(n))=f(n):存在正常量c和n0,使得对所有n≥n0,有0≤cg(n)≤f(n)
Ω 记号只给出一个渐进下界:
非渐近紧确上界:o(小-oh)
ο g ( n ) ) = f ( n ) : 对 任 意 正 常 量 c > 0 , 存 在 常 量 n 0 > 0 , 使 得 对 所 有 n ≥ n 0 , 有 0 ≤ f ( n ) < c g ( n ) ο g(n) ) = {f(n): 对任意正常量c>0,存在常量n_0>0,使得对所有n≥n_0,有0≤f(n)<cg(n)} οg(n))=f(n):对任意正常量c>0,存在常量n0>0,使得对所有n≥n0,有0≤f(n)<cg(n)
非渐近紧确下界:ω(小-omege)
ω ( g ( n ) ) = f ( n ) : 对 任 意 正 常 量 c > 0 , 存 在 常 量 n 0 > 0 , 使 得 对 所 有 n ≥ n 0 , 有 0 ≤ c g ( n ) < f ( n ) ω( g(n)) = {f(n): 对任意正常量c>0,存在常量n_0>0,使得对所有n≥n_0,有0≤cg(n)<f(n)} ω(g(n))=f(n):对任意正常量c>0,存在常量n0>0,使得对所有n≥n0,有0≤cg(n)<f(n)
渐近记号Θ、Ο、o、Ω、ω关系
记号 | 含义 | 通俗理解 |
---|---|---|
Θ | 紧确界 | 相当于"=" |
O | 上界 | 相当于"<=" |
o | 非紧的上界 | 相当于"<" |
Ω | 下界 | 相当于">=" |
ω | 非紧的下界 | 相当于">" |
练习
- 设f(n)与g(n)为渐近非负函数,利用Θ的基本定义,证明max{f(n),g(n)}=Θ(f(n)+g(n))。
已知:
f(n) <= max{f(n),g(n)}
g(n) <= max{f(n),g(n)}
推导:
(f(n) + g(n))/2 <= max{f(n),g(n)}
(f(n) + g(n)) >= max{f(n),g(n)}
即当c0=1/2, c1=1 满足 Θ
max{f(n),g(n)} = Θ(f(n) + g(n))
- 证明对任意实常数a和b,其中b>0,有 (n+a)^b = Θ(n^b)
要想证明上式成立,先要来证明等式:
c1(n^b)<=(n+a)^b<=c2(n^b)
又因b>0可以和c1结合成新的c1,推导出:
c1<=(1+a/n)<=c2
可证明出当n足够大时,不管a正负都接近0, c1,c2分别可取值1/2,2,使得等式成立
- 1-3 解释为什么“算法A的运行时间至少是O(n²)”这句话是无意义的
根据O记号的定义可知,它是用来表示上界的,当用它作为算法的最坏情况运行时间的上界时,就有对任意输入的运行时间的上界
这与题中的“至少是”表达的是同一个意思。所以题中的话是无意义的。
- 1-4 2^(n+1) =O( 2^n )成立吗?2^(2n)= O(2^n)成立吗?
针对第一个等式,
2^(n+1)=2* 2^(n)<=c(2^n)
当c=2时,即等式成立
针对第二个等式,
2^(2n) = 2^n* 2^(n)
c值无法确定, 即等式不成立
- 1-5 证明定理,在o中表示当n趋于无穷大时,函数f(n)相对于g(n)来说就不重要了。
证明:根据o记号的定义:对f(n)=o(g(n)),界o<=f(n)<=cg(n)对所有常数c>0成立,这句话说明了函数g(n)的增长速度要快于f(n),当n趋向无穷大时,差距就更大了
- 1-6 证明:一个算法的运行时间是Θ(g(n))当且仅当其最坏情况运行时间O(g(n)),且最佳情况运行时间是Ω(g(n))
一个算法的运行时间是Θ(g(n)),则说明存在这样两个正常数c1,c2使得(当n充分大时):0<=c1g(n)<=f(n)<=c2g(n)
因而等式0<=c1g(n)<=f(n)成立,即存在正常数c1和n0,该算法的最佳情况运行时间是Ω(g(n))。
同理,因为等式0<=f(n)<=c2g(n)成立,所以该算法的最坏情况运行时间是O(g(n))。
- 1-7 证明o(g(n))∩ω(g(n))是空集。
[1] 0<=f(n)<=cg(n),o(g(n))
[2] 0<=cg(n)<f(n), ω(g(n))
推得:
[1] g(n)>=(1/c)f(n)
[2] g(n)<(1/c)f(n)
可得,o(g(n))和ω(g(n))两集合没有共有部分。即证得o(g(n))∩ω(g(n))是空集。
- 1-8 可以将我们的表示法扩展到有两个参数n和m的情形,其中n和m的值可以以不同的速率,互相独立地趋于无穷。对给定的函数g(n,m),O(g(n,m))为函数集: O ( g ( n , m ) ) = f ( n , m ) : 存 在 正 整 数 c , n 0 和 m 0 , 使 对 所 有 n > = n 0 或 m > = m 0 , 有 0 < = f ( n , m ) < = c g ( n , m ) 。 O(g(n,m))={ f(n,m): 存在正整数c,n0和m0,使对所有n>=n0或m>=m0,有0<=f(n,m)<=cg(n,m) }。 O(g(n,m))=f(n,m):存在正整数c,n0和m0,使对所有n>=n0或m>=m0,有0<=f(n,m)<=cg(n,m)。给出对应的Ω(g(n,m))和Θ(g(n,m))的定义。
[1] Ω(g(n,m))={ f(n,m): 存在正整数c,n0和m0,使对所有n>=n0或m>=m0,有0<=cg(n,m)<=f(n) }
[2] Θ(g(n,m))={ f(n,m): 存在正整数c1,c2,n0和m0,n0和m0,使对所有n>=n0或m>=m0,有0<=c1g(n,m)<=f(n)<=c2g(n,m) }
标准记号与常用函数
回顾一些标准的数学函数与记号并探索它们之间的关系,还将阐明渐近记号的应用。
单调性
- 若m≤n蕴涵f(m)≤f(n),则函数f(n)是单调递增的。
- 若m<n蕴涵f(m)<f(n),则函数f(n)是严格递增的
- 递增类似
向下取整与向上取整
对任意实数x,我们用x表示小于或等于x的最大整数,读作“x的向下取整” ⌊ x ⌋ \lfloor x\rfloor ⌊x⌋并用x表示大于或等于x的最小整数,读作“x的向上取整” ⌈ x ⌉ \lceil x\rceil ⌈x⌉。
模运算
对任意整数a和任意正整数n,amodn的值就是商a/n的余数
a
m
o
d
n
=
a
−
n
∗
⌊
a
/
n
⌋
a \bmod n=a-n* \lfloor a/n \rfloor
amodn=a−n∗⌊a/n⌋
多项式
给定一个非负整数d,n的d次多项式为具有以下形式的一个函数p(n):
p
(
n
)
=
∑
i
=
0
d
a
i
n
i
p(n)=\sum_{i=0}^d a_in^i
p(n)=i=0∑daini
一个多项式为渐近正的当且仅当a>0。对于一个d次渐近正的多项式p(n),有p(n)=Θ(n^d)
指数
对所有实数a>0、m和n,我们有以下恒等式:
a
0
=
1
a
1
=
a
a
−
1
=
1
/
a
(
a
m
)
n
=
a
m
n
(
a
m
)
n
=
(
a
n
)
m
a
m
a
n
=
a
m
+
n
a^0=1\\a^1=a\\a^{-1}=1/a\\(a^m)^n=a^{mn}\\(a^m)^n=(a^n)^m\\a^ma^n=a^{m+n}
a0=1a1=aa−1=1/a(am)n=amn(am)n=(an)maman=am+n
可以通过以下事实使多项式与指数的增长率互相关联。对所有使得a>1的实常量a和b
lim
n
→
∞
n
b
a
n
=
0
⟹
n
b
=
o
(
a
n
)
\lim_{n\to\infty}\frac{n^b}{a^n} = 0 \implies n^b=o(a^n)
n→∞limannb=0⟹nb=o(an)
任意底大于1的指数函数比任意多项式函数增长得快.
自然数e
自然常数e,是一个无理数,也是超越数,其值为2.71828。其定义
e
=
lim
n
→
∞
(
1
+
1
n
)
n
=
∑
n
=
0
∞
1
n
!
e = \lim_{n\to\infty}(1+\frac{1}{n})^n = \sum_{n=0}^\infty \frac{1}{n!}
e=n→∞lim(1+n1)n=n=0∑∞n!1
最早的指数函数指e^x
对数
对数常用记号
lg
n
=
log
2
n
ln
n
=
log
e
n
lg
k
n
=
(
lg
n
)
k
lg
lg
n
=
lg
(
lg
n
)
\lg n = \log_2n \\ \ln n = \log_en \\ \lg^kn = (\lg n)^k \\ \lg\lg n = \lg(\lg n)
lgn=log2nlnn=logenlgkn=(lgn)klglgn=lg(lgn)
根据上述工式
lim
n
→
∞
n
b
a
n
=
0
\lim_{n\to\infty}\frac{n^b}{a^n} = 0
n→∞limannb=0
将lgn代替n并用2^a代替a, 可以使多项式与多对数的增长互相关联
lim
n
→
∞
(
lg
n
)
b
(
2
a
)
lg
n
=
lim
n
→
∞
(
lg
n
)
b
n
a
=
0
=
>
⟹
(
l
g
n
)
b
=
o
(
n
a
)
\lim_{n\to\infty}\frac{(\lg n)^b}{(2^a)^{\lg n}} = \lim_{n\to\infty}\frac{(\lg n)^b}{n^a} = 0 => \implies (lg n)^b=o(n^a)
n→∞lim(2a)lgn(lgn)b=n→∞limna(lgn)b=0=>⟹(lgn)b=o(na)
阶乘
记号n!(读作“n的阶乘”)定义为对整数n≥0,有
n
!
=
{
1
if
n
=
0
n
∗
(
n
−
1
)
!
if
n
>
0
n! = \begin{cases} 1 &\text{if } n=0 \\ n * (n-1)! &\text{if } n>0 \end{cases}
n!={1n∗(n−1)!if n=0if n>0
阶乘函数的一个弱上界是n!≤n^n,因为在阶乘中,n项的每项最多为n.结合斯特林(Stirling)近似公式
可以给出了一个更紧确的上界和下界
n
!
=
o
(
n
n
)
n
!
=
w
(
2
n
)
lg
(
n
!
)
=
Θ
(
n
lg
n
)
n!=o(n^n)\\ n!=w(2^n)\\ \lg (n!)=\Theta(n \lg n)
n!=o(nn)n!=w(2n)lg(n!)=Θ(nlgn)
多重函数
我们使用记号f(i)(n)来表示函数f(n)重复i次作用于一个初值n上,形式化地,假设f(n)为实数集上的一个函数。对非负整数i,我们递归地定义
f
(
i
)
(
n
)
=
{
n
if
i
=
0
f
(
f
(
i
−
1
)
(
n
)
)
if
i
>
0
f^{(i)}(n) = \begin{cases} n &\text{if } i=0 \\ f (f^{(i-1)}(n)) &\text{if } i>0 \end{cases}
f(i)(n)={nf(f(i−1)(n))if i=0if i>0
当f = lg , 我们使用记号lg*n 表示 当 f(n) = n 时,i的数值,也称为多重对数函数(读作“log星n”)例
lg
∗
2
=
1
lg
∗
4
=
2
lg
∗
16
=
3
\lg^*2 =1 \\ \lg^*4 =2 \\ \lg^*16=3
lg∗2=1lg∗4=2lg∗16=3
斐波那契数
使用下面的递归式来定义斐波那契数:
F
0
=
0
F
1
=
1
F
i
=
F
i
−
1
+
F
i
−
2
(
i
≥
2
)
F_0=0 \\ F_1=1 \\ F_i=F_{i-1}+F_{i-2} (i\ge2)
F0=0F1=1Fi=Fi−1+Fi−2 (i≥2)
黄金分割数和共轭数,是方程x^2=x +1的两个根
斐波那契数与黄金分割率及其共轭数关系
练习
- 2-1证明:若f(n)和g(n)是单调递增的函数,则函数f(n)+g(n)和f(g(n))也是单调递增的,此外,若f(n)和g(n)是非负的,则f(n)·g(n)是单调递增的
已知:
n<=m f(n)<=f(m)
n<=m g(n)<=g(m)
=> f(n) + g(n) <= f(m)+g(m)
当n=g(n),m=g(m)
=> f(g(n)) <= f(g(m))
当f(n),g(n)>0
=> f(n) * g(n) <= f(m)*g(m)
- 2-2证明等式:
a
(
log
b
c
)
=
c
(
log
b
a
)
a^{(\log_bc)}=c^{(\log_ba)}
a(logbc)=c(logba)
- 2-3证明等式
l
g
(
n
!
)
=
Θ
(
n
l
g
n
)
lg(n!)=Θ(nlgn)
lg(n!)=Θ(nlgn)并证明
n
!
=
ω
(
2
n
)
且
n
!
=
o
(
n
n
)
n!=ω(2^n)且n!=o(n^n)
n!=ω(2n)且n!=o(nn)
- 2-4函数 ⌈ l g n ! ⌉ \lceil lgn! \rceil ⌈lgn!⌉多项式有界吗?函数 ⌈ l g l g n ! ⌉ \lceil lglg n! \rceil ⌈lglgn!⌉多项式有界吗?
假设 f(n) 多项式存在边界
f(n) <= cn^k
推导出
lg f(n) <= lgc + klgn
即 lgf(n)= Θ(lgn)
已知: lg(n!) = Θ(nlgn)
lg(⌈lgn⌉!) = Θ(⌈lgn⌉lg⌈lgn⌉) = Θ(lgn lg lgn) = ω(lgn).
违反假设,即⌈lgn⌉!没有边界
lg(⌈lglgn⌉!) = Θ(⌈lglgn⌉lg⌈lglgn⌉) = Θ(lglgn lg lglgn)= o((lglgn)^2) = o(lgn)
即⌈lglgn⌉!有边界
- 2-5如下两个函数中,哪一个渐近更大些:lg(lgn)还是lg*(lgn)?
- 2-6证明:黄金分割率及其共轭数都满足方程x2=x+1
- 2-7用归纳法证明:第i个斐波那契数满足等式
- 2-8证明:klnk=Θ(n)蕴涵着k=Θ(n/lnn)。
思考题
- 1.(多项式的渐进行为) 假设
p
(
n
)
=
∑
i
=
0
d
a
i
n
i
p(n)=\sum_{i=0}^d a_in^i
p(n)=∑i=0daini 是一个关于 n的 d 次多项式,其中
a
d
>
=
0
a_d>=0
ad>=0,令k为一个常数。使用渐进符号的定义来证明下面的性质。
- a. 若 k>=d ,则 p ( n ) = O ( n k ) p(n)=O(n^k) p(n)=O(nk)。
- b. 若 k<=d ,则 p ( n ) = Ω ( n k ) p(n)=Ω(n^k) p(n)=Ω(nk) 。
- c. 若 k =d ,则 p ( n ) = Θ ( n k ) p(n)=Θ(n^k) p(n)=Θ(nk) 。
- d. 若 k>d ,则 p ( n ) = o ( n k ) p(n)=o(n^k) p(n)=o(nk) 。
- e. 若 k<d ,则 p ( n ) = ω ( n k ) p(n)=ω(n^k) p(n)=ω(nk) 。
a情况:
p(n) = a_1*n^1 + ... + a_d*n^i <= c*n^d<=c*n^k
即 p(n)=O(n^k)
同理可得其它情况。
- 2.(相对渐进增长) 为下表中的每对表达式 (A, B) 指出 A 是否是 B 的 O、o、Ω,ω或Θ。假设 k 、
ϵ
>
0
\epsilon > 0
ϵ>0 且 c > 1 均为常量。回答应以表格的形式,将“是”或“否”写在每个空格中。
- 3a. 根据增长的阶来排序下面的函数,即求出满足 g 1 = Ω ( g 2 ) , g 2 = Ω ( g 3 ) , . . . , g 29 = Ω ( g 30 ) g_1 = \Omega(g_2), g_2 = \Omega(g_3), ..., g_{29} = \Omega(g_{30}) g1=Ω(g2),g2=Ω(g3),...,g29=Ω(g30)的函数的一种排列 g 1 , g 2 , . . . , g 30 g_1, g_2, ..., g_{30} g1,g2,...,g30。把你的表划分成等价类,使得函数 f(n) 和 g(n) 在相同类中当且仅当 f ( n ) = Θ ( g ( n ) ) f(n) = \Theta(g(n)) f(n)=Θ(g(n))。
- 3b.给出非负函数 f(n) 的一个例子,使得对所有在(3a)部分中的函数 g i ( n ) g_i(n) gi(n),f(n) 既不是 O ( g i ( n ) ) O(g_i(n)) O(gi(n)) 也不是 Ω ( g i ( n ) ) \Omega(g_i(n)) Ω(gi(n))。
( 2 2 2 n + 1 ) s i n x (2^{2^{2n+1}})^{sinx} (222n+1)sinx
- 4(渐进记号的性质) 假设 f(n) 和 g(n) 为渐进正函数。证明或反驳下面的每个猜测。
- f(n)=O(g(n)) implies g(n)=O(f(n))
- f(n)+g(n)=Θ(min(f(n),g(n)))
- f(n)=O(g(n)) implies lg(f(n))=O(lg(g(n))), 其中对所有足够大的 n,有 lg ( g ( n ) ) ≥ 1 且 f ( n ) ≥ 1 \lg(g(n)) \geq 1 且 f(n) \geq 1 lg(g(n))≥1且f(n)≥1。
- f(n)=O(g(n)) implies 2 f ( n ) = O ( 2 g ( n ) ) 2^{f(n)}=O(2^{g(n)}) 2f(n)=O(2g(n))
- f ( n ) = O ( ( f ( n ) ) 2 ) f(n)=O((f(n))^2) f(n)=O((f(n))2)
- f(n)=O(g(n)) implies g(n)=Ω(f(n))
- f(n)=Θ(f(n/2))
- f(n)+o(f(n))=Θ(f(n))
- 错。例如:n = O(n^2)
- 错。例如: n + n 2 = Θ ( n 2 ) ≠ Θ ( n , n 2 ) = Θ ( n ) n + n^2 = \Theta(n^2) \neq \Theta(n, n^2) = \Theta(n) n+n2=Θ(n2)=Θ(n,n2)=Θ(n)
- 正确.
- 错。例如: 2 n = O ( n ) , 2 2 n = 4 n ≠ O ( 2 n ) 2n = O(n), 2^{2n} = 4^n \neq O(2^n) 2n=O(n),22n=4n=O(2n)
- 当f(n)≥1时, f ( n ) = O ( ( f ( n ) ) 2 ) f(n)=O((f(n))^2) f(n)=O((f(n))2); 当f(n)<1时,不成立
- 正确
- 错。例如: n ! ≠ Θ ( ( n 2 ) ! ) n! \neq \Theta((\frac{n}{2})!) n!=Θ((2n)!)。
- 正确
- 5 略
- 6(多重函数) 我们可以把用于函数
lg
∗
\lg^*
lg∗中的多重操作符 * 应用于实数集上的任意单调递增函数f(n) 。对给定的常量
c
∈
R
c \in R
c∈R,我们定义多重函数
f
c
∗
f^*_c
fc∗为
f
c
∗
(
n
)
=
m
i
n
{
i
≥
0
:
f
(
i
)
(
n
)
≤
c
}
f^∗_c(n)=min\{i≥0:f^{(i)}(n)≤c\}
fc∗(n)=min{i≥0:f(i)(n)≤c},该函数不必在所有情况下都是良定义的。换句话说,值
f
c
∗
(
n
)
f^∗_c(n)
fc∗(n) 是为缩小其参数到 c 或更小所需函数f 重复应用的数目。 对如下每个函数 f(n) 和常量 c,给出
f
c
∗
(
n
)
f^*_c(n)
fc∗(n) 的一个尽量紧确的界。
主要参考
《《算法导论》[第3章] 函数的增长-[3.1] 渐进记号》
《算法导论第三版 第3章习题答案》
《自然常数e到底是个什么东西》
《斐波那契数,计算与分析》
《算法导论第三版 第3章习题答案》
《《算法导论》第三章-思考题(参考答案)》
《《算法导论》第三章-思考题(参考答案)》