代码意识流——花朵数问题(一)

0.问题                          (黑体,15)

  一个N位的十进制正整数,如果它的每个位上的数字的N次方的和等于这个数本身,则称其为花朵数。              (宋体,15,Arial)
  例如:
  当N=3时,153就满足条件,因为 1^3 + 5^3 + 3^3 = 153,这样的数字也被称为水仙花数(其中,“^”表示乘方,5^3表示5的3次方,也就是立方)。
  当N=4时,1634满足条件,因为 1^4 + 6^4 + 3^4 + 4^4 = 1634。
  当N=5时,92727满足条件。
  实际上,对N的每个取值,可能有多个数字满足条件。

  程序的任务是:求N=21时,所有满足条件的花朵数。注意:这个整数有21位,它的各个位数字的21次方之和正好等于这个数本身。如果满足条件的数字不只有一个,请从小到大输出所有符合条件的数字,每个数字占一行。因为这个数字很大,请注意解法时间上的可行性。要求程序在3分钟内运行完毕。

1.用C语言表达问题

/*0_问题.h*/
#ifndef WENTI_H
#define WENTI_H 

    #define CESHI        //进行测试  
    //#define QIUJIE     //求解21位问题                              
    
    #ifdef CESHI             //测试
      #define JINZHI 10      //十进制 
      #define WEISHU 3       //3位花朵数   
      #define N      WEISHU  //幂次==位数 
    #endif //CESHI
    
    #ifdef QIUJIE            //求解            
      #define JINZHI 10      //十进制 
      #define WEISHU 21      //位数    
      #define N      WEISHU  //幂次==位数 
    #endif //QIUJIE
    
#endif // WENTI_H

  编写代码首先要提出问题,理解问题,并用C语言表达问题。

  这里的符号常量就是C语言对问题的描述。尽管不可能完全描述,但却充分地表现了问题的特征。由于对问题进行了抽象,使得代码可以具有更广的适应范围。即不但适合求解21位花朵数,同样适合求解其他位数的花朵数问题;不但适合求解十进制问题,也适合其他进制问题。
  把问题中的常数写成符号常量同时也是为了测试的需要。没有人敢开一辆没有测试过的汽车,但是很奇怪,人们却敢于使用没有经过充分测试的软件,实际上后者往往更危险。

  测试的必要性还体现在,稍具规模的代码几乎绝对不可能一气呵成地一次性写正确,测试在这里实际上也是开发的有力助手。
  在开始写代码时并不清楚究竟有没有21位花朵数,如果有的话,同样也不清楚这个花朵数是多少。如果希望对自己的代码有点信心,那么这种信心只能来自测试。然而对21位花朵数的测试几乎没有可能,这时只能测试规模较小的同类问题。较小规模问题测试通过后,我们才能对较大的问题的解产生一些自信。

  这里的“#define CESHI        //进行测试”就如同一个开关一样,如果把CESHI改成QIUJIE,不用修改代码,只要重新编译,就可以得到求解21位问题的程序。

       
2.开始求解

    首先写main()。由于在代码编写过程中涉及到测试,所以写了两个main()。   

   /*1_MAIN.c*/
/*
  Name:花朵数问题 
  Author:键盘农夫  
  Date:2011,5,30 
  Description: 
*/

#include "1_MAIN.h"

#ifdef CESHI               //测试

   int main( void )
   {
   
      system("PAUSE");	
      return 0;
   }

#endif //CESHI

#ifdef QIUJIE              //求解

   int main( void )
   {
   
      system("PAUSE");	
      return 0;
   }
   
#endif //QIUJIE

  与之对应的,还有一个 

/*1_MAIN.h*/
#ifndef MAIN_H
#define MAIN_H 

   #include "0_问题.h"           
/**************************类型定义**************************/ 


/**************************函数原型**************************/ 


   #include <stdlib.h>                 //system()
    	
#endif // MAIN_H

  这是联系“0_问题.h”和main()的纽带,同时也以备以后补充类型定义和函数原型。

  3.解决方案

  回顾问题不难发现,问题的全部要求可分为“求解”和“输出”两个步骤。

  搜索一下自己的数学知识,并没有什么神奇的定理对解决这个问题有所帮助。没办法,只好用最笨的办法——穷举。穷举只能在穷举的过程中对所枚举的各种可能进行验算,因此main()改写为

#ifdef QIUJIE              //求解

   int main( void )
   {
      //求解:穷举<=>验算<=>记录结果 
      //输出 
      system("PAUSE");	
      return 0;
   }
   
#endif //QIUJIE

  “记录结果”而不是立即输出的原因是问题要求“从小到大输出”。 

  编译通过,运行正常!收工。 

4.完成第一个自定义函数框架的过程

  至少有四个函数需要写,首先从“穷举”入手。步骤:

  1.在main()中写出函数调用,qiongju();

  2.紧接着在原地描述这个函数的原型,void qiongju( void );

#ifdef QIUJIE              //求解

   int main( void )
   {
      //求解:穷举<=>验算<=>记录结果 
      qiongju();//void qiongju( void );
      //输出 
      system("PAUSE");	
      return 0;
   }
   
#endif //QIUJIE

  3.考虑这个函数定义的位置,由于这个函数可能比较复杂,所以把这个函数安排在另外一个源文件“2_穷举.c”中,添加“2_穷举.c”和“2_穷举.h”

  4.在"1_MAIN.h"中加入 #include "2_穷举.h" 预处理命令

  5.将main()中的qiongju()的函数原型移动到“2_穷举.h”的函数原型部分,并修改为
    extern void qiongju( void );

/*2_穷举.h*/ 
#ifndef QIONGJU_H
#define QIONGJU_H 
/**************************类型定义**************************/
/**************************函数原型**************************/
 extern void qiongju( void );
#endif // QIONGJU_H

  6.在"2_穷举.c"中写出空的qiongju()函数定义,顺便把main()中的一部分注释移过来

/*2_穷举.c*/

#include "2_穷举.h"

extern void qiongju( void )
{
//<=>验算<=>记录结果
}

  把 0_问题.h 中的 #define CESHI  中的CESHI改成QIUJIE,编译通过,运行正常!收工。

(未完待续)

posted @ 2011-05-30 13:49  键盘农夫  阅读(2726)  评论(12编辑  收藏  举报