算法分析笔记

本文深入探讨算法分析的关键要素,包括正确性、复杂度、简洁性、最优性等,并详细讲解了伪代码规范、分治策略、递推式、递归树、数学归纳法等核心概念。同时,通过具体案例如二叉搜索、矩阵乘法,阐述了算法分析的实际应用。

计算问题(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}

algorithm_environment

这一环境属于浮动体,因此可以像 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\;}

built-in commands

注释命令可以缺省

\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\;}

comments

如果需要实现更为复杂的分支结构,可以使用 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\;
}

if-elseif-else

switch-case 语句

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

switch-case

要定义、声明并调用一个函数

\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}

functions

可以针对某一行进行 labelref 操作

procedures\; \label{algorithm:linelabel:procedures}
goto line \ref{algorithm:linelabel:procedures}\;

label-ref

分治(Divide and Conquer)

分治一般有以下步骤

  1. 划分divide the problem into subproblems)
  2. 解决conquer recursively solve the subproblems)
  3. 合并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)

递归树可以用于求解递推式,但这种方法得出的结果往往并不可靠。对于上述例子

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 c1 ,就会有

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(c1)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(c1)n=O(nlogn)

我们还需要验证基本情况(base cases)。在这个具体问题中,我们无法证明

T ( 1 ) = 1 ≤ c log ⁡ 1 = 0 T(1) = 1 \le c\log1 = 0 T(1)=1clog1=0

但是我们可以证明,对于 ∀ c ≥ 2 \forall c\ge 2 c2 ,有

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=42clog2T(3)=2T(1)+3=53clog3

因此,我们证明了

∀ c ≥ 2 ,   n ≥ 2 ,   T ( n ) ≤ c n log ⁡ n \forall c \ge 2,\ n \ge 2,\ T(n) \le cn\log n c2, n2, 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+1cn

但是如果我们减去一个低阶项 T ( k ) ≤ c k − d T(k) \le ck - d T(k)ckd ,那么只要 d ≥ 1 d\ge 1 d1 ,就有

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)=2c2n2d+1cnd

变量替换(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 a1 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ϵ)k0 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]AB

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 log72.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.

二分图(匈牙利,KM算法详解)
指派问题——匈牙利法

附录

渐进分析(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))==={fc>0,limnf(n)/g(n)c}{fc>0,limnf(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) fO(n), hO(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)

参考

写一手漂亮的伪代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LutingWang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值