分析算法
循环不变式 (Loop Invariant)
循环不变式是用于证明算法(特别是循环类算法)正确性的重要工具。它是一个逻辑断言,在循环的每一次迭代前后都保持为真。
为了证明算法的正确性,我们需要验证关于循环不变式的三个性质:
- 初始化 (Initialization): 在循环的第一次迭代开始之前,循环不变式为真。这通常可以被视为数学归纳法的基本情况(Base case)。
- 保持 (Maintenance): 如果在某次迭代开始之前循环不变式为真,那么在下一次迭代开始之前,它依然为真。这对应于数学归纳法的归纳步骤。
- 终止 (Termination): 当循环结束时,不变式提供了一个有用的性质,该性质有助于证明算法是正确的。
示例(插入排序):
在
for j = 2 to A.length的循环开始前,子数组A[1..j-1]包含了原数组中前j-1个元素,且已按序排列。
伪代码 (Pseudocode)
伪代码是一种非正式的高级语言,用于描述算法的逻辑结构,忽略了具体的语法细节(如变量声明、内存管理),以便人类阅读和理解。
通用约定:
- 缩进:表示块结构(Block structure),代替了
{}或begin/end。 - 控制流:使用
if、else、while、for、repeat-until等常见关键字。 - 赋值:通常使用
←或=表示赋值操作(例如x ← y)。 - 变量:通常是局部变量,除非显式声明为全局。
- 数组访问:使用
A[i]表示数组 的第 个元素。 - 对象属性:使用
attribute[x]或x.attribute(例如length[A]或A.length)。 - 注释:通常用
//或▷表示。
示例片段:
INSERTION-SORT(A)
1 for j ← 2 to A.length
2 key ← A[j]
3 // 将 A[j] 插入到已排序序列 A[1..j-1]
4 i ← j - 1
5 while i > 0 and A[i] > key
6 A[i + 1] ← A[i]
7 i ← i - 1
8 A[i + 1] ← key2
3
4
5
6
7
8
9
执行时间 (Execution Time)
执行时间是指算法解决问题所需的时间,通常表示为输入规模
- 输入规模 (
):- 对于排序问题,
是数组中的元素个数。 - 对于图算法,
可能是顶点数或边数。 - 对于整数运算,
可能是二进制表示的位数。
- 对于排序问题,
- 计算模型 (RAM 模型):
- 假设指令(加、减、乘、除、数据移动、控制流)按顺序执行。
- 每条简单指令执行需常量时间
。
- 分析类型:
- 最坏情况 (Worst-case):对任意规模
的输入,算法运行时间的最长可能时间。这是最常用的分析指标,因为它提供了时间的上界保证。 - 平均情况 (Average-case):期望的运行时间,通常基于输入数据的概率分布假设。
- 最好情况 (Best-case):通常用处不大,但在特定场景下(如快速排序的最好情况分析)有参考价值。
- 最坏情况 (Worst-case):对任意规模
- 渐进符号:
:紧确界(既是上界又是下界)。 :渐进上界(关注最坏情况)。 :渐进下界。
习题
初始化 (Initialization): * 在循环开始前,我们还没有处理任何数字。 *
保持 (Maintenance): * 假设在某次迭代开始前,性质
终止 (Termination): * 当循环结束时,所有的位数都已处理完毕,