算法分析与设计
为什么要分析算法?
- 按难度对问题和算法进行分类
- 预测性能,比较算法,调整参数
- 更好地理解和改进实现和算法
算法分析
问题:
- 正确性 (Correctness)
- 时间效率 (time efficiency)
- 空间效率 (space efficiency)
- 最优性 (optimality)
方法:
- 实证分析 (Empirical analysis)
- 理论分析 (Theoretical analysis)
运行时间
程序的运行时间通常取决于输入规模,并随输入规模的增加而增加。
时间效率的实证分析
- 编写实现算法的程序
- 使用各种输入规模运行程序(例如,500、1000、2000等)
- 测量实际运行时间(例如,在C/C++中使用clock()函数)
- 绘制结果图表
时间效率的理论(Theoretical)分析
影响运行时间的两个主要因素:
- 执行每个操作的成本(Cost)
取决于机器、编译器
- 每个操作的执行频率(Frequency)
取决于算法、输入数据
时间效率是通过计算其基本操作执行的次数来衡量的, o作为输入规模的函数。
"用一个关于输入规模n的函数f(n)来表示算法的时间效率,其中f(n)表示算法执行基本操作的次数。"
- 基本操作:对算法运行时间贡献最大的操作(例如,算法最内层循环中最耗时的操作)
- 近似计算cop和C(n)(忽略低阶项)
最佳情况、平均情况和最坏情况
对于某些算法,效率取决于输入的形式:
- 最坏情况:Cworst(n) - 在大小为n的输入上执行基本操作的最大次数
- 最佳情况:Cbest(n) - 在大小为n的输入上执行基本操作的最小次数
- 平均情况:Cavg(n) - 在大小为n的典型输入上执行基本操作的"平均"次数
增长的阶
假设C(n)=21n(n−1),如果计算机的速度提高一倍,算法将运行多快?如果问题规模增加一倍,需要多长时间来求解?
关键因素:
- 不是给定n时执行的基本操作的确切数量
- 而是随着n的增加,基本操作的数量如何增长
忽略常数因子、常数和小输入规模,关注n→∞时的增长阶数。
渐近增长阶数
- 比较函数的方法,忽略常数因子和小输入规模
- 根据增长率对函数进行分类
- O(g(n)):大O符号 - 增长速度不超过g(n)的函数f(n)类(最坏情况的上界)
- Θ(g(n)):大Θ符号 - 增长速度与g(n)相同的函数f(n)类(最佳情况)
- Ω(g(n)):大Ω符号 - 增长速度至少与g(n)一样快的函数f(n)类(最坏情况的下界)
大O符号(O-notation)
上界。如果存在常数c>0和n0≥0,使得对于所有n≥n0,有t(n)≤c⋅g(n),则t(n)是O(g(n))。
例如,t(n)=100n+5,证明t(n)∈O(n2):
证明:对于所有n≥1,有100n+5≤100n2+5n2=105n2(即取c=105和n0=1)。因此,t(n)∈O(n2)。
大Ω符号(Ω-notation)
下界。如果存在常数c>0和n0≥0,使得对于所有n≥n0,有t(n)≥c⋅g(n),则t(n)是Ω(g(n))。
例如,t(n)=n3,证明t(n)∈Ω(n2):
证明:对于所有n≥0,有n3≥n2(即取c=1和n0=0)。因此,t(n)∈Ω(n2)。
大Θ符号(Θ-notation)
紧界。如果存在常数c1>0,c2>0和n0≥0,使得对于所有n≥n0,有c2⋅g(n)≤t(n)≤c1⋅g(n),则t(n)是Θ(g(n))。
例如,t(n)=21n(n−1),证明t(n)∈Θ(n2):
证明:对于所有n≥0,有21n(n−1)=21n2−21n≤21n2(上界)。对于所有n≥2,有21n(n−1)=21n2−21n≥21n2−21n≥41n2(下界)(即取c2=41,c1=21和n0=2)。因此,t(n)∈Θ(n2)。
基本渐近效率类
类 | 时间 | 示例 |
---|
1 | 常数 | 两数相加 |
logn | 对数级 | 二分搜索 |
n | 线性 | 查找n个数中的最大值 |
nlogn | n-log-n或线性对数级 | 许多分治算法,例如归并排序 |
n2 | 平方 | 枚举所有元素对(通常是两个嵌套循环的算法),例如选择排序 |
n3 | 立方 | 枚举所有三元组(通常是三个嵌套循环的算法) |
2n | 指数 | 枚举n个项的所有子集 |
n! | 阶乘 | 生成n个项的所有排列或顺序 |
性质
传递性:
- 如果f∈O(g)且g∈O(h),则f∈O(h)。
- 如果f∈Ω(g)且g∈Ω(h),则f∈Ω(h)。
- 如果f∈Θ(g)且g∈Θ(h),则f∈Θ(h)。
可加性:
- 如果f∈O(h)且g∈O(h),则f+g∈O(h)。
- 如果f∈Ω(h)且g∈Ω(h),则f+g∈Ω(h)。
- 如果f∈Θ(h)且g∈Θ(h),则f+g∈Θ(h)。
如果f1(n)∈O(g1(n))且f2(n)∈O(g2(n)),则f1(n)+f2(n)∈O(max(g1(n),g2(n)))。
如果f1(n)∈Ω(g1(n))且f2(n)∈Ω(g2(n)),则f1(n)+f2(n)∈Ω(max(g1(n),g2(n)))。
如果f1(n)∈Θ(g1(n))且f2(n)∈Θ(g2(n)),则f1(n)+f2(n)∈Θ(max(g1(n),g2(n)))。
由两个依次执行的部分组成的算法的总体效率由具有更高增长阶数的部分决定(即其效率最低的部分)。
使用极限比较增长阶数
如果limn→∞g(n)f(n)=⎩⎨⎧0,c,∞,则f(n)∈O(g(n))则f(n)∈Θ(g(n))且f(n)∈O(g(n))且f(n)∈Ω(g(n))则f(n)∈Ω(g(n))
例如,比较21n(n−1)和n2的增长阶数:
limn→∞n221n(n−1)=21limn→∞n2n2−n=21limn→∞(1−n1)=21
因此,21n(n−1)∈Θ(n2)。
总结
- 时间效率:表示算法运行的速度
- 输入规模的函数
- 基本操作执行的次数
- 最坏情况、平均情况、最佳情况
- 渐近增长阶数
- 效率类别
- 常数级(Constant)、对数级(Logarithmic)、线性级(Linear)、线性对数级(Linearithmic)、平方级(Quadratic)、立方级(Cubic)、指数级(Exponential)