文章目录
计算问题(computational problem)是对于输入输出的一组规定。例如 sorting 问题
Input: Array a[1...n]
Output: Permutation a'[1...n] s.t. a'[i] <= a'[j] for all 1 <= i < j <= n
对于一个问题的合法输入称为问题实例(problem instance)。如果一个计算过程(computational procedure)可以把任意的问题实例映射到期望输出,就称之为一个正确的算法(algorithom)。
算法分析
算法分析可以帮助我们比较不同的算法。
正确性(Correctness)
一个正确的算法,应该对所有合法输入,在有限时间内输出正确结果。
An algorithm is correct if when given a valid input it computes for a finite amount of time and produces the right answer.
所以在分析算法正确性之前,应该明确指出,对于每个输入,什么样的输出才是正确的。对于循环,通常使用数学归纳法证明。
复杂度
时间复杂度(Time Complexity)通常可以分为三类
- worst-case (usually) maximum time
- average-case (sometimes) expected time, with assumption of statistical distribution of inputs
- best-case (bogus) cheat with a slow algorithm that works fast on some input.
空间复杂度(Space Complexity)通常包括以下部分
- 指令空间
- 数据空间(不包括输入数据)
- 栈空间
简洁性(Simplicity)
简洁性在算法分析、代码实现、纠错过程中非常关键。
最优性(Optimality)
如果没有一个算法比当前算法的时间复杂度更低,则当前算法具有最优性。通常,我们可以构造一个时间复杂度的下界,然后证明当前算法达到了下界,从而证明最优性。
伪代码(Pseudo Code)
在 LaTex 中插入伪代码时需要使用宏包
\usepackage[ruled, linesnumbered]{algorithm2e}
宏包中最常用的环境是
\begin{algorithm}
\caption{algorithm name}
\KwIn{input values}
\KwOut{output values}
procedures\;
\end{algorithm}

这一环境属于浮动体,因此可以像 figure 环境一样使用 label 等命令。环境内部预定义了一些命令
\If(then comment){condition}{then clause\;}
\BlankLine
\eIf(then comment){condition}{then clause\;}(else comment){else clause\;}
\BlankLine
\For(for comment){condition}{For-loop body\;}
\BlankLine
\ForEach(foreach comment){condition}{ForEach-loop body\;}
\BlankLine
\ForAll(forall comment){condition}{ForAll-loop body\;}
\BlankLine
\While(while comment){condition}{While-loop body\;}
\BlankLine
\Repeat(repeat comment){condition}{Repeat-loop body\;}(until comment)
\BlankLine
\Return{return values\;}

注释命令可以缺省
\tcc{c style comment}
\If(\tcc*[f]{in block comment}){condition}{then clause\;}
\tcp{cpp style comment}
\If(\tcp*[h]{in block comment}){condition}{then clause\;}

如果需要实现更为复杂的分支结构,可以使用 if-elseif-else 结构
\uIf(\tcc*[f]{if comment}){condition}{
if clause\;
} \uElseIf(\tcc*[f]{else if comment}){condition}{
else if clause\;
} \Else(\tcc*[f]{else comment}){
else clause\;
}

或 switch-case 语句
\Switch(\tcc*[f]{switch comment}){condition}{
\uCase(\tcc*[f]{case comment}){case description}{case block\;}
\Other(\tcc*[f]{other comment}){otherwise block\;}
}

要定义、声明并调用一个函数
\begin{function}
\caption{function name (argument1, argument2)}
\KwData{input arguments}
\KwResult{return values}
function body\;
\end{function}
\begin{algorithm}
\caption{algorithm name}
\KwIn{input values}
\KwOut{output values}
\SetKwFunction{func}{fn}
\func{arg1, arg2}\;
\end{algorithm}

可以针对某一行进行 label 和 ref 操作
procedures\; \label{algorithm:linelabel:procedures}
goto line \ref{algorithm:linelabel:procedures}\;

分治(Divide and Conquer)
分治一般有以下步骤
- 划分(divide the problem into subproblems)
- 解决(conquer recursively solve the subproblems)
- 合并(combine subproblem solutions)
假设我们把原问题分为 a a a 个子问题,每个子问题的输入大小为 n / b n/b n/b ,划分和合并过程的时间复杂度为 f ( n ) f(n) f(n) ,那么分治算法的时间复杂度可以表示为
T ( n ) = a T ( n / b ) + f ( n ) T(n) = aT(n/b) + f(n) T(n)=aT(n/b)+f(n)
递推式(Recurrences)
递推式描述了一种函数,他的值依赖于更小输入尺度下的函数值。
A recurrence is an equality or inequality equation, which dipict a function with function values on smaller input scales.
例如,归并排序的递推式为
T ( n ) = { Θ ( 1 ) , n = 1 2 T ( n / 2 ) + Θ ( n ) , n > 1 T(n) = \begin{cases} \Theta(1), & n = 1\\ 2T(n/2) + \Theta(n), & n > 1 \end{cases} T(n)={Θ(1),2T(n/2)+Θ(n),n=1n>1
当 n > 1 n>1 n>1 时,我们需要递推地解决子问题,直到 n = 1 n=1 n=1
- recursive case the n > 1 n>1 n>1 case
- base case the n = 0 n=0 n=0 case
递归树(Recursion Tree)
递归树可以用于求解递推式,但这种方法得出的结果往往并不可靠。对于上述例子

可以求出
T
(
n
)
=
c
n
log
n
+
Θ
(
n
)
=
O
(
n
log
n
)
T(n) = cn\log n + \Theta(n) = O(n\log n)
T(n)=cnlogn+Θ(n)=O(nlogn)
数学归纳法(Induction)
如果我们可以猜出解的形式,就可以使用数学归纳法证明。假设 T ( k ) ≤ c k log k , ∀ k ∈ ( 0 , n ) T(k) \le ck\log k, \forall k\in(0,n) T(k)≤cklogk,∀k∈(0,n) 对某个 c > 0 c>0 c>0 成立,那么只要 c ≥ 1 c \ge 1 c≥1 ,就会有
T ( n ) ≤ 2 c n 2 log n 2 + n = c n log n − ( c − 1 ) n ≤ c n log n \begin{array}{rcl} T(n) &\le& 2c\frac{n}{2}\log\frac{n}{2} + n\\ &=& cn\log n - (c - 1)n\\ &\le& cn\log n \end{array} T(n)≤=≤2c2nlog2n+ncnlogn−(c−1)ncnlogn
值得注意的是,我们需要导出与归纳假设(induction hypothesis)完全相同的结论,而不是简单的
c n log n − ( c − 1 ) n = O ( n log n ) cn\log n - (c-1) n = O(n\log n) cnlogn−(c−1)n=O(nlogn)
我们还需要验证基本情况(base cases)。在这个具体问题中,我们无法证明
T ( 1 ) = 1 ≤ c log 1 = 0 T(1) = 1 \le c\log1 = 0 T(1)=1≤clog1=0
但是我们可以证明,对于 ∀ c ≥ 2 \forall c\ge 2 ∀c≥2 ,有
T ( 2 ) = 2 T ( 1 ) + 2 = 4 ≤ 2 c log 2 T ( 3 ) = 2 T ( 1 ) + 3 = 5 ≤ 3 c log 3 T(2) = 2T(1) + 2 = 4 \le 2c\log2\\ T(3) = 2T(1) + 3 = 5 \le 3c\log3 T(2)=2T(1)+2=4≤2clog2T(3)=2T(1)+3=5≤3clog3
因此,我们证明了
∀ c ≥ 2 , n ≥ 2 , T ( n ) ≤ c n log n \forall c \ge 2,\ n \ge 2,\ T(n) \le cn\log n ∀c≥2, n≥2, T(n)≤cnlogn
等价于
T ( n ) = O ( n log n ) T(n) = O(n\log n) T(n)=O(nlogn)
强化归纳假设(Strengthen I.H.)
考虑递推式
T ( n ) = 2 T ( n / 2 ) + 1 T(n) = 2T(n/2) + 1 T(n)=2T(n/2)+1
我们猜测解的形式是 T ( n ) = O ( n ) T(n) = O(n) T(n)=O(n) 。如果归纳假设是 T ( k ) ≤ c k T(k) \le ck T(k)≤ck ,会得到
T ( n ) = 2 c n 2 + 1 ≰ c n T(n) = 2c\frac{n}{2} + 1 \not\le cn T(n)=2c2n+1≤cn
但是如果我们减去一个低阶项 T ( k ) ≤ c k − d T(k) \le ck - d T(k)≤ck−d ,那么只要 d ≥ 1 d\ge 1 d≥1 ,就有
T ( n ) = 2 c n 2 − 2 d + 1 ≤ c n − d T(n) = 2c\frac{n}{2}-2d+1\le cn-d T(n)=2c2n−2d+1≤cn−d
变量替换(Variable Substitution)
考虑递推式
T ( n ) = 2 T ( n ) + log n T(n) = 2T(\sqrt{n}) + \log n T(n)=2T(n)+logn
使用 m = log n m = \log n m=logn 和 S ( m ) = T ( 2 m ) S(m) = T(2^m) S(m)=T(2m) 进行替换可以得到
T ( 2 m ) = 2 T ( 2 m / 2 ) + m S ( m ) = 2 S ( m / 2 ) + m \begin{array}{rcl} T(2^m) &=& 2T(2^{m/2}) + m\\ S(m) &=& 2S(m/2) + m \end{array} T(2m)S(m)==2T(2m/2)+m2S(m/2)+m
因此
S ( m ) = O ( m log m ) T ( n ) = O ( log n log ( log n ) ) \begin{array}{rcl} S(m) &=& O(m\log m)\\ T(n) &=& O(\log n\log(\log n)) \end{array} S(m)T(n)==O(mlogm)O(lognlog(logn))
主方法(Master Method)
主方法对以下形式的递推式有效
T ( n ) = a T ( n / b ) + f ( n ) T(n) = aT(n/b) + f(n) T(n)=aT(n/b)+f(n)
其中 a ≥ 1 a \ge 1 a≥1 , b > 1 b > 1 b>1 ,而且 f f f 是渐进正函数(asymptotically positive)
T ( n ) = { Θ ( n log b a ) , ∃ ϵ > 0 s.t. f ( n ) = O ( n log b a − ϵ ) Θ ( n log b a log k + 1 n ) , ∃ k ≥ 0 s.t. f ( n ) = Θ ( n log b a log k n ) Θ ( f ( n ) ) , ∃ ϵ > 0 s.t. f ( n ) = Ω ( n log b a + ϵ ) T(n) = \begin{cases} \Theta(n^{\log_ba}), & \exist \epsilon > 0\text{ s.t. }f(n) = O(n^{\log_ba-\epsilon})\\ \Theta(n^{\log_ba}\log^{k+1}n), & \exist k \ge 0\text{ s.t. }f(n) = \Theta(n^{\log_ba}\log^kn)\\ \Theta(f(n)), & \exist \epsilon > 0\text{ s.t. }f(n) = \Omega(n^{\log_ba+\epsilon}) \end{cases} T(n)=⎩⎪⎨⎪⎧Θ(nlogba),Θ(nlogbalogk+1n),Θ(f(n)),∃ϵ>0 s.t. f(n)=O(nlogba−ϵ)∃k≥0 s.t. f(n)=Θ(nlogbalogkn)∃ϵ>0 s.t. f(n)=Ω(nlogba+ϵ)
需要注意的是,在使用第三种情况时 f ( n ) f(n) f(n) 需要满足
∃ c < 1 , ∃ N s.t. ∀ n > N , a f ( n / b ) ≤ c f ( n ) \exist c<1, \exist N\text{ s.t. }\forall n>N,\ af(n/b) \le cf(n) ∃c<1,∃N s.t. ∀n>N, af(n/b)≤cf(n)
以上题为例
∵ a = b = 2 f ( n ) = n ∴ T ( n ) = O ( n log n ) \begin{array}{rl} \because & a = b = 2\\\\ & f(n) = n\\\\ \therefore & T(n) = O(n\log n) \end{array} ∵∴a=b=2f(n)=nT(n)=O(nlogn)
二叉搜索(Binary Search)
To find an element in a sorted array
- divide check middle element
- conquer recursively search 1 1 1 subarray
- combine trivial
T ( n ) = T ( n / 2 ) + Θ ( 1 ) = Θ ( log n ) \begin{array}{rcl} T(n) &=& T(n/2) + \Theta(1)\\ &=& \Theta(\log n) \end{array} T(n)==T(n/2)+Θ(1)Θ(logn)
矩阵乘法(Matrix Multiplication)
Based on the definition, we have an algorithm
T
(
n
)
=
Θ
(
n
3
)
T(n) = \Theta(n^3)
T(n)=Θ(n3)
Adopting the D&C idea, we think of dividing a
n
×
n
n\times n
n×n matrix into
2
×
2
2\times 2
2×2 matrix of
(
n
/
2
)
×
(
n
/
2
)
(n/2) \times (n/2)
(n/2)×(n/2) sub matrices
[
r
s
t
u
]
=
[
a
b
c
d
]
⋅
[
e
f
g
h
]
C
=
A
⋅
B
\begin{array}{rcl} \left[\begin{array}{cc} r & s\\ t & u \end{array}\right] &=& \left[\begin{array}{cc} a & b\\ c & d \end{array}\right] \cdot \left[\begin{array}{cc} e & f\\ g & h \end{array}\right]\\ C &=& A \cdot B \end{array}
[rtsu]C==[acbd]⋅[egfh]A⋅B
which takes
8
8
8 multiplies and
4
4
4 additions of
(
n
/
2
)
×
(
n
/
2
)
(n/2) \times (n/2)
(n/2)×(n/2) sub matrices
T
(
n
)
=
8
T
(
n
/
2
)
+
Θ
(
n
2
)
=
Θ
(
n
3
)
\begin{array}{rcl} T(n) &=& 8T(n/2) + \Theta(n^2)\\ &=& \Theta(n^3) \end{array}
T(n)==8T(n/2)+Θ(n2)Θ(n3)
and is no better than the ordinary algorithm. Strassen modified the algorithm so that it can be done in
7
7
7 multiplies and
18
18
18 additions/subtractions
T
(
n
)
=
7
T
(
n
/
2
)
+
Θ
(
n
2
)
=
Θ
(
n
log
7
)
\begin{array}{rcl} T(n) &=& 7T(n/2) + \Theta(n^2)\\ &=& \Theta(n^{\log 7}) \end{array}
T(n)==7T(n/2)+Θ(n2)Θ(nlog7)
where log 7 ≈ 2.81 \log 7 \approx 2.81 log7≈2.81.
The best to date algorithm is T ( n ) ≈ Θ ( n 2.376 ) T(n) \approx \Theta(n^{2.376}) T(n)≈Θ(n2.376) which is of theoretical interest only.
例
附录
渐进分析(Asymptotic Analysis)
在分析时间复杂度时,我们往往只关心在 n → ∞ n \rightarrow\infin n→∞ 时 T ( n ) T(n) T(n) 的增长趋势。引入上界 O O O (upper bounds)、下界 Ω \Omega Ω (lower bounds)和紧界 Θ \Theta Θ (tight bounds)的数学定义
O ( g ( n ) ) = { f ∣ ∃ c > 0 , lim n → ∞ f ( n ) / g ( n ) ≤ c } Ω ( g ( n ) ) = { f ∣ ∃ c > 0 , lim n → ∞ f ( n ) / g ( n ) ≥ c } Θ ( g ( n ) ) = O ( g ( n ) ) ∩ Ω ( g ( n ) ) \begin{array}{rcl} O(g(n)) &=& \{f \vert \exist c>0,\lim_{n\rightarrow\infty} f(n)/g(n) \le c\}\\ \Omega(g(n)) &=& \{f \vert \exist c>0,\lim_{n\rightarrow\infty} f(n)/g(n) \ge c\}\\ \Theta(g(n)) &=& O(g(n)) \cap \Omega(g(n)) \end{array} O(g(n))Ω(g(n))Θ(g(n))==={f∣∃c>0,limn→∞f(n)/g(n)≤c}{f∣∃c>0,limn→∞f(n)/g(n)≥c}O(g(n))∩Ω(g(n))
类似可以定义严格上界和严格下界
o ( g ( n ) ) = O ( g ( n ) ) ∖ Θ ( g ( n ) ) ω ( g ( n ) ) = Ω ( g ( n ) ) ∖ Θ ( g ( n ) ) \begin{array}{rcl} o(g(n)) &=& O(g(n))\setminus \Theta(g(n))\\ \omega(g(n)) &=& \Omega(g(n))\setminus \Theta(g(n))\\ \end{array} o(g(n))ω(g(n))==O(g(n))∖Θ(g(n))Ω(g(n))∖Θ(g(n))
为了方便,公式中使用集合来表示其中的任何一个元素
A set in a formula represents an anonymous function in the set
举例来说, n 2 + O ( n ) = O ( n 2 ) n^2 + O(n) = O(n^2) n2+O(n)=O(n2) 表示
∀ f ∈ O ( n ) , ∃ h ∈ O ( n 2 ) , s.t. n 2 + f ( n ) = h ( n ) \forall f\in O(n),\ \exist h\in O(n^2),\text{ s.t. }n^2+f(n) = h(n) ∀f∈O(n), ∃h∈O(n2), s.t. n2+f(n)=h(n)
运算律
O ( c f ( n ) ) = O ( f ( n ) ) O ( f ( n ) ) + O ( g ( n ) ) = O ( max { f ( n ) , g ( n ) } ) O ( f ( n ) ) + O ( g ( n ) ) = O ( f ( n ) + g ( n ) ) O ( f ( n ) ) ⋅ O ( g ( n ) ) = O ( f ( n ) ⋅ g ( n ) ) \begin{array}{rcl} O(cf(n)) &=& O(f(n))\\ O(f(n))+O(g(n)) &=& O(\max\{f(n),g(n)\})\\ O(f(n))+O(g(n)) &=& O(f(n)+g(n))\\ O(f(n))\cdot O(g(n)) &=& O(f(n)\cdot g(n)) \end{array} O(cf(n))O(f(n))+O(g(n))O(f(n))+O(g(n))O(f(n))⋅O(g(n))====O(f(n))O(max{f(n),g(n)})O(f(n)+g(n))O(f(n)⋅g(n))
定理
log ( n ! ) = Θ ( n log n ) ∑ i = 1 n 1 / i = Θ ( log n ) \begin{array}{rcl} \log(n!) &=& \Theta(n\log n)\\ \sum_{i = 1}^n 1 / i &=& \Theta(\log n) \end{array} log(n!)∑i=1n1/i==Θ(nlogn)Θ(logn)
本文深入探讨算法分析的关键要素,包括正确性、复杂度、简洁性、最优性等,并详细讲解了伪代码规范、分治策略、递推式、递归树、数学归纳法等核心概念。同时,通过具体案例如二叉搜索、矩阵乘法,阐述了算法分析的实际应用。
1920

被折叠的 条评论
为什么被折叠?



