循环不变式的三个性质:
伪代码中的约定:
2.1-1 以图2-2为模型,说明INSERTION-SORT在数组A=<31,41,59,26,41,58>上的执行过程。
31 | 41 | 59 | 26 | 41 | 58 |
31 | 41 | 59 | 26 | 41 | 58 |
31 | 41 | 59 | 26 | 41 | 58 |
26 | 31 | 41 | 59 | 41 | 58 |
26 | 31 | 41 | 41 | 59 | 58 |
26 | 31 | 41 | 41 | 58 | 59 |
注:红色表示要准备插入的数,蓝色表示与红色数字比较过的数。
2.1-2 重写过程INSERTION-SORT,使之按非升序(而不是按非降序)排序。
伪代码如下:
INSERTION-SORT(A)
for j←to length[A]
do key←A[J]
i←j-1
while i>0 and A[i]<key
do A[i+1]←A[i]
i←i-1
A[i+1]←key
2.1-3 考虑下面的查找问题:
输入:一列数A=<a1,a2,…,an>和一个值V。
输出:下标i,使得V=A[i],或者当V不再A中出现时为NIL。
写出针对这个问题的线性查找的伪代码,它顺序地扫描整个序列以查找V。利用循环不变式证明算法的正确性。确保所给出的循环不变式满足三个必要的性质。
伪代码如下:
LINEAR-SEARCH(A,V)
flag←0
for i←1 to length[A]
do if A[i]==V
then print i
flag←1
if flag==0
then print NIL
循环不变式的证明如下:
初始化:首先,先来证明在第一轮迭代之前,它是成立的。此时,flag初始化为0,表示未找到这样一个小标i,使得A[i]=V.若为1,则表示已找到一个或多个这样的下标。那么,很显然,在还未开始之前flag=0是正确的,也就证明了循环不变式在循环的第一轮迭代开始之前是成立的。
保持:接下来证明每一轮循环都能使循环不变式保持成立。我们看到,在第一个for循环体内,随着i逐个从1到遍历完整个数列的过程中,只要有一个下标i,使得A[i]等于V,那么在输出i的同时,将flag重新标记为1,表示已找到。无论接下来是否还有这样满足条件的i出现,flag的值不变,仍为1,反之,若遍历完之后,没有找到这样的一个i,那么flag在这个for循环中未做任何改变,仍为0。所以,循环不变式的第二个性质也成立。
终止:对此线性查找算法来说,当i大于length[A]的值(即遍历完整个A数列后),for循环结束。这时,如果flag未改变(即flag=0),则说明未能找到这样的下标i,输出NIL;反之,若已在for循环中被修改为1,则不会运行此步语句,这也就意味着该算法是正确的。
2.1-4 有两个各存放在数组A和B中的n位二进制整数,考虑它们的相加问题。两个整数的和以二进制形式存放在具有(n+1)个元素的数组C中。请给出这个问题的形式化描述,并写出伪代码。
形式化描述如下:
首先,初始化数组C,使其n+1各元素的值都为0。接着,利用for循环用i同时从n到1反向遍历A、B数组,每遍历一步,作一个判断:若A[i]+B[i]>1,则C[i]=C[i]+1;反之,C[i+1]=A[i]+B[i]。最后,当i=0时,for循环结束,此时C数组中存储的即是所求的结果。
伪代码如下:
BINRARY-SUM(A,B,C)
for i←1 to n+1
do C[i]←0
for i←n to 1
do if A[i]+B[i]>1
then C[i]=C[i]+1
else C[i+1]=A[i]+B[i]