算法
1. 算法的基本概念
对于给定问题,算法就是用计算机求解这个问题的方法。一般来说,一个算法具有有限条指令构成,每条指令规定了计算机所要执行的有限次运算或操作。
定义:
- 定义 问题:需要回答的一般性提问,通常含有若干参数,为了清晰地描述一个问题,需要说明参数的含义和解所满足的条件。如果对问题参数给出一组赋值,就得到了一个问题的实例
- 定义 算法:有限条指令序列,这个指令序列确定了解决某个问题的运算或操作的步骤。算法 A 解决问题 P 指的是:把问题 P 的任何实例作为算法 A 的输入,A 能够在有限步停机,并输出该实例的正确解
- 定义 算法的时间复杂度:对于算法的效率的度量,一般做法是针对问题选择基本运算,用基本运算次数来表示算法的效率,运算符次数越多,效率就越低。这还和数据的规模和数据的具体内容有关,详细定义参见 TODO 复杂度分析。
定义 最坏情况下的时间复杂度 为 W(n),代表该算法求解输入规模为 n 的实例所需的最长时间。
定义 平均情况下的时间复杂度 为 A(n),代表了该算法求解输入规模为 n 的实例所需的平均时间。
2. 伪代码编写
定义 伪代码 是方便人理解和分析的代码,而非一定可执行的代码。伪代码通常用于分析问题,忽略了真实的实现细节。伪代码应该专注于算法的设计与分析,具体的实现应该由具体的语言编写。
定义符号:
- 赋值语句:= 或者 ←
- 分支语句:if⋯then⋯[elseif⋯][else⋯]
- 循环语句:
- while⋯do⋯
- for⋯to⋯do
- repeat⋯
- do⋯until⋯
- 跳转:goto
- 输出语句:return
- 注释://⋯
伪代码的语句通常有标号,这也是为了分析和表示方便。
有时,伪代码可以直接使用自然语言描述出来,伪代码不定义什么是非法语句。如果前面已经实现了某种算法,后面需要的时候可以直接使用自然语言描述。常见的操作也可以使用自然语言描述。
例如,Euclid 算法用于计算 m 和 n 的最大公约数:
算法:Euclid(m,n)
输入:非负整数 m,n,其中 m 与 n 不全为 0
输出:m 与 n 的最大公约数
- while m>0 do
- r←nmodm
- n←m
- m←r
- return n
3. 算法的数学基础
关于程序的复杂度表示请参考 TODO 复杂度分析。
3.1 函数的渐进的界
给出下列定义:
- 定义 若存在正数 c 和 n0 使得对于 ∀n⩾n0 有 0⩽f(n)⩽cg(n) 成立,则称 f(n) 的 渐进的上界 是 g(n),记作 f(n)=O(g(n))
- 定义 若存在正数 c 和 n0 使得对于 ∀n⩾n0 有 0⩽cg(n)⩽f(n) 成立,则称 f(n) 的 渐进的下界 是 g(n),记作 f(n)=Ω(g(n))
- 对于任意正数 c 都存在 n0,使得 n⩾n0 时有 0⩽f(n)<cg(n) 成立,则 f(n)=o(g(n))
- 对于任意正数 c 都存在 n0,使得 n⩾n0 时有 0⩽cg(n)<f(n) 成立,则 f(n)=ω(g(n))
- f(n)=O(g(n)) 且 f(n)=Ω(g(n)),则记作 f(n)=Θ(g(n))
定理 设 f 和 g 是定义域为自然数集 N 上的非负函数,那么有:
- 如果 n→∞limg(n)f(n) 存在,并且等于某个常数 c>0,那么 f(n)=Θ(g(n))
- 如果 n→∞limg(n)f(n)=0,那么 f(n)=o(g(n))
- 如果 n→∞limg(n)f(n)=+∞,那么 f(n)=ω(g(n))
定理 设 f,g,h 是定义域为自然数集 N 上的函数,那么有:
- 如果 f=O(g) 且 g=O(h),那么 f=O(h)
- 如果 f=Ω(g) 且 g=Ω(h),那么 f=Ω(h)
- 如果 f=Θ(g) 且 g=Θ(h),那么 f=Θ(h)
定理 设 f 和 g 是定义域为自然数集 N 上的函数,若对于某个其他函数 h,有 f=O(h) 和 g=O(h),那么 f+g=O(h)。
定理 对于每个 b>1 和每个 a>0,有 logbn=o(na)。
对数的另一条性质是:对于不同的底 a 与 b,logan=Θ(logbn),只需要使用对数的基本恒等式
logan=logbalogbn
即可证明。通常我们以 logn 指以 2 为底的对数。
定理 对于每一个 r>1 和每一个 d>0,有 nd=o(rn)。
阶乘函数 f(n)=n! 是增长很快的函数,根据 斯特林公式,阶乘函数
n!=2πn(en)n(1+Θ(n1))
关于阶乘函数有下面的结果:
n!n!logn!=o(nn)=ω(2n)=Θ(nlogn)
阶乘
运算符 ! 的优先级大于加减乘除和其他常见运算符。
下面列举常见函数的界的大小比较结果,从高到低排序:
函数 |
---|
22n |
n! |
n2n |
(3/2)n |
(logn)logn=nloglogn |
n3 |
logn!=Θ(nlogn) |
n=Θ(2logn) |
log2n |
logn |
logn |
loglogn |
n1/logn=Θ(1) |
取整函数也是常见的一类函数,向下取整函数 ⌊x⌋ 相当于 C 语言的 floor(x)
,向上取整函数 ⌈x⌉ 相当于 C 语言的 ceil(x)
。
取整函数具有下列性质:
- x−1<⌊x⌋⩽x⩽⌈x⌉<x+1
- ⌊x+n⌋=⌊x⌋+n,⌈x+n⌉=⌈x⌉+n,其中 n 为整数
- ⌈2n⌉+⌊2n⌋=n,其中 n 为整数
- ⌈b⌈n/a⌉⌉=⌈abn⌉,⌊b⌊n/a⌋⌋=⌊abn⌋,其中 a,b,n 为整数
关于算法中常见的函数,其他章节也会进行讨论。