2.2 分析算法
一.
1.
假定采用一种通用的单处理器计算模型——随机访问机(random-access machine,RAM)来作为实现技术,算法可以通过计算机程序来实现。在RAM模型中,指令一条接一条的执行,没有并发操作。
2.
RAM模型包含真实计算机中常见的指令:
- 算术指令,如加法、减法、乘法、除法、取余、向下取整、向上取整)
- 数据移动指令:装入、存储、复制
- 控制指令:条件与无条件转移、子程序调用与返回
每条这样的指令所需时间都为常量
3.
RAM模型中的数据类型有整数型和浮点实数型。
4.
我们还对每个数据字的规模假定一个范围。
例如:当处理规模为n的输入时,我们一般假定对某个≥1的常量c,整数由clgn位来表示。我们要求c≥1,这样每个字都可以保存n的值,从而使我们能索引单个输入元素。我们限制c为常量,这样字长就不会任意增长。
5.
一般来说,算法需要的时间与输入的规模同步增长,所以通常把一个程序的运行时间描述成其输入规模的函数。
输入规模依赖于被研究的问题。
运行时间:一个算法在特定输入上的运行时间是指执行的基本操作数或步数。
6.
研究算法复杂度时采用以下观点:
执行每行伪代码需要常量时间。虽然一行与另一行可能需要不同数量的时间,但是我们假定第i行每次执行时间为ci,其中ci是一个常量。
这个观点是和RAM模型是一致的,并且也反映了伪代码在大多数真实计算机上如何实现。
7.
算法的运行时间是执行每条语句的运行时间之和。
需要执行ci步且执行n次的一条语句将贡献cin给总运行时间。但是这一特性对内存式的资源并不成立,访问m个存储字且执行n次的一条语句不必访问mn个不同的存储字。
8.
有些“随机化”算法即使对固定的输入其行为也可能变化。通常情况下,算法的运行时间对给定的输入是固定的。
9.
一般研究算法的最坏情况运行时间,即对规模为n的任意输入,算法的最长运行时间。
理由如下:
- 一个算法的最坏情况运行时间给出了任何输入的运行时间的一个上界。
- 对某些算法,最坏情况经常出现。
- “平均情况”往往与最坏情况大致一样差。
在某些情况下,会对算法的平均情况运行时间感兴趣——使用随机化算法,做出随机选择以允许进行概率分析并产生某个期望的运行时间。
10.
对于算法真正感兴趣的是其运行时间的增长率或增长量级。用符号Θ表示,如插入排序算法具有最坏情况运行时间为Θ(n2)。
如果一个算法的最坏情况运行时间具有比另一个算法更低的增长量级,通常认为前者比后者更有效。
二.
2.2-1
用Θ记号表示函数n3/1000 - 100n2 - 100n + 3
Θ(n3)
2.2-2
考虑排序存储在数组A中的n个数:首先找出A中的最小元素并将其与A[1]中的元素进行交换。接着,找出A中的次最小元素并将其与A[2]中的元素进行交换。对A中的前n - 1个元素按该方式继续。该算法称为选择算法,写出其伪代码。该算法维持的循环不变式是什么?为什么他只需要对前n - 1个元素,而不是对所有n个元素运行?用θ记号给出选择排序的最好情况和最坏情况运行时间。
SELECTION-SORT(A)
n = A.length - 1
for i = 1 to n
m = i
for j = i + 1 to A.length
if a[m] > a[j]
m = j
k = a[m]
a[m] = a[i]
a[i] = k
循环不变式为:A[1 . . i - 1]是序列中最小的按序排列的i - 1个数
初始化:A[1 . . 0]为空,循环不变式成立
保持:A[1 . . i - 1]为序列中最小的i - 1个数,当下一次循环迭代前,取出之后最小元素,将其放在A[i]则A[1 . . i]为序列中最小的i个数
终止:当循环结束时i = A.length,A[1 . . n-1]是序列中按序排列最小的n - 1个数,故A[n]是A[1 . . n]中最大的数,按序排列完毕,算法正确性得证
Θ(n2) Θ(n2)
2.2-3
再次考虑线性查找问题(参见练习 2.1-3)。假定要查找的元素等可能地为数组中的任意元素,平均需要检查输入序列的多少元素?最坏情况又如何?用θ记号给出线性查找的平均情况和最坏情况运行时间。证明你的答案。
最好情况,第一个数就是目标数,次数为1
最坏情况,最后一个数为目标数或没有目标数,查找次数为n
平均需要检查(n + 1) / 2
Θ(n)
Θ(n)
2.2-4
应如何修改任何一个算法,才能使之具有良好的最好情况运行时间?
略