代码意识流——花朵数问题(六)
本文前一部分的链接
http://www.cnblogs.com/KBTiller/archive/2011/06/03/2072122.html
16.检讨
在xunhuan()函数定义中有些代码是为测试用的,并非真正需要的程序代码。把这些代码和已经写好的程序代码放在一起犹如把墨水滴到了稿纸上,非常不妥。应该用某种方式把测试代码和实际写好的代码区分开来,这样代码更干净。
修改
static void xunhuan( const int gssx /*个数上限*/,
const int sz /*关于哪个数字*/)
{
static DASHU he = { { 0 } } ;//static DASHU he ; // =0 待完成
DASHU he_ = he ; //记录累加前的值 //记录累加前的值
if( sz > 0 ){
int i;
for( i = 0 ; i <= gssx ; i++ ){
#ifdef CESHI
printf("%d*%d +" , i , sz );
{
DASHU temp ;
ds_fuzhi ( &temp , i * sz ) ;
ds_jiaru ( &he , &temp ) ;
//void ds_jiaru ( DASHU * const , const DASHU * const ) ; // 每次调用都从he开始累加
}
#endif
xunhuan( gssx - i , sz - 1 );
he = he_ ; //恢复原来的值
}
}
else{
#ifdef CESHI
{
DASHU temp ;
ds_fuzhi ( &temp , gssx * sz ) ;
ds_jiaru ( &he , &temp ) ; //待修改 he += gssx * sz ;
}
//待修改 printf("%d*%d = %d" , gssx , sz , he );
printf("%d*%d = " , gssx , sz );
ds_shuchu( &he ) ;//extern void ds_shuchu( const DASHU * const ) ;
#endif
//<=>验算<=>记录结果
}
}
17.空间换时间
各个数字的N次方的整数倍在计算中要反复用到,如果每次都重新计算一次可能将非常费时。为节省运算时间,应该避免多次重复的运算。为此可在事先一次性计算完各个数字N次方的整数倍并存储起来,以后用到时就不必重新计算了。这里用数组存储各个数字N次方的整数倍,数组的结构为
0*(JINZHI-1)^N 1*(JINZHI-1)^N ……WEISHU*(JINZHI-1)^N
0*(JINZHI-2)^N 1*(JINZHI-2)^N ……WEISHU*(JINZHI-1)^N
……
0*(1)^N 1*(1)^N ……WEISHU*(1)^N
这个数据只在xunhuan()中使用,因此在"2_穷举.c"中描述其类型就可以了:
typedef DASHU SHUJUBIAO[JINZHI-1][WEISHU];
考虑到这种数组比较大(有继续求解21位以上花朵数的想法),所以不作为局部变量定义,只在函数内定义一个指向这种数据的指针,在程序执行时申请内存。
将这个指针定义在何处颇感踌躇。这个数据只在xunhuan()内部使用,似乎应该定义在xunhuan()内,但是在xunhuan()必须写一个讨厌的条件判断语句以便在第一次调用的时候建立相应的数据。如果定义在qiongju(),则xunhuan()必须增加一个参数。最后的选择是在qiongju()内部定义这个指针,因为那里比较空(一个不是理由的理由)。
在递归调用前建立这个数据表:
jianli_sjb ( & p_sjb ); //建立数据表
在写这个函数的函数定义时需要在“2_穷举.h”中增加预处理命令 #include <stdlib.h>
修改
/*2_穷举.h*/
#ifndef QIONGJU_H
#define QIONGJU_H
/**************************类型定义**************************/
#include "3_大数.h"
/**************************函数原型**************************/
extern void qiongju( void );
#include <stdlib.h> //malloc()
#endif // QIONGJU_H
期间,在"3_大数.h" 、"3_大数.c" 添加了ds_chengyi()的原型和定义
修改
/*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 ;
/**************************函数原型**************************/
extern void ds_fuzhi ( DASHU * const , const int ) ;
extern void ds_shuchu( const DASHU * const ) ;
extern void ds_jiaru ( DASHU * const , const DASHU * const ) ;
extern void ds_chengyi( DASHU * const , const int );
#endif // DASHU_H
修改
/*3_大数.c*/
#include "3_大数.h"
static void ds_jinwei ( DASHU * const );
//* p_ds *= cs
extern void ds_chengyi( DASHU * const p_ds , const int cs )
{
int * t = p_ds->gw_sz ;
while( t < p_ds->gw_sz + WEISHU ){
*t++ *=cs ;
}
ds_jinwei ( p_ds );
}
//进位
static void ds_jinwei ( DASHU * const p_ds )
{
int * t = p_ds->gw_sz , * const w = t + WEISHU - 1 ;
while( t < w ){
*( t + 1 ) += * t / JINZHI ;
* t ++ %= JINZHI ;
}
}
// *p_he += *p_js
extern void ds_jiaru ( DASHU * const p_he , const DASHU * const p_js )
{
int *he_t = p_he->gw_sz , * const he_w = he_t + WEISHU - 1 ;
const int *js_t = p_js->gw_sz ;
while( he_t < he_w ){
*he_t++ += *js_t++ ;
}
ds_jinwei ( p_he ); // static void ds_jinwei ( DASHU * const );
}
extern void ds_shuchu( const DASHU * const p_ds )
{
int * const t = p_ds->gw_sz , *w = t + WEISHU - 1 ;
while( w > t && *w == 0 ){ //高位0不输出
w--;
}
while( w > t ){
printf( "%d" , * w -- );
}
printf( "%d\n" , * t ) ;
}
//在*p_ds中写入n
extern void ds_fuzhi ( DASHU * const p_ds , const int n )
{
p_ds->gw_sz[0] = n ;
//可能有进位,同时把高位写为0
{
int *t = p_ds->gw_sz , //指向 p_ds->gw_sz[0]
*w = t + WEISHU - 1 ; //指向 p_ds->gw_sz[WEISHU-1]
while( t < w ){
*( t + 1 ) = * t / JINZHI ;
* t ++ %= JINZHI ;
}
}
}
“2_穷举.c”中完成的代码及测试
/*2_穷举.c*/
#include "2_穷举.h"
#include "0_问题.h"
typedef DASHU SHUJUBIAO[JINZHI-1][WEISHU + 1];
//0*(JINZHI-1)^N 1*(JINZHI-1)^N ……WEISHU*(JINZHI-1)^N
//0*(JINZHI-2)^N 1*(JINZHI-2)^N ……WEISHU*(JINZHI-1)^N
//……
//0*(1)^N 1*(1)^N ……WEISHU*(1)^N
static void xunhuan(const int ,const int ) ;
static void jianli_sjb ( SHUJUBIAO * * ); //建立数据表
//建立数据表
static void jianli_sjb ( SHUJUBIAO * * p_p_sjb )
{
if( (* p_p_sjb = malloc(sizeof(SHUJUBIAO)) ) == NULL ){ //#include <stdlib.h>
exit(!EXIT_SUCCESS);
}
{
int i , j ;
for( i = 0 ; i < JINZHI - 1 ; i ++){
ds_fuzhi( *( * * p_p_sjb + i ) + 0 , 0 );//第一列为0
ds_fuzhi( *( * * p_p_sjb + i ) + 1 , 1 );//第二列先赋值为1
for( j = 0 ; j < N ; j ++ ){ //求N次幂
ds_chengyi( *( * * p_p_sjb + i ) + 1 , JINZHI - 1 - i );
}
for( j = 2 ; j <= WEISHU ; j ++ ){
(*( * * p_p_sjb + i ))[j] = (*( * * p_p_sjb + i ))[j-1] ;
ds_jiaru ( *( * * p_p_sjb + i ) + j , *( * * p_p_sjb + i ) + 1 ) ;
}
#ifdef CESHI
for( j = 0 ; j <= WEISHU ; j ++ ){
ds_shuchu( *( * * p_p_sjb + i ) + j );
}
#endif
}
}
#ifdef CESHI
system("PAUSE");
exit(0);
#endif
}
extern void qiongju( void )
{
SHUJUBIAO *p_sjb = NULL ;
jianli_sjb ( & p_sjb ); //建立数据表
xunhuan( WEISHU , JINZHI-1 ) ;
}
static void xunhuan( const int gssx /*个数上限*/,
const int sz /*关于哪个数字*/)
{
static DASHU he = { { 0 } } ;//static DASHU he ; // =0 待完成
DASHU he_ = he ; //记录累加前的值 //记录累加前的值
if( sz > 0 ){
int i;
for( i = 0 ; i <= gssx ; i++ ){
#ifdef CESHI
printf("%d*%d +" , i , sz );
{
DASHU temp ;
ds_fuzhi ( &temp , i * sz ) ;
ds_jiaru ( &he , &temp ) ;
//void ds_jiaru ( DASHU * const , const DASHU * const ) ; // 每次调用都从he开始累加
}
#endif
xunhuan( gssx - i , sz - 1 );
he = he_ ; //恢复原来的值
}
}
else{
#ifdef CESHI
{
DASHU temp ;
ds_fuzhi ( &temp , gssx * sz ) ;
ds_jiaru ( &he , &temp ) ; //待修改 he += gssx * sz ;
}
//待修改 printf("%d*%d = %d" , gssx , sz , he );
printf("%d*%d = " , gssx , sz );
ds_shuchu( &he ) ;//extern void ds_shuchu( const DASHU * const ) ;
#endif
//<=>验算<=>记录结果
}
}
编译运行,输出的前4行为
0
729
758
787
很幸运,这里发现了一个BUG。BUG的位置在ds_jiaru()中
he_w = he_t + WEISHU - 1 ; 应为 he_w = he_t + WEISHU ;
修改后重新编译运行,结果正确。
之后撤销jianli_sjb()中的测试代码
//建立数据表
static void jianli_sjb ( SHUJUBIAO * * p_p_sjb )
{
if( (* p_p_sjb = malloc(sizeof(SHUJUBIAO)) ) == NULL ){ //#include <stdlib.h>
exit(!EXIT_SUCCESS);
}
{
int i , j ;
for( i = 0 ; i < JINZHI - 1 ; i ++){
ds_fuzhi( *( * * p_p_sjb + i ) + 0 , 0 );//第一列为0
ds_fuzhi( *( * * p_p_sjb + i ) + 1 , 1 );//第二列先赋值为1
for( j = 0 ; j < N ; j ++ ){ //求N次幂
ds_chengyi( *( * * p_p_sjb + i ) + 1 , JINZHI - 1 - i );
}
for( j = 2 ; j <= WEISHU ; j ++ ){
(*( * * p_p_sjb + i ))[j] = (*( * * p_p_sjb + i ))[j-1] ;
ds_jiaru ( *( * * p_p_sjb + i ) + j , *( * * p_p_sjb + i ) + 1 ) ;
}
#if 0 //#ifdef CESHI
for( j = 0 ; j <= WEISHU ; j ++ ){
ds_shuchu( *( * * p_p_sjb + i ) + j );
}
#endif
}
}
#if 0 //#ifdef CESHI
system("PAUSE");
exit(0);
#endif
}
18.修改qiongju()
添加free()
xunhuan()增加了一个参数
extern void qiongju( void )
{
SHUJUBIAO *p_sjb = NULL ;
jianli_sjb ( & p_sjb ); //建立数据表
xunhuan( WEISHU , JINZHI-1 , *p_sjb ) ;
free ( p_sjb );
}
19.修改xunhuan()
修改xunhuan()函数原型
static void xunhuan(const int , const int , DASHU (const *)[WEISHU + 1] ) ;
修改xunhuan()函数定义
static void xunhuan( const int gssx /*个数上限*/,
const int sz /*关于哪个数字*/,
DASHU (*p_hang)[WEISHU + 1] /*指向一行数据*/)
{
static DASHU he = { { 0 } } ;//static DASHU he ; // =0 待完成
DASHU he_ = he ; //记录累加前的值 //记录累加前的值
if( sz > 0 ){
int i;
for( i = 0 ; i <= gssx ; i++ ){
ds_jiaru ( &he , *p_hang + i ) ;
#ifdef CESHI
printf("%d*%d +" , i , sz );
#endif
xunhuan( gssx - i , sz - 1 , p_hang + 1 );
he = he_ ; //恢复原来的值
}
}
else{
#ifdef CESHI
printf("%d*%d = " , gssx , sz );
ds_shuchu( &he ) ;//extern void ds_shuchu( const DASHU * const ) ;
#endif
//<=>验算<=>记录结果
}
}
xunhuan()函数从数据表中的某一行中选取某个数字N次方的某个倍数求和,然后递归调用从下一行中选取下一个数字N次方的某个倍数求和。
编译运行,通过测试。
之后测试了一下21位数时跑一遍程序的时间,不到十秒,应该能满足性能要求。