C語言初學者代碼中的常見錯誤與瑕疵(5) 代碼寫得要"拽"(DRY)——《C解毒》試讀

問題:

素數html

在世博園某信息通訊館中,遊客可利用手機等終端參與互動小遊戲,與虛擬人物Kr. Kong 進行猜數比賽。數據結構

當屏幕出現一個整數X時,若你能比Kr. Kong更快的發出最接近它的素數答案,你將會得到一個意想不到的禮物。函數

 

例如:當屏幕出現22時,你的回答應是23;當屏幕出現8時,你的回答應是7;post

若X自己是素數,則回答X;若最接近X的素數有兩個時,則回答大於它的素數。測試

 

輸入:第一行:N 要競猜的整數個數url

接下來有N行,每行有一個正整數Xspa

輸出:輸出有N行,每行是對應X的最接近它的素數設計

 

樣例:輸入rest

4code

22

5

18

8

輸出

23

5

19

7

 原代碼:

 1 #include <stdio.h>
 2 int prime(int x)        //判斷是否爲素數
 3 {  4     int i;  5 
 6     if (x==1)  7         return 0;  8 
 9     for(i=2;i<x;i++) 10  { 11         if(!(x%i)) 12             return 0; 13  } 14 
15     return 1; 16 } 17 
18 int main() 19 { 20     int i,j,n,a[6],temp1,temp2; 21     int prime(int x); 22 
23     //printf("n=");
24     scanf("%d",&n); 25 
26     for (i=0;i<n;i++)    //輸入相應數值
27  { 28         scanf("%d",&a[i]); 29  } 30 
31     for(i=0;i<n;i++) 32  { 33         
34         temp1=(temp2=a[i]); 35         for(j=a[i];;j++)        //求出大於等於數值的素數
36  { 37             if(prime(j)) 38  { 39                 temp1=j; 40                 break; 41  } 42  } 43 
44         for(j=a[i]-1;;j--)        //求出小於數值的素數
45  { 46             if(prime(j)) 47  { 48                 temp2=j; 49                 break; 50  } 51  } 52 
53         printf("%d\n",temp1-a[i]<=a[i]-temp2?temp1:temp2); 54  } 55     
56     return 0; 57 }

 評析:

    int i,j,n,a[6],temp1,temp2;
    int prime(int x);

   老問題,變量太多,數據結構設計不合理,函數類型定義位置不當。實際上這種問題有一個以不變應萬變的「標準」句型:

#include <stdio.h>

int main( void )
{

   int n ;

   scanf("%d" , &n);
   
   while ( n -- > 0 )
   {
     //定義解決問題須要的變量
      //輸入測試數據
      //解決問題
   }
   
   return 0;
}

   其中,裸體的「scanf("%d" , &n);」只在這種特定狀況(刷題)下能夠接受,對於通常狀況,在輸入前給用戶一個提示信息爲好。

    for (i=0;i<n;i++)    //輸入相應數值
    {
        scanf("%d",&a[i]);
    }

    for(i=0;i<n;i++)
    {
        
        temp1=(temp2=a[i]);
        for(j=a[i];;j++)        //求出大於等於數值的素數
        {
            if(prime(j))
            {
                temp1=j;
                break;
            }
        }

        for(j=a[i]-1;;j--)        //求出小於數值的素數
        {
            if(prime(j))
            {
                temp2=j;
                break;
            }
        }

        printf("%d\n",temp1-a[i]<=a[i]-temp2?temp1:temp2);
    }

   顯然應該合併爲一個for語句。原做者大概誤覺得必須所有輸入以後才能開始解決問題,估計是題目對輸入輸出樣式的說明容易讓人誤解,實際上輸入與輸出是相互獨立的。沒必要拘泥於先所有輸入後再開始逐個解決問題。 

        for(j=a[i];;j++)        //求出大於等於數值的素數
        {
            if(prime(j))
            {
                temp1=j;
                break;
            }
        }

        for(j=a[i]-1;;j--)        //求出小於數值的素數
        {
            if(prime(j))
            {
                temp2=j;
                break;
            }
        }

   這裏存在幾方面的問題,下面逐個評述。

  首先,拋開具體構思,僅僅從代碼形式上來講這中寫法就是不能容忍的。

  代碼寫得必定要「拽」(DRY)(參見代碼寫得要"拽"(DRY)——《C解毒》試讀)。

  而這裏的兩條for語句,本質上是如出一轍的。能夠說,一個是對另外一個的複製粘貼(若是寫的時候連複製粘貼都沒用到,而是老老實實一個字一個寫成的,那就更成問題了。那說明連計算機都不會用)。

  因此不管如何這兩條語句都應該想到用函數實現: 

temp1 = find_prime(a[i],1);   //求從a[i]開始的第一個素數(步長爲1);
temp2 = find_prime(a[i]-1,-1);//求從a[i]-1開始的第一個素數(步長爲-1);

   這樣寫代碼要漂亮多了。

  再說一下邏輯上的缺陷。

  「大於等於」的素數求出以後,若是是「等於」,那麼問題已經得解,就不必執行第二條循環語句了,因此應該跳過第二條循環語句。 

    for( i = 0 ; i < n ; printf("%d\n",solve) , i++ )
    {
        for(j=a[i];;j++)        //求出大於等於數值的素數
        {
                  //……
        }

       if ( temp1 == a[i] )
       {
           //輸出該素數(temp1或a[i])
           continue ;                  //轉到計算下一個a[i]
       }

        for(j=a[i]-1;;j--)        //求出小於數值的素數
        {
                 //……
        }
       printf("%d\n",temp1-a[i]<=a[i]-temp2?temp1:temp2);
    }

   若是用函數求下一個素數,代碼還能夠寫得更漂亮一些:

    for(i=0;i<n;i++)
    {
        int temp1 , temp2 ;
        
        if ( (temp1 = find_prime(a[i],1)) == a[i] )
        {
           printf("%d\n",temp1);//輸出temp1
           continue ; 
        }
      
        temp2 = find_prime(a[i]-1,-1)

        printf("%d\n",temp1-a[i]<=a[i]-temp2?temp1:temp2);
    }

   這顯然要比原來的代碼乾淨整潔多了。若是以爲兩條printf()調用很差,還能夠這樣寫(又是逗號表達式,呵呵):

    for( i = 0 ; i < n ; printf("%d\n",solve) , i++ )
    {
        int temp1 , temp2 , solve ;
        
        
        if ( (temp1 = find_prime(a[i],1)) == a[i] )
        {
           solve = temp1 ;
           continue ; 
        }
      
        temp2 = find_prime(a[i]-1,-1)
        solve = temp1-a[i]<=a[i]-temp2?temp1:temp2 ;
    }

   此外要說一下的是,不管是在main()中仍是在prime()函數中,做者都沒有認真考慮當輸入整數並不處於兩個素數之間的狀況。事實上這是有可能的。好比輸入爲1,那麼找小於1的素數是不可能的。但在原代碼中,明顯能看出做者根本就沒意識到這件事(main()中沒有相關的處理,prime()函數沒有對0和負整數進行判斷)。這應該說是原代碼中的一個BUG。

  最後再說一下整體思路方面的問題。      

  做者的思路是先「求出大於等於數值的素數」,再「求出小於數值的素數」。這個思路可行,但不夠好。比較好的思考應該是:

  1. 看這個數(假定是X)是否是素數
  2. 如不是,判斷X+1是否是素數
  3. 如不是,判斷X-1是否是素數
  4. 如不是,判斷X+2是否是素數
  5. 如不是,判斷X-2是否是素數
  6. ……

  這種想法更天然,一旦找到素數就完活兒,而不會作無用功。

  而原做者的代碼則存在這樣的可能:先求出一個比X大不少的素數,但其實解是X-1;或者求出大於X的素數是X+1,又去找出一個比X小不少的素數。這兩種狀況下,計算機總要作一些很無聊且無心義的工做。

  固然,原代碼還能夠繼續改進,但在錯誤的整體思路指導下,改進的空間極其狹仄。即便改了,也沒法完全根除指令雍餘的問題。

  而按照下面次序尋找最近素數

  X X+1 X-1 X+2 X-2 X+3……

  則不存在相似問題。

  不難發現,這個序列相鄰兩項差的絕對值,剛好構成天然數列:1 2 3 4 5 ……

  據此,重構以下: 

重構:

/* 問題: 素數 在世博園某信息通訊館中,遊客可利用手機等終端參與互動小遊戲,與虛擬人物Kr. Kong 進行猜數比賽。 當屏幕出現一個整數X時,若你能比Kr. Kong更快的發出最接近它的素數答案,你將會得到一個意想不到的禮物。 例如:當屏幕出現22時,你的回答應是23;當屏幕出現8時,你的回答應是7; 若X自己是素數,則回答X;若最接近X的素數有兩個時,則回答大於它的素數。 輸入:第一行:N 要競猜的整數個數 接下來有N行,每行有一個正整數X 輸出:輸出有N行,每行是對應X的最接近它的素數 樣例:輸入 4 22 5 18 8 輸出 23 5 19 7 做者:薛非 出處:http://www.cnblogs.com/pmer/ 「C語言初學者代碼中的常見錯誤與瑕疵」系列博文 */ #include <stdio.h> #include <stdbool.h> int get_nearest( int); bool be_prime( int ); int main( void ) { unsigned n ; puts( "數據組數=?" ); scanf("%u" , &n); //這裏沒寫輸入提示 
   
   while ( n -- > 0u ) { int x ; scanf("%d" , & x); printf("%d\n" , get_nearest( x ) ); } return 0; } int get_nearest( int x ) { int n , s ;//步長增量,符號
    
   for ( n = 0 , s = -1 ; ! be_prime( x ) ; n ++ , s = -s , x += s * n ) { } return x ; } bool be_prime( int x ) { int fac ; if ( x <= 1 ) return false ; for ( fac = 2 ; fac * fac <= x ; fac ++ ) if ( x % fac == 0 ) return false ; return true ; }

不足:

  重構的代碼中,be_prime()函數,僅僅從功能角度來講並無什麼問題。但若放在問題的背景下,它的效率過低了。這個函數須要反覆地對每個x都進行for ( fac = 2u ; fac * fac <= x ; fac ++ )這樣的循環判斷,若是問題要求判斷的只有一個整數,這種寫法也許無可厚非。但問題要求判斷的是X X+1 X-1 X+2 X-2 X+3……這樣一個序列,這樣的寫法就很是笨拙了。可是若想進一步改進卻也不那麼容易。此次就不改了,我將在後續的博文中給出改進的寫法。

相關文章
相關標籤/搜索