代碼評析與重構——求完數問題

求完數問題

【題目2-10】

  一個數若是剛好等於它的因子之和,這個數就稱爲"完數"。例如,6的因子爲一、二、3,而6=1+2+3,所以6是「完數」。編程序找出1000以內的全部完數,並按下面格式輸出其因子:c++

  6     Its    factors     are   1     2     3程序員

                      ——譚浩強 ,《C程序設計(第四版)學習輔導》,清華大學出版社,2010年7月,p43編程

【評析2-10-1】

  6的因子(divisor)有一、二、3和6共4個,因此所謂的完數(perfect number)顯然絕非指那些「一個數若是剛好等於它的因子之和」的數。學習

  在數論中,完數(perfect number)指的是一個正整數,它等於其正的真因數(proper positive divisors)之和。所謂正的真因數不包括該正整數自身。spa

【樣本2-10】

1.	#define M 1000                       //定義尋找範圍 
2.	#include <stdio.h>
3.	int main()
4.	{
5.	int k1,k2,k3,k4,k5,k6,k7,k8,k9,k10;
6.	int i,a,n,s;
7.	for(a=2;a<=M;a++)                 //a是2~1000之間的整數,檢查它是否完數 
8.	{n=0;                            //n用來累計a的因子的個數
9.	s=a;                            //s用來存放還沒有求出的因子之和,開始時等於a 
10.	for(i=1;i<a;i++)               //檢查i是否a的因子 
11.	if(a%i==0)                   //若是i是a的因子 
12.	{n++;                           //n加1,表示新找到一個因子 
13.	s=s-i;                       //s減去已找到的因子,s的新值是還沒有求出的因子之和 
14.	switch(n)                      //將找到的因子賦給k1~k9,或k10 
15.	{case 1:
16.	k1=i;break;               //找到的第1個因子賦給k1 
17.	case 2:
18.	k2=i;break;               //找到的第2個因子賦給k2
19.	case 3:
20.	k3=i;break;               //找到的第3個因子賦給k3
21.	case 4:
22.	k4=i;break;               //找到的第4個因子賦給k4
23.	case 5:
24.	k5=i;break;               //找到的第5個因子賦給k5
25.	case 6:
26.	k6=i;break;               //找到的第6個因子賦給k6
27.	case 7:
28.	k7=i;break;               //找到的第7個因子賦給k7
29.	case 8:
30.	k8=i;break;               //找到的第8個因子賦給k8
31.	case 9:
32.	k9=i;break;               //找到的第9個因子賦給k9
33.	case 10:
34.	k10=i;break;              //找到的第10個因子賦給k10
35.	}
36.	}
37.	if(s==0)
38.	{
39.	printf("%d,Its factors are ",a);
40.	if(n>1)printf("%d,%d",k1,k2);  //n>1表示a至少有2個因子 
41.	if(n>2)printf(",%d",k3);       //n>2表示a至少有3個因子
42.	if(n>3)printf(",%d",k4);       //n>3表示a至少有4個因子 
43.	if(n>4)printf(",%d",k5);       //如下相似
44.	if(n>5)printf(",%d",k6);
45.	if(n>6)printf(",%d",k7);
46.	if(n>7)printf(",%d",k8);
47.	if(n>8)printf(",%d",k9);
48.	if(n>9)printf(",%d",k10);
49.	printf("\n");
50.	}         
51.	}
52.	return 0;
53.	}

【評析2-10-2】

  這代碼!醜的簡直驚天地泣鬼神。設計

5.    int k1,k2,k3,k4,k5,k6,k7,k8,k9,k10;

  一上來就一口氣定義了10個變量,很有愚公移山的氣概,吃奶的力氣都使出來了。code

  惋惜的是從名字上根本看不出這些變量是作什麼用的。實際上這是一種惡劣的命名方法。從後面的代碼來猜,這些變量應該是用於存放各個因子的。可是爲何要定義十個呢?毫無根據。blog

  是1000以內的天然數的因子很少於十個嗎?這是壓根不成立的。譬如,210有1,2,3,5,6,7,10,14,15,21,30,35,42,70,105,210共16個因子,即便不考慮其自身,也有15個因子。定義10個變量存放因子顯然是一種粗暴且武斷的作法,所以代碼無疑是錯誤的。若是它能輸出正確的結果,那也只不過是瞎貓碰到死耗子的巧合而已。it

6.    int i,a,n,s;

  這裏最多須要定義一個變量a用於完成循環。一塊兒定義了4個說明思惟漂移太遠,不清楚當下到底應該作什麼。有句話叫活在當下,編寫代碼也是如此,不用急着定義當下根本用不着的變量。io

7.	for(a=2;a<=M;a++)                 //a是2~1000之間的整數,檢查它是否完數 
8.	{n=0;                            //n用來累計a的因子的個數
9.	s=a;                            //s用來存放還沒有求出的因子之和,開始時等於a 
10.	for(i=1;i<a;i++)               //檢查i是否a的因子 
11.	if(a%i==0)                   //若是i是a的因子 
12.	{n++;                           //n加1,表示新找到一個因子 
13.	s=s-i;                       //s減去已找到的因子,s的新值是還沒有求出的因子之和 
14.	switch(n)                      //將找到的因子賦給k1~k9,或k10 
15.	{case 1:
16.	k1=i;break;               //找到的第1個因子賦給k1 
17.	case 2:
18.	k2=i;break;               //找到的第2個因子賦給k2
19.	case 3:
20.	k3=i;break;               //找到的第3個因子賦給k3
21.	case 4:
22.	k4=i;break;               //找到的第4個因子賦給k4
23.	case 5:
24.	k5=i;break;               //找到的第5個因子賦給k5
25.	case 6:
26.	k6=i;break;               //找到的第6個因子賦給k6
27.	case 7:
28.	k7=i;break;               //找到的第7個因子賦給k7
29.	case 8:
30.	k8=i;break;               //找到的第8個因子賦給k8
31.	case 9:
32.	k9=i;break;               //找到的第9個因子賦給k9
33.	case 10:
34.	k10=i;break;              //找到的第10個因子賦給k10
35.	}
36.	}
37.	if(s==0)
38.	{
39.	printf("%d,Its factors are ",a);
40.	if(n>1)printf("%d,%d",k1,k2);  //n>1表示a至少有2個因子 
41.	if(n>2)printf(",%d",k3);       //n>2表示a至少有3個因子
42.	if(n>3)printf(",%d",k4);       //n>3表示a至少有4個因子 
43.	if(n>4)printf(",%d",k5);       //如下相似
44.	if(n>5)printf(",%d",k6);
45.	if(n>6)printf(",%d",k7);
46.	if(n>7)printf(",%d",k8);
47.	if(n>8)printf(",%d",k9);
48.	if(n>9)printf(",%d",k10);
49.	printf("\n");
50.	}         
51.	}

  這個實在使人無語。這恐怕是C語言有史以來最長的for語句,整整寫了45行,絕對應該載入史冊。儘管前輩編程達人曾經諄諄告誡過咱們,Don’t be too clever(不要過於機靈),可是依本人粗陋的理解,這絕對不是讓咱們過於愚蠢。

  如此龐大笨拙的for語言是否意味着一種愚蠢,仍是留給讀者本身判斷吧。
9.    s=a;                            //s用來存放還沒有求出的因子之和,開始時等於a

  從後面的代碼來看,s根本不是如註釋中所說的那樣「用來存放還沒有求出的因子之和」,由於誰也不知道,還沒有求出的因子和是多少,可是s的初值倒是a。因此,註釋和代碼中至少有一個是口是心非地在撒謊。

  14.~35.行是錯誤的。緣由前面提到過,1000之內的正整數的真因子可能多於10個。整整12行啊!悲催無比。程序員最大的悲劇是什麼?就是任勞任怨兢兢業業認認真真吭哧癟肚地寫出了一大堆繁複無比比爛棉花套還要複雜的代碼以後,暮然回首,卻被指出那代碼徹底是錯誤的。

  至此,咱們一樣不難判斷出37.~49.行一樣是錯誤的。這很天然。程序的所有基礎都創建在不靠譜的

5.    int k1,k2,k3,k4,k5,k6,k7,k8,k9,k10;

基礎之上。這個基礎既然是荒謬的,整個代碼必然是錯誤的,想改都無法改。只能將之徹底推倒重來。

  最後還有一個錯誤不容易被發現。這個程序的輸出是:

6,Its factors are 1,2,3

28,Its factors are 1,2,4,7,14

496,Its factors are 1,2,4,8,16,31,62,124,248

  這個輸出與題目要求的格式:

  6     Its    factors     are   1     2     3

並不一致。

【重構2-10】

 1 #include <stdio.h>
 2 
 3 #define TOP 1000
 4 
 5 int main( void )
 6 {
 7    int n ;
 8    for( n = 1 ; n <= TOP ; n++)
 9    {
10       int fac , sum ;
11       for( sum = 0 , fac = 1 ; fac < n ; fac++)  //求真因子和
12          if( n % fac == 0 )
13             sum += fac ;
14 
15       if( sum == n ) //真因子和與整數相等
16       {
17          printf("%d是一個徹底數,其真因子爲:",n);
18          for( fac = 1 ; fac < n ; fac++)
19             if( n % fac == 0 )
20                printf(" %d",fac);
21 
22          putchar('\n');
23       }
24    }
25 
26    return 0;
27 }
相關文章
相關標籤/搜索