CMU 15-751 课程第二课笔记。
CS Theory Toolkit at CMU - YouTube
照抄参考了 Lecture Note。
渐进标记(Asymptotic Notation)
我们知道
i=1∑ni=2n(n+1)=21n2+21n
在 n 很大的时候,平方项这个函数值的影响会更显著。我们可以把这一个特性写作
i=1∑ni=O(n2)
一般我们说 f(x)=O(g(x)),表示当 x 足够大的时候,存在常数 C>0,使 0≤f(x)≤Cg(x)。
更加形式的定义是,∃C,x0>0,使得 ∣f(x)∣≤Cg(x),∀x≥x0。一般来说,我们研究的函数往往是正数,因此常常写为 0≤f(x)≤Cg(x)。
上面所说的是 x→∞ 时的大 O 符号的含义。有的时候,这个符号也可以用在 x→0+ 时。那么类似的定义就是 ∃C>0,x0,使得 ∣f(x)∣≤Cg(x),∀0≤x≤x0。
一般,被我们叫做 n 的变量是趋向于 ∞ 的,叫做 ε 的变量是趋向于 0+ 的,因此我们常常省略自变量的趋向。
很多时候 O(g(x)) 可以表示一个满足 ∣f(x)∣≤Cg(x),∀x≥x0(也就是上面的定义)的一个匿名函数。这样的写法并不标准,但是经常被使用,例如之前的函数可以计作下面的形式:
i=1∑ni=21n2+O(n)=21n2(1+O(n1))
上面第二行的写法非常直观地体现了函数值的近似答案,以及一个趋近于 1 的乘法误差。
我们还有一些类似的符号来表示一些不同的函数渐进性:
- f(x)=Ω(g(x)) 代表 ∃C>0,x0,使得 f(x)≥Cg(x),∀x≥x0。
- f(x)=Θ(g(x)) 代表 f(x)=O(g(x)) 且 f(x)=Ω(g(x)),即 ∃C1,C2>0,x0,使得 C1g(x)≤f(x)≤C2g(x)。
- f(x)=o(g(x)) 表示 g(x)f(x)→0。
- f(x)=ω(g(x)) 表示 g(x)f(x)→∞。
- f(x)≤poly(g(x)) 代表 f(x)=g(x)O(1),即 f(x) 被限制在 g(x) 的常数次幂下。
- f(x)=O~(g(x)) 代表 f(x)≤g(x)⋅poly(logg(x))。这样的限制成为 lazy bound。例如,n2log3n=O~(n2),n5⋅3n=O~(3n)。需要注意的是,n5⋅3n 不能计作 O~(2n)。
- 如果 x→0+,那么其含义与上面不同,此时 f(x)=g(x)⋅poly(log1/g(x))。
- f(x)=Ω~(g(x)) 代表 f(x)≥poly(g(x))g(x)。例如,log2nn3=Ω~(n3)。
- f(x)∼g(x) 代表 g(x)f(x)→1。这种形式可以等价地写成 f(x)=g(x)(1±o(1)),在证明中常常使用这样的形式。比如 ∑i=1ni=Θ(n2),我们可以将之写成更明确的 ∑i=1ni∼21n2。
显然,我们使用这些符号来表示一个函数渐进的界限,是为了函数值的变化更容易分析。因此,g(n) 这样的函数就不能太复杂。对于这样的函数的取用,我们有一些约定俗成的规则,如果它应该是下面几种情形之一,或者它们的乘积,我们就称之为标准形式(不是正式术语):
- 常数(如 3,2π);
- logn 的常数次幂(如 logn,logn,logn1),一般来说我们用 ln 表示 loge,lg 表示 log2,如果只有 log 则代表我们不关心其底数;
- n 的常数次幂(如 n,n5.3);
- 指数函数(如 2n,e−n,2n/2);
- ncn,其中 C 为常数。
比如,g(n)=n5⋅3n、g(n)=6n2logn 和 g(n)=2πn(en)n 都是这里所说的标准形式,后者就是我们一会儿就会提到的斯特林公式中 n! 的渐进。
以上五种形式的函数,每一种都满足在渐进性上小于下一种函数,即使对其做上任意正数次幂。比如 (lnn)100=o(n1/10),100n50=o(1.1n)。
这些并不是我们可能会用到的渐进函数的全部集合,比如 O(loglogn) 也是我们常用到的,但是我们在处理一个复杂的函数时,可以首先尝试这些标准形式的乘积。
调和数(The Harmonic Number)
调和数 Hn=1+21+31+⋯+n1。
首先让我们用一些并不精确的方法估计一下这个函数的渐进性。
我们将每一个 i1 缩放到其最接近的两个 2 的整次幂,便可以得到
Hn≤1+21+21+41+41+41+41+81+…≤⌊log2n⌋+1
Hn≥1+21+41+41+81+81+81+81+161+…≤21⌈log2n⌉+1
所以我们便可以得到 Hn=Θ(logn)。一般来说,我们得到这个结果就可以了,但是如果我们想要得到更精确的结果,便可以使用积分来近似。如下图,其中曲线为 y=x1,(b) 中的阴影部分是 y=x1 和 x 轴所夹部分在 [1,n] 上的面积,即 ∫1nx1dx。图 (a) 和图 © 分别是调和数的两种近似方法。

由此我们可以得到两个结论:
Hn≤1+∫1nx1dx=1+lnx∣∣∣1n=1+lnn
Hn≥∫1n+1x1dx=lnx∣∣∣1n+1=ln(n+1)
于是我们可以得到 lnn≤ln(n+1)≤Hn≤lnn+1。
到这里,我们已经可以确定 Hn∼lnn。如果我们想要比较用 lnn 代替 ln(n+1) 和 1+lnn 时的误差,可以将之写成 lnn(1±o(1)) 的形式:
1+lnnln(n+1)=lnn(1+lnn1)=ln(n(1+n1))=lnn+ln(1+n1)=lnn+n1±O(n21)=lnn(1+nlnn1±O(n2lnn1))
其中 ln(n+1) 的第二步来源于 ln(1+x) 的泰勒级数:
ln(1+x)=x−2x2+3x3−4x4+…(−1<x≤1)
于是我们可以得到 ln(1+x)=x±O(x2)∼x(x→0)。所以对于 ln(1+n1),n1→0+,我们也有 ln(1+n1)=n1±O(n21)。
事实上,如果我们用 lnn 来代替 Hn,这个误差大约会趋近于 21 左右的值。这个值我们一般写作 γ≈0.577,被称为欧拉常数,Hn=lnn+γ−O(n1).
渐进技巧(Asymptotic Tricks)
泰勒级数(Taylor Series)
除了上面的对很小的 x 有 ln(1+x)≈x,还有很多类似的结论,比如对于很小的 x 有 ex≈1+x。
事实上,在泰勒级数中,ex=1+x+2x2+3x3+4x4+…(∀x∈R)。因而我们可以说 ex=1+x+O(x2)(x→0)。实际上,在 −1≤x≤1 时,这个 O(n2) 的误差项被严格包含在 [0,x2] 的区间内。
除此之外,还有更多常用的泰勒级数结论:
1−ε1=1+ε+ε2+ε3+…=1+ε±O(ε2)
实际上,这也可以由 1−ε1≈e−ε1=eε 印证。
1+ε=1+2ε−8ε2+16ε3−…=1+2ε±O(ε2)
同样,这也可以由 (1+ε)1/2≈(eε)1/2=eε/2≈1+2ε 印证。
一些例题
n+1−n 的渐进?
n+1n+1−n=n(1+n1)=n1+n1=n(1+2n1±O(n21))=n(1+2n1±O(n21)−1)=2n1±O(n1.5)=2n1(1±O(n1))∼2n1
log221−ε1?
log221−ε1=log21−2ε2=log22−log2(1−2ε)=1−ln2ln(1−2ε)=1−ln21(−2ε±O(ε2))=1+ln22ε±O(ε2)
反函数
假设我们有 y=xlnx,x≥1。这是一个单调递增的函数,所以它一定有反函数。求 x=f(y) 的渐进?
根据定义 y=Θ~(x),即除了一些很小的因式,y 基本上是和 x 呈线性关系的。因此大概会有 lnx≈lny。实际上也就是,
lny=lnx+lnlnx∼lnx(x→∞,y→∞)
那么 lnx 和 lny 是渐进相等的,我们就可以做一些替换:
y=xlnx∼xlny⇒x∼lnyy
t2logt=n3,求 t 的渐进性。
⇒⇒⇒⇒2logt+loglogt=3lognlogn=32logt+31loglogt∼32logtlogt=Θ(logn)t2Θ(logn)=n3t=Θ(lognn3)=Θ(lognn3/2)
含参最小化
如果一个算法的运行时间是 O(tn3)+O(tlogt),t 可以取任意值。那么应该选择怎样的 t 使运行时间最低呢?
我们知道,max(a,b)≤a+b≤2max(a,b)。因此,如果我们不太在意常数因子,我们可以认为 a+b≈max(a,b)。
于是,如果我们想要最小化原式两个部分的和,我们的任务等价于要同时让这两个部分都变小。

考虑到,两个子式随着 t 的增加,一个单调增,一个单调减(大概如上图所示),我们很容易发现当两个部分相等的时候,它们的最大值才是最小的。
因此我们只需要让 tn3=tlogt。这是我们刚刚才解决过的问题,其答案是 t=Θ(lognn3/2)。
则 logt=Θ(logn)。代入原式,得到总的时间为 O(n3/2logn)。
实际上,上面的过程可不能并不严谨,但是对于这样的问题我们一般并不需要形式化地证明,只需要会求出这样的 t 值即可。
生日悖论(Birthday Paradox)
生日悖论是指在不少于 23 个人中至少有两人生日相同的概率大于 50%,这听上去与一般直觉相抵触而已,所以常常被戏称为“悖论”。生日悖论的衍生版本经常在 TCS 中出现。
我们换一种方式理解这个问题:将 n 个球均匀随机地扔进 m(=365) 个桶中,设没有发生冲突,即所有的球都在不同的桶中的概率为 Pn,m(这里假设 n≤m,因为 n>m 时一定会发生冲突)。我们很容易写出 Pn,m 的计算式:
Pn,m=1⋅(1−m1)(1−m2)⋯(1−mn−1)
对这样的很多项的乘积做渐进分析,我们常常会在两边取 ln 来解决。但是这样我们保留乘积,使用 ex≈1+x 来替换每一项。
通过之前泰勒级数或者别的方法,我们很容易知道 1−x≤e−x,∀x>0。所以我们得到
Pn,m≤exp(0)exp(−m1)exp(−m2)⋯exp(−mn−1)=exp(−m1(1+2+⋯+(n−1)))=exp(−2mn(n−1))
这样,我们就得到了 Pn,m 的上限,通过这个上界我们已经可以估算和验证生日问题的结论了。当然,我们同样可以求出它的下限。
类似于 1−ε≤e−ε 的上界,我们也有它的下界公式:∃C,1−ε≥exp(−ε−Cε2),∀0≤ε<1。这个公式可以通过对于 ln(1+x) 的泰勒级数来说明。(可恶,我并不会证明这个结论,而且我觉得这个结论的 ∃C 和 ∀ε 可能要调换一下顺序才成立……不过这不影响这个式子能够正确地解决现在的问题)
Pn,m≥exp(−m1−Cm21)exp(−m2−Cm222)⋯exp(−mn−1−Cm2(n−1)2)=exp(−2mn(n−1))exp(−m2C(12+22+⋯+(n−1)2))=exp(−2mn(n−1))exp(−O(m2n3))=exp(−2mn(n−1))(1−O(m2n3))
这里分子上的 (n−1) 处理起来很不方便,我们希望能将分子化成 n2 的形式:
exp(−2mn(n−1))=exp(−2mn2+2mn)=exp(−2mn2)exp(2mn)=exp(−2mn2)(1+O(mn))
于是我们得到
Pn,m=exp(−2mn2)(1+O(mn))(1−O(m2n3))
当 n 远小于 m 的时候,后面两项带来的误差都很小。但我们仍然想知道哪一项才是主导的误差项。事实上,当 n 很小的时候是 (1+O(mn)),n 稍大一些时是 (1−O(m2n3))。其分界点是 mn=m2n3 即 n=m 时,即
Pn,m=exp(−2mn2)⋅⎩⎪⎪⎪⎨⎪⎪⎪⎧1±O(mn)1±O(m2n3)n≤mn≥m
生日问题关心的是什么时候碰撞的概率会超过 0.5。因此我们只需要令 Pn,m≈exp(−2mn2)=21 即可,则 n=2ln2m=Θ(m)。
此时的 n≥m,所以我们可以说 n=2ln2m±O(1) 时,Pn,m=21±O(m1)。