代码意识流——花朵数问题(一)
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()。
/* 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,编译通过,运行正常!收工。
(未完待续)