当输入规模足够大时,使得只有运行时间的增长量级有关时,我们要研究算法的渐近效率。也就是说,我们关心当输入规模无限增加时,在极限中,算法的运行时间如何随着输入规模的变大而增加。通常,渐近地更有效的某个算法对很小的输入外的所有情况将是最好的。
本章给出几种标准方法来简化算法的渐近分析。下一节首先定义几类“渐近记号”,其中,我们已经见过一个例子是Θ记号。然后,我们给出贯穿本书使用的几种记号的约定。最后,回顾以下在算法分析中常见的若干函数的行为。
3.1 渐近记号
用来描述算法渐近运行时间的记号根据定义域为自然数集N={0,1,2,……}的函数来定义。这样的记号对描述最坏情况运行时间函数T(n)是方便的,因为该函数通常只定义在整数输入规模上。
渐近记号、函数与运行时间
我们将主要用渐近记号来描述算法的运行时间。然而,渐近记号实际应用与函数。
本书中对其使用渐近记号的函数通常刻画算法的运行时间。但是渐近记号也可以适用于刻画算法的某个方面的函数,甚至可以适用于和算法没有关系的函数。
Θ记号
在第 2 章,我们发现插入排序的最坏情况运行时间T(n)=Θ(n2)。让我们来定义这个记号意指什么。对一个给定的函数g(n),用Θ(g(n))来表示以下函数的集合:
Θ(g(n)) = { f(n):存在常量c1、c2和n0,使得对所有n ≥ n0,有 0 ≤ c1g(n) ≤ f(n) ≤ c2g(n) }
若存在常量c1和c2,使得对于足够大的n,函数f(n)能“夹入”c1g(n) 与 c2g(n)之间,则f(n) 属于集合Θ(g(n))。因为Θ(g(n))是一个集合,所以可以记为“f(n) ∈ Θ(g(n))”,以指出f(n)是Θ(g(n))的成员。作为替代,我们通常记为“f(n) = Θ(g(n))”以表达相同的概念。
换句话说,对所有 n ≥ n0,函数f(n)在一个常量因子内等于g(n)。我们称g(n) 是 f(n) 的一个渐近紧确界。
Θ(g(n))的定义要求每个成员f(n) ∈ Θ(g(n))均渐近非负,即当 n 足够大时,f(n)非负。因此,函数g(n)本身必为渐近非负,否者集合Θ(g(n))为空。所有我们假设用在 Θ 记号的每个函数均渐近非负。这个假设对本章定义的其他渐近号也成立。
一般来说,对任意多项式p(n) = ∑di=0aini,其中ai为常量且 ad > 0 ,我们有p(n) = Θ(nd)。
因为任意常量是一个 0 阶多项式所以可以把任意常量函数表示成Θ(n2)或Θ(1)。然而,后一种记号是一种轻微的活用,因为该表达式并为指出什么变量趋于无穷。我们将经常是使用记号Θ(1)来意指一个常量或者关于某个变量的一个常量函数。
Ο记号
Θ记号渐近地给出了一个函数的上界和下界。当只有一个渐近上界时,使用O记号。对于给定的函数g(n) ,用O(g(n))来表示以下函数的集合:
O(g(n)) = { f(n)存在正常量c和n0,使得对所有 n ≥n0, 有 0≤f(n)≤cg(n)}
我们记f(n)=O(g(n))以指出函数f(n)是集合O(g(n))的成员。按集合论的写法,我们有Θ(g(n)) ⊆ O(g(n))。
当我们说“运行时间为O(n2)时”,意指存在一个O(n2)的函数f(n),使得对 n 的任意值,不管选择什么特定的规模的 n 的输入,其运行时间的上界都是f(n)。这也就是说最坏情况运行时间为O(n2)。
Ω记号
Ω记号提供了渐近下界。对于给定的函数g(n),Ω(g(n))来表示以下函数的集合:
Ω(g(n))={f(n):存在正常量c 和 n0,使得对所有n≥n0,有 0≤cg(n)≤f(n)}
定理 3.1 对任意两个函数f(n)和g(n),我们有f(n)=Θ(g(n)),当且仅当f(n)=O(g(n))且f(n)=Ω(g(n)).
当称一个算法的运行时间为Ω(g(n))时,我们意指对每个n值,不管选择什么特定的规模为n的输入,只要n足够大,对那个输入的运行时间至少是g(n)的常量倍。等价地,我们在对一个算法的最好情况运行时间给出一个下界。
等式和不等式中的渐近记号
当渐近记号独立于等式的右边时,我们已经定义等号意指集合的成员关系。然而,一般来说,当渐近号出现在某个公式中时,我们将其解释为代表某个我们不关注名称的匿名函数。例如,公式2n2+3n+1=2n2+Θ(n)意指2n^2+3n+1=2n^2+f(n),其中f(n)是集合Θ(n)中的某个函数。在这里,假设f(n)=3n+1,该函数确实在Θ(n)中。
按这种方式使用渐近记号可以帮助消除一个等式中无关紧要的细节与混乱。
o记号
由O记号提供的渐近上界可能是也可能不是渐近紧确的。我们使用o记号来表示一个非渐近紧确的上界。形式化地定义o(g(n))为以下集合:
o(g(n))={f(n):对任意正常量c>0,存在常量n0>0,使得对所有n≥n0,有0≤f(n)<cg(n)}
O记号与o记号类似。主要区别是在f(n)=O(g(n))中,界0≤f(n)≤cg(n)对某个常量c>0成立,但在f(n)=o(g(n))对所有常量c>0成立。直观上,在o记号中,当n区域无穷大时,函数f(n)相对于g(n)来说变得微不足道了,即
有些学者使用这个极限作为o记号的定义;本书中的定义还限定匿名函数是渐近非负的。
ω记号
ω与Ω的关系类似与o记号与O记号的关系。我们使用ω记号来表示一个非渐近紧确的下界。定义它的一种方式是:
f(n) ∈ ω(g(n))当且仅当g(n) ∈ o(f(n))
然而,我们形式化第定义ω(g(n))为以下集合:
ω(g(n))={f(n):对任意正常量c>0,存在常量n0>0,使得对所有n≥n0,有0≤cg(n)<f(n)}
关系f(n)=ω(g(n))蕴含着:
也就是说,如果这个极限存在,那么当n趋于无穷大时,f(n)相对于g(n)来说变得任意大了。
比较各种函数
实数的许多关系性质也使用于渐近比较。下面假定f(n)和g(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)=Ω(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)=ω(g(n)) 且 g(n) = ω(h(n)) 蕴含f(n)=ω(h(n))
自反性
f(n) = Θ(f(n))
f(n) = O(f(n))
f(n) = Ω(f(n))
对成型
f(n) = Θ(g(n)),当且仅当g(n)=Θ(f(n))
转置对成性
f(n) = O(g(n))当且仅当g(n)= Ω(f(n))
f(n) = o(g(n))当且仅当g(n)= ω(f(n))
因为这些性质对渐近记号成立,所以可以在两个函数f和g的渐近比较和两个实数a与b的比较之间做一种类比。
f(n)=O(g(n)) 类似于a≤b
f(n)=Ω(g(n)) 类似于a≥b
f(n)=Θ(g(n)) 类似于a=b
f(n)=o(g(n)) 类似于a
f(n)=ω(g(n)) 类似于a>b
若f(n)=o(g(n)),则称f(n)渐近小于**g(n);若f(n)=ω(g(n)),则称f(n)渐近大于**g(n);
然而,实数的下列性质不能携带到渐近记号:
三分性:对任意两个实数a和b,下列三种情况恰有一种必须成立:a
3.2 标准记号与常用函数
本节将回顾一些标准的数学函数与记号并探索它们之间的关系
单调性
若m≤n蕴含f(m)≤f(n),则函数f(n)是单调递增的。类似地,若m≤n蕴含f(m)≥f(n)则函数f(n)是单调递减的。若m<n蕴含f(m)<f(n),则函数f(n)是严格递增的。类似地,若m<n蕴含f(m)>f(n)则函数f(n)是严格递减的。
向下取整与向上取整
对任意实数x,我们用⌊x⌋表示小于或等于x的最大整数(读作“x的向下取整”),并用⌈x⌉表示大于或等于x的最小整数(读作“x的向上取整”)。对所有实数x,
x-1<⌊x⌋≤x≤⌈x⌉< x+1
对任意实数n,⌊n/2⌋+⌈n/2⌉ = n
对任意实数x≤0和整数a,b>0,
⌈⌈x/a⌉b⌉=⌈xab⌉
⌊⌈x/a⌉b⌋=⌊xab⌋
⌈ab⌉≤⌈a+(b−1)b⌉
⌊ab⌋≥⌊a−(b−1)b⌋
向下取整函数f(x)=⌊x⌋是单调递增的,向上取整函数f(x)=⌈x⌉也是单调递增的。
模运算
对任意整数a和任意正整数n,a mod n的值就是a/n的余数:
a mod n=a-n⌊a/n⌋
若(a mod n)=(b mod n),则记a ≡b(mod n),并称模n时a等价于b。
多项式
给定一个非负整数d,n的d次多项式为具有以下形式的一个函数p(n):
其中,常量a0,a1,...,ad是多项式的系数且ad≠0。一个多项式为渐近正的当且仅当ad>0。对于一个d次渐近正的多项式p(n),有p(n)=Θ(nd).若对某个常量k,有f(n)=O(nk),则称函数f(n)是多项式有界的。
指数
对所有实数a>0、m和n,我们有以下恒等式:
a0=1
a1=a
a−1=1/a
(am)n=amn
(am)n=(an)m
aman=am+n
对所有n和a≥1,函数an关于n单调递增。方便时,我们假定00=1.
可以通过以下事实使多项式与指数的增长率互相关联。对所有使得a>1的实常量a和b,有
据此可得:nb=o(an)
因此,任意底大于 1 的指数函数比任意多项式函数增长的快。
使用e来表示自然对数函数的底2.71828…,对所有实数x,我们有
其中“!”表示本节后面定义的阶乘函数。对所有实数x,我们有不等式:
其中只有当x=0是等号成立。当|x|≤1时,我们有近似估计
当x→0时,用1+x作为eex近似是相当好的:ex=1+x+Θ(x2)
对所有x,我们有:
对数
我们将使用下面的记号:
lgn=log2n(以2为底的对数)
lnn=logen(自然对数)
lgkn=(lgn)k(取幂)
lglgn=lg(lgn)(复合)
对所有实数a>0,b>0,c>0和n,有
其中,上面的每个等式中,对数的底不为1.
若对某个常量k,f(n)=O(lgkn),则称函数f(n)是多对数有界的。
对任意常量a>0,lgbn=o(na),因此任意正的多项式函数比任意多对数函数增长的快。
阶乘
记号n!定义为对整数n≥0,有n!=1*2*3…*n
阶乘的一个弱上界是n!≤nn,因为在阶乘中,n项的每项最多为n。斯特林近似公式
给出了一个更紧确的上界和下界。
对所有n≥1,下面的等式也成立:
其中,112n+1<αn<112n
多重函数
我们使用记号f(i)(n)来表示f(n)重复i次作用于一个初值n上。形式化地,假设f(n)为实数集上的一个函数。对非负整数i,我们递归地定义
f(i)(n)={n,f(f(i−1)(n)),若i=0若i>0
例如,若f(n)=2n,则f(i)(n)=2in。
多重对数函数
我们使用lg∗n(读作“log星n”)来表示多重对数,下面给出它的定义。假设lg(i)n定义如上,其中f(n)=lgn.因为非正数的对数无定义,所以只能在lg(i−1)n>0时lg(i)n才有定义。一定要区分lg(i)n(从参数n开始,连续应用对数函数i次)与lgin(n的对数i次幂)。于是定义多重对数函数为:
多重对数函数是一个增长非常慢的函数:
斐波那契数
使用下面的递归式来定义斐波那契数:
因此,每个斐波那契数都是两个前面的数之和。
斐波那契数与黄金分割率Φ及其共轭数
特别地有:
因为|Φ^|<1,所以有
这蕴含着
这就是说第i个斐波那契数Fi等于\frac{\hat Φ^i}{\sqrt5}舍入到最近的整数。因此,斐波那契数以指数形式增长。