代码评析与重构——求完数问题
求完數(shù)問(wèn)題
【題目2-10】
一個(gè)數(shù)如果恰好等于它的因子之和,這個(gè)數(shù)就稱(chēng)為"完數(shù)"。例如,6的因子為1、2、3,而6=1+2+3,因此6是“完數(shù)”。編程序找出1000之內(nèi)的所有完數(shù),并按下面格式輸出其因子:
6???? Its??? factors???? are?? 1???? 2???? 3
——譚浩強(qiáng) ,《C程序設(shè)計(jì)(第四版)學(xué)習(xí)輔導(dǎo)》,清華大學(xué)出版社,2010年7月,p43
【評(píng)析2-10-1】
6的因子(divisor)有1、2、3和6共4個(gè),所以所謂的完數(shù)(perfect number)顯然絕非指那些“一個(gè)數(shù)如果恰好等于它的因子之和”的數(shù)。
在數(shù)論中,完數(shù)(perfect number)指的是一個(gè)正整數(shù),它等于其正的真因數(shù)(proper positive divisors)之和。所謂正的真因數(shù)不包括該正整數(shù)自身。
【樣本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之間的整數(shù),檢查它是否完數(shù) 8. {n=0; //n用來(lái)累計(jì)a的因子的個(gè)數(shù) 9. s=a; //s用來(lái)存放尚未求出的因子之和,開(kāi)始時(shí)等于a 10. for(i=1;i<a;i++) //檢查i是否a的因子 11. if(a%i==0) //如果i是a的因子 12. {n++; //n加1,表示新找到一個(gè)因子 13. s=s-i; //s減去已找到的因子,s的新值是尚未求出的因子之和 14. switch(n) //將找到的因子賦給k1~k9,或k10 15. {case 1: 16. k1=i;break; //找到的第1個(gè)因子賦給k1 17. case 2: 18. k2=i;break; //找到的第2個(gè)因子賦給k2 19. case 3: 20. k3=i;break; //找到的第3個(gè)因子賦給k3 21. case 4: 22. k4=i;break; //找到的第4個(gè)因子賦給k4 23. case 5: 24. k5=i;break; //找到的第5個(gè)因子賦給k5 25. case 6: 26. k6=i;break; //找到的第6個(gè)因子賦給k6 27. case 7: 28. k7=i;break; //找到的第7個(gè)因子賦給k7 29. case 8: 30. k8=i;break; //找到的第8個(gè)因子賦給k8 31. case 9: 32. k9=i;break; //找到的第9個(gè)因子賦給k9 33. case 10: 34. k10=i;break; //找到的第10個(gè)因子賦給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個(gè)因子 41. if(n>2)printf(",%d",k3); //n>2表示a至少有3個(gè)因子 42. if(n>3)printf(",%d",k4); //n>3表示a至少有4個(gè)因子 43. if(n>4)printf(",%d",k5); //以下類(lèi)似 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. }【評(píng)析2-10-2】
這代碼!丑的簡(jiǎn)直驚天地泣鬼神。
5. int k1,k2,k3,k4,k5,k6,k7,k8,k9,k10;一上來(lái)就一口氣定義了10個(gè)變量,頗有愚公移山的氣概,吃奶的力氣都使出來(lái)了。
可惜的是從名字上根本看不出這些變量是做什么用的。實(shí)際上這是一種惡劣的命名方法。從后面的代碼來(lái)猜,這些變量應(yīng)該是用于存放各個(gè)因子的。但是為什么要定義十個(gè)呢?毫無(wú)根據(jù)。
是1000之內(nèi)的自然數(shù)的因子不多于十個(gè)嗎?這是壓根不成立的。譬如,210有1,2,3,5,6,7,10,14,15,21,30,35,42,70,105,210共16個(gè)因子,即使不考慮其自身,也有15個(gè)因子。定義10個(gè)變量存放因子顯然是一種粗暴且武斷的做法,因此代碼無(wú)疑是錯(cuò)誤的。如果它能輸出正確的結(jié)果,那也只不過(guò)是瞎貓碰到死耗子的巧合而已。
6. int i,a,n,s;這里最多需要定義一個(gè)變量a用于完成循環(huán)。一起定義了4個(gè)說(shuō)明思維漂移太遠(yuǎn),不清楚當(dāng)下到底應(yīng)該做什么。有句話叫活在當(dāng)下,編寫(xiě)代碼也是如此,不用急著定義當(dāng)下根本用不著的變量。
7. for(a=2;a<=M;a++) //a是2~1000之間的整數(shù),檢查它是否完數(shù) 8. {n=0; //n用來(lái)累計(jì)a的因子的個(gè)數(shù) 9. s=a; //s用來(lái)存放尚未求出的因子之和,開(kāi)始時(shí)等于a 10. for(i=1;i<a;i++) //檢查i是否a的因子 11. if(a%i==0) //如果i是a的因子 12. {n++; //n加1,表示新找到一個(gè)因子 13. s=s-i; //s減去已找到的因子,s的新值是尚未求出的因子之和 14. switch(n) //將找到的因子賦給k1~k9,或k10 15. {case 1: 16. k1=i;break; //找到的第1個(gè)因子賦給k1 17. case 2: 18. k2=i;break; //找到的第2個(gè)因子賦給k2 19. case 3: 20. k3=i;break; //找到的第3個(gè)因子賦給k3 21. case 4: 22. k4=i;break; //找到的第4個(gè)因子賦給k4 23. case 5: 24. k5=i;break; //找到的第5個(gè)因子賦給k5 25. case 6: 26. k6=i;break; //找到的第6個(gè)因子賦給k6 27. case 7: 28. k7=i;break; //找到的第7個(gè)因子賦給k7 29. case 8: 30. k8=i;break; //找到的第8個(gè)因子賦給k8 31. case 9: 32. k9=i;break; //找到的第9個(gè)因子賦給k9 33. case 10: 34. k10=i;break; //找到的第10個(gè)因子賦給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個(gè)因子 41. if(n>2)printf(",%d",k3); //n>2表示a至少有3個(gè)因子 42. if(n>3)printf(",%d",k4); //n>3表示a至少有4個(gè)因子 43. if(n>4)printf(",%d",k5); //以下類(lèi)似 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. }這個(gè)實(shí)在令人無(wú)語(yǔ)。這恐怕是C語(yǔ)言有史以來(lái)最長(zhǎng)的for語(yǔ)句,整整寫(xiě)了45行,絕對(duì)應(yīng)該載入史冊(cè)。盡管前輩編程達(dá)人曾經(jīng)諄諄告誡過(guò)我們,Don’t be too clever(不要過(guò)于機(jī)靈),但是依本人粗陋的理解,這絕對(duì)不是讓我們過(guò)于愚蠢。
如此龐大笨拙的for語(yǔ)言是否意味著一種愚蠢,還是留給讀者自己判斷吧。 9. s=a; //s用來(lái)存放尚未求出的因子之和,開(kāi)始時(shí)等于a從后面的代碼來(lái)看,s根本不是如注釋中所說(shuō)的那樣“用來(lái)存放尚未求出的因子之和”,因?yàn)檎l(shuí)也不知道,尚未求出的因子和是多少,但是s的初值卻是a。所以,注釋和代碼中至少有一個(gè)是口是心非地在撒謊。
14.~35.行是錯(cuò)誤的。原因前面提到過(guò),1000以內(nèi)的正整數(shù)的真因子可能多于10個(gè)。整整12行啊!悲催無(wú)比。程序員最大的悲劇是什么?就是不辭辛苦兢兢業(yè)業(yè)認(rèn)認(rèn)真真吭哧癟肚地寫(xiě)出了一大堆繁復(fù)無(wú)比比爛棉花套還要復(fù)雜的代碼之后,暮然回首,卻被指出那代碼完全是錯(cuò)誤的。
至此,我們同樣不難判斷出37.~49.行同樣是錯(cuò)誤的。這很自然。程序的全部基礎(chǔ)都建立在不靠譜的
5. int k1,k2,k3,k4,k5,k6,k7,k8,k9,k10;基礎(chǔ)之上。這個(gè)基礎(chǔ)既然是荒謬的,整個(gè)代碼必然是錯(cuò)誤的,想改都沒(méi)法改。只能將之完全推倒重來(lái)。
最后還有一個(gè)錯(cuò)誤不容易被發(fā)現(xiàn)。這個(gè)程序的輸出是:
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
這個(gè)輸出與題目要求的格式:
6???? Its??? factors???? are?? 1???? 2???? 3
并不一致。
【重構(gòu)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 ) //真因子和與整數(shù)相等 16 { 17 printf("%d是一個(gè)完全數(shù),其真因子為:",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 }?
總結(jié)
以上是生活随笔為你收集整理的代码评析与重构——求完数问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: EF架构~为IEnumerable接口添
- 下一篇: Query DSL