zoukankan      html  css  js  c++  java
  • 编程珠玑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输出也明显减少。

     


     

  • 相关阅读:
    算法:希尔排序(Shell Sort)
    算法:五步教你消除递归
    算法:排列算法
    算法:逆波兰表达式求值
    算法:中缀表达式转换为逆波兰表达式
    算法:优先级队列(PriorityQueue)
    Entityframework:“System.Data.Entity.Internal.AppConfig”的类型初始值设定项引发异常。
    算法:基于 RingBuffer 的 Deque 实现
    算法:基于 RingBuffer 的 Queue 实现《续》
    算法:基于 RingBuffer 的 Queue 实现
  • 原文地址:https://www.cnblogs.com/2010Freeze/p/2362932.html
Copyright © 2011-2022 走看看