编程珠玑2第3章 程序员的忏悔

本章主要讲述在代码测试和调试过程中使用 “脚手架方法”。

“脚手架” 就是在代码中添加的冗余测试代码(通常输出程序的局部变量)。

1首先请看一个二分搜索的代码,要看仔细了:

 1 function bsearch(t)
2 {
3 l=1;u=n;//l是头,u是尾
4 while(l<=u)
5 {
6 m=int((l+u)/2);
7 print(l,m,u);
8 if(x[m]<t) l=m;
9 else if(x[m]>t) u=m;
10 else return m;
11 }
12 return 0;
13 }
14

咋一看好像没有问题,算法的基本结构都对的,但是代码确实有问题。

那么我们需要建立实例进行测试,对于数组x[n]的长度,不太大亦不能太小,作者认为n取5最好。对数组赋初值。

for(i=1;i<=n;i++) x[i]=10*i;

当bsearch(50)时,出现如下提示:

1 3 5

3 4 5

4 4 5

4 4 5

4 4 5

......

程序出现死循环,因为:

4<=5且int((4+5)/2)=4

当第一次看到着,我有种像是被人抓到把柄似的,我找到了自己以前写的二分查找的代码

我回忆了下,当初是因为考虑到x[m]已经考虑过,不需要再考虑。所以我写的是

if(x[m]<t) l=m+1;   else if(x[m]>t)  u=m-1;

这个想法使我避免了这个错误,事实上我不知道这是为了避免错误而必须采取的。

那么你是否曾经意识到这一点呢?

2下面看第二个例子,一个选择算法

 1 function select(l,u,k)
2 {
3 if(l<u){
4 swap(l,l+int((u-l+1)*rand()));
5 m=1;
6 for(i=l+1;i<=u;i++)
7 if(x[i]<x[l])
8 swap(++m,i);
9 swap(l,m);
10 if(m<k) select(m+1,u,k);
11 else if(m>k) select(l,m-1,k);
12 }
13 }

这个算法是Hoare写的用于求数组x[n]中第k小的元素,数组是无序的,运行后,x[k]就是要找的数据。

光看看可能看不明白,可以找个小纸条自己比划比划。写的凝练了,看起来简洁,但是更不容易直接理解。

熟悉Hoare的朋友知道,快速排序就是他创造的。

快速排序也叫快速定位,每次确定一个数据的位置。上面算法还混合了概率算法(sherwood)思想来平滑平均性能。

作者为了去掉尾递归部分(可能是出于性能考虑) 得到改进后的代码如下:

 1 function select(l,u,k)
2 {
3 l=1,u=n;
4 while(l<u){
5 print(l,u);
6 swap(l,l+int((u-l+1)*rand()));
7 m=1;
8 comps=comps+u-1;//记录比较次数
9 for(i=l+1;i<=u;i++)
10 if(x[i]<x[l])
11 swap(++m,i);
12 swap(l,m);
13 if(m<k) l=m+1;
14 else if(m>k) u=m-1;
15 }
16 }

作者给出了一个输入使得程序出现错误。就是当m==k的时候可能会产生死循环。

作者给出的改进方法是将if(m<k)改为if(m<=k),我不敢苟同。

第一:

我发现作者改进后还是有错误:

我的测试代码是:

测试完整代码 C语言
 1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <time.h>
4 int x[6];
5 void init()
6 {
7 int i;
8 for(i=1;i<6;i++)
9 {
10 x[i]=(int)rand();
11 printf("%d ",x[i]);
12 }
13 printf("\n");
14 }
15 void swap(int a,int b)
16 {
17 int temp=x[a];
18 x[a]=x[b];
19 x[b]=temp;
20 }
21 void select(int l,int u,int k)
22 {
23 int i,m,comps=0;//记录比较次数
24 while(l<u){
25 printf("%d %d\n",l,u);
26 swap(l,l+(int)(rand()%(u-l+1)));
27 m=1;
28 comps=comps+u-1;
29 for(i=l+1;i<=u;i++)
30 if(x[i]<x[l])
31 swap(++m,i);
32 swap(l,m);
33 if(m<=k) l=m+1;
34 else if(m>k) u=m-1;
35 }
36 }
37 void main()
38 {
39 int i;
40 srand(time(0));
41 init();
42 select(1,5,3);
43 for(i=1;i<6;i++)
44 {
45 printf("%d ",x[i]);
46 }
47 printf("\n");
48
49 }

发现的错误如下:

输出显示第三个数不是8504,具体原因我不分析了,如果你有兴趣可以琢磨琢磨。

 第二:

我们的目标是为了使第i个数存放在第i个位置。

如果运气好,随机选到的数刚好是要找的数,那么仅第一次循环它就被放到正确位置,此时程序就可以结束了。

就算第一次没被选到,后面也会被选到。也就是说当发现m=k的时候,程序就应该终止排序。

添加代码

if(m<k) l=m+1;    

else if(m>k) u=m-1;

else break;

我进行了初步测试,没发现问题。而且其print输出也明显减少。

 


 

原文地址:https://www.cnblogs.com/2010Freeze/p/2362932.html