分析算法
循环不变式 (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)。 - 注释:通常用
//或▷表示。
示例片段:
text
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] ← key1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
执行时间 (Execution Time)
执行时间是指算法解决问题所需的时间,通常表示为输入规模
- 输入规模 (
):- 对于排序问题,
是数组中的元素个数。 - 对于图算法,
可能是顶点数或边数。 - 对于整数运算,
可能是二进制表示的位数。
- 对于排序问题,
- 计算模型 (RAM 模型):
- 假设指令(加、减、乘、除、数据移动、控制流)按顺序执行。
- 每条简单指令执行需常量时间
。
- 分析类型:
- 最坏情况 (Worst-case):对任意规模
的输入,算法运行时间的最长可能时间。这是最常用的分析指标,因为它提供了时间的上界保证。 - 平均情况 (Average-case):期望的运行时间,通常基于输入数据的概率分布假设。
- 最好情况 (Best-case):通常用处不大,但在特定场景下(如快速排序的最好情况分析)有参考价值。
- 最坏情况 (Worst-case):对任意规模
- 渐进符号:
:紧确界(既是上界又是下界)。 :渐进上界(关注最坏情况)。 :渐进下界。
习题
1. 指出竖式除法(除数是整数)的循环不变式
背景设定: 假设我们要计算
是被除数,由数字序列 组成(或者 ,视索引习惯而定,这里假设从高位到低位处理)。 是除数(整数)。 是最终的商。 是最终的余数。- 在算法过程中,
是当前生成的商, 是当前的部分余数。
竖式除法过程回顾: 我们在每一轮循环中,从被除数
循环不变式:
假设我们在第
在处理第
位数字之前,当前累积的商 和当前余数 满足以下关系: 其中:
是被除数 已经被处理过的前缀部分(即从最高位到第 位组成的整数)。 (余数必须始终小于除数)。
证明(验证三要素)
为了更清晰地说明,假设被除数
初始化 (Initialization):
- 在循环开始前,我们还没有处理任何数字。
是空的,数值为 0。- 我们初始化
, 。 - 代入公式:
,且 。 - 成立。
保持 (Maintenance):
- 假设在某次迭代开始前,性质
成立。 - 循环操作:
- 落下下一位数字
。新的被除数前缀值变为了 。 - 构造新的被除部分:
。 - 计算商的当前位:
。 - 更新余数:
。 - 更新总商:
。
- 落下下一位数字
- 验证: 我们需要证明
。 根据归纳假设, 。 所以上式 ,这正是 。 - 成立。
- 假设在某次迭代开始前,性质
终止 (Termination):
- 当循环结束时,所有的位数都已处理完毕,
等于完整的被除数 。 - 此时,
变成了最终的商 , 变成了最终的余数 。 - 根据不变式:
,且 。 - 这正是除法算法正确性的定义。
- 成立。
- 当循环结束时,所有的位数都已处理完毕,