代码意识流——花朵数问题(四)
本文前一部分的链接
http://www.cnblogs.com/KBTiller/archive/2011/06/01/2065569.html
10.考虑求各位数N次方的和
有两个问题需要考虑,第一是存放各位数N次方的和的变量定义的位置,其次是这个变量的类型。
变量定义的位置,首先考虑在xunhuan()函数定义形参的地方,把这个函数定义为
static void xunhuan( const int gssx /*个数上限*/,
const int sz /*关于哪个数字*/,
某类型 和)
{
}
这样的话在qiongju()函数中应该这样调用
xunhuan( WEISHU , JINZHI-1 ,(某类型) 0 ) ;
然而由于这个数据的类型不可能是简单的基本类型,所以如果编译器不支持C99的话,就必须首先在qiongju()函数中定义一个变量,把这个变量初始化为0之后再用这个变量作为实参进行调用:
extern void qiongju( void )
{
某类型 和 = {0} ;
xunhuan( WEISHU , JINZHI-1 , 和 ) ;
}
除了初始化为0之后做实参,“和”这个变量在qiongju()函数中根本没有其他任何用处,所以在qiongju()函数中定义这个变量恐怕难免给人一种牵强的感觉,当然,如果不太讲究的话,这样写也可以。
C99中有一种东西叫Compound literal,这种东西的本质就是一种复杂数据类型的常量。如果可以使用这种东东,就可以避免牵强地去定义变量。
由于C99的实现不是那么普遍,所以还是选择把这个变量定义在xunhuan()内部。这个变量用于递归调用中传递数据,因此必须是static类别。
static void xunhuan( const int gssx /*个数上限*/,
const int sz /*关于哪个数字*/)
{
static 某类型 he ;//=0
……
}
11.测试
先考虑求各位数一次方的和,这时可以把“和”的类型暂时确定为int
static void xunhuan( const int gssx /*个数上限*/, const int sz /*关于哪个数字*/) { static int he = 0 ;//=0 //static 某类型 he ;//=0 int he_ = he ; //记录累加前的值 if( sz > 0 ){ int i; for( i = 0 ; i <= gssx ; i++ ){ printf("%d*%d +" , i , sz ); he += i * sz ; // 每次调用都从he开始累加 xunhuan( gssx - i , sz - 1 ); he = he_ ; //恢复原来的值 } } else{ he += gssx * sz ; printf("%d*%d = %d" , gssx , sz , he ); putchar('\n');//<=>验算<=>记录结果 } }
运行结果正确。
12.一个修正
突然觉得前面把 #include "0_问题.h" 写在 1_MAIN.h 和 2_穷举.h 中很傻,这条预处理命令应该写在 1_MAIN.c 和 2_穷举.c 中。分别移动一下,然后编译,运行,通过。移动成功
13.添加新类型
由于“和”这个变量可能需要存储一个很大的整数值,所以普通的整数类型是不能胜任的,需要自己定义新的类型。设新的类型的名字为 DASHU
修改
static void xunhuan( const int gssx /*个数上限*/, const int sz /*关于哪个数字*/) { static DASHU he ; // =0 待完成 DASHU he_ ; // = he 待完成 if( sz > 0 ){ int i; for( i = 0 ; i <= gssx ; i++ ){ printf("%d*%d +" , i , sz ); //待修改 he += i * sz ; // 每次调用都从he开始累加 xunhuan( gssx - i , sz - 1 ); //待修改 he = he_ ; //恢复原来的值 } } else{ //待修改 he += gssx * sz ; //待修改 printf("%d*%d = %d" , gssx , sz , he ); putchar('\n');//<=>验算<=>记录结果 } }
因为涉及到这种新类型的运算较多,这些运算大多需要通过定义函数来重新定义,因此把这种类型的定义及函数统一写在其他模块。
修改
/*2_穷举.h*/ #ifndef QIONGJU_H #define QIONGJU_H /**************************类型定义**************************/ #include "3_大数.h" /**************************函数原型**************************/ extern void qiongju( void ); #endif // QIONGJU_H
在工程中添加“3_大数.h”源文件,最初的内容为
/*3_大数.h*/ #ifndef DASHU_H #define DASHU_H /**************************类型定义**************************/ typedef DASHU ; /**************************函数原型**************************/ #endif // DASHU_H
随手编译了一下,居然没有任何错误。(没想到这也能编译,typedef 有这种用法吗?这个编译器太善解人意了)
14.大数类型
有很多表示大数的方案。最不可取的大概是用字符串存储各位数字的方案,因为会涉及到字符与数值之间的反复转换。除了输入输出比较容易写,这种方案没有什么优势。
所以采用用一个int存储一位数的方案,WEISHU位数就构成一个数组,各位数字按从低到高顺序存储(下标为0的元素存储个位)。因为运算不能保证一定不超过WEISHU位,这种数据结构应该能表示溢出,即超过WEISHU位时的表示。这个可以通过使得存储最高位数字的元素大于等于JINZHI来表示。
这样DASHU类型可以定义为
typedef struct {
int gw_sz[WEISHU] ;//gw_sz[0]为个位,gw_sz[WEISHU-1]为最高位
}
DASHU ; //gw_sz[WEISHU-1]的值大于等于JINZHI表示溢出
之所用struct 把这个数组包装起来是因为注意到了xunhuan()函数中有这种类型数据的赋值运算。结构体类型的数据可以直接赋值,数组要完成同样的功能写起来就复杂多了。
修改
/*3_大数.h*/ #ifndef DASHU_H #define DASHU_H #include "0_问题.h" //DASHU用到了WEISHU /**************************类型定义**************************/ //gw_sz[0]为个位,gw_sz[WEISHU-1]为最高位 //gw_sz[WEISHU-1]的值大于等于JINZHI表示溢出 typedef struct { int gw_sz[WEISHU] ; } DASHU ; /**************************函数原型**************************/ #endif // DASHU_H
然后返回xunhuan()函数改写代码。
15.改写代码
static DASHU he ; // =0 待完成
现在可以补全了
static DASHU he = { { 0 } } ;
DASHU he_ ; // = he 待完成 //记录累加前的值
现在可以直接
DASHU he_ = he ; //记录累加前的值
//待修改 he = he_ ; //恢复原来的值
现在也可以直接
he = he_ ; //恢复原来的值
下面要做的事情是改写
//待修改 he += i * sz ;
这需要对DASHU这种类型定义加法运算