天天看点

c语言除法等式,填运算符(C语言)

最近看到一个有趣的老问题,填运算符。给定五个数字 比如 5 5 5 5 = 5,

要求在等号左边的四个5之间填写{+,-,*,/}使其满足等式,编写程序枚举运算符号出现的各种可能,可以求所有满足条件的情况。

如果问题再一般化一点呢?这些数字不一定相同,而且个数不定,比如有这样一个需求:

从文件读入一行数字共有N个数字,前面N-1个数字进行加减乘除四则运算,看计算结果是否等于最后一个数字,

如果等于,则记录到文件out.txt

输出所有可能的符合条件的情况结果存入文件中.

例如

in.txt

3 3 3 3

out.txt

3+3-3=3

3-3+3=3

3*3/3=3

3/3*3=3

算法分析:

决定用C语言实现这个算法,程序采用主程序子程序风格

程序需要实现如下几个功能

1.从文件中读取数据

1.1 读入的数据需要存储到方便操作的数据结构中,自然这里会想到使用数组来存储读入的前N-1个整数,

然后用一个整数result存储最后一个数字,为了使计算结果和result相比较,来确定是否符合条件

所以这里还需要定义一个整型变量存储计算结果。

经过分析可有如下数据结构:

int[MAX_SIZE] num; //存储整数

int num_len;//存储整数数组的实际长度

int result;//存储运算式的结果

int t_result;//存储运算式的实际运算结果

1.2 定义一下函数名称

read();

2.处理数据,这个子功能点是程序的核心,需要详细分析

2.1 根据num_len就可以知道数字之间需要有num_len-1个符号,分析到这里就会遇到,这些符号怎样表示的问题。要计算

加减乘除四则运算,总得需要一个结构存储表示它才行,所以这里想到用0,1,2,3四个整型常量来代表{+-*/},为什么

不直接使用{+-*/}的char类型来表示呢?因为它们的ascii码不连续(+43 *42 -45 /47),为每个位置上的字符赋值的时候

就不能用for循环直接赋值来遍历{+-*/}。确定了表示方式,就可以确定符号的存储结构了,用整型数组来表示。

经分析可有如下存储结构:

int[MAX_SIZE] sign;//存储整数中间的符号

int sign_len;//存储符号的实际个数(=num_len-1)

2.2 确定了符号的存储结构和表示方式后,我们就可以产生出一个可能的符号组合方式,比如{0000}代表数字之间运算

符号全为+号,{0001}代表数字之间前三个运算符号为+号,最后一个为-号。要列出所有符号条件的记录就需要生成所有的

可能情况,这里很容易想到用多个for循环嵌套,在每个字符位置上循环{0123}代表{+-*/}四种可能情况,但那是在知道

循环嵌套层数的情况下,也就是sign_len确定的情况下才适用。所以我们这里用递归来实现生成所有可能情况的任务

把这个函数定义为generator();

当然这里还有其他的非递归方法来产生符号数组的方法,可以人为规定符号位为4进制值的整数(逢四进一),

由num_len确定sign_len,(sign_len=num_len-1)也即是四进制数组的实际长度,每次让最后一个符号位加一,然后从后

往前扫描一遍数组,有大于等于4的就进一位,一遍扫描完成后,数组就是符号位可能情况的一种表示,

直到数组全为3结束,即是从{000000}-{333333}。

2.3 生成一个可能的情况后,我们就可以根据num[],num_len,sign[],sign_len这些数据计算出表达的结果t_result,然后与result

向比较,如果相同就写入文件,考虑到每次发现一个符合条件的记录就写入文件,频繁写入操作影响效率,这里使用一个

缓冲区来存放满足条件的记录,等到缓冲区满后在写入文件,但是把写表达式到缓冲区的时候,是不能用{0123}表示运算符的

所以需要一个char数组来存储与其对应的符号。在计算表达式结果的时候我们需要自定义一个计算函数,来计算两个

数之间的四则运算结果。

经分析可有如下存储结构:

buffer[][];//固定大小的缓冲区

char[]={'+','-','*','/'};

2.4 根据2.3的分析我们可以定义三个函数

cal(number1,oper,number2);//计算两个整数的四则运算值,参数number1为第一数,oper为操作符{0123},

参数number2为第二个数,函数返回计算结果

getResult();//计算表达式的值,返回表达式的计算结果

writerBuffer();//根据计算结果t_result和result比较确定是否把表达式写入缓冲区,并且监测缓冲区是否已满

3.把符合条件的记录写入文件

3.1 当缓冲区超过一定长度时,就需要调用writefile()函数来写文件,这里还有个情况,就是满足情况的记录很少一直没有触发

写文件操作,这时我们就需要在程序结束的时候,把缓冲区的内容写入文件。

程序源代码:

点击(此处)折叠或打开

#include

#define BLOCK_SIZE 1024

#define MAX_LEN 20

const char *filein="in.txt"; //输入文件

const char *fileout="out.txt"; //输出文件

int num[MAX_LEN]; //存储从文件读取的整数

int num_len=0; //存储整数数组的实际长度

int sign[MAX_LEN]; //存储整数中间的符号

int sign_len=0; //存储符号的实际长度

int result=0; //存储期望结果

float t_result=0; //存储实际结果

char op[]={'+','-','*','/'}; //

char buffer[BLOCK_SIZE]; //输出记录的缓冲区

int buffer_len=0; //buffer的实际长度

void read(const char * filename,int num[],int *num_len,int *result);

void generator(int sign_point);

void getResult();

float calc(float a,int p,float b);

void writeBuffer();

void convertINTtoSTR(int num,char str[],int * str_len);

void writefile(const char * filename ,const char * buffer);

void showNum(int num[],int num_len);

void showSign(int sign[],int sign_len);

void showBuffer(char * buffer);

int main()

{

read(filein,num,&num_len,&result);

showNum(num,num_len);

generator(0);

showBuffer(buffer);

buffer[buffer_len]='\0';

writefile(fileout,buffer);

getchar();

return 0;

}

void read(const char * filename,int num[],int *num_len,int *result){

int c;

int tmp=0;

FILE *fp=fopen(filename,"r+");

if(!fp){

printf("error to open file ");

return ;

}

while((c=fgetc(fp))!=EOF)

{

if(c==32)

{

num[(*num_len)++]=tmp;

tmp=0;

}else if(c>='0' && c<='9'){

tmp=tmp*10+c-'0';

}

}

(*result)=tmp;

fclose(fp);

//(*num_len)=(*num_len)+1;

sign_len=(*num_len)-1;

}

void generator(int sign_point){

//负责产生一种运算符可能的排列情况

//参数 sign_point 指示当前指针在数组sign中的位置

//程序采用递归方式,

//    如果sign_point==len则说明sign_point已经指向数组尾部,也就是说现在sign数组中的状态代表了一种可能的运算符排列情况

//    否则

//        a所指向的那个sign[sign_point]元素(运算符)有{+,-,*,/}四种可能,分别用{0,1,2,3}来表示

//            当sign[sign_point]运算符确定后,指针继续下移一位,再次调用generator函数

if(sign_point==sign_len){

showSign(sign,sign_len);

getResult();

printf("reslut=%d,t_result=%f\n",result,t_result);

writeBuffer();

}else{

for(int i=0;i<4;i++){

sign[sign_point]=i;

generator(sign_point+1);

}

}

}

void getResult(){

//为了方便计算,假设在num[0]前还有个0+,即是0+num[0]

float left=0;

int op_cur=0;

float right=num[0];

for(int i=0;i

if(sign[i]<2){

//如果当前符号为加减,就计算前面的两个数的运算值

left=calc(left,op_cur,right);

op_cur=sign[i];

right=num[i+1];

}else{

//如果当前符号为乘除,因为乘除优先级高,就计算当前符号位后面的数和right的运算值

right=calc(right,sign[i],num[i+1]);

}

}

//计算最后的结果

t_result=calc(left,op_cur,right);

}

float calc(float a,int p,float b){

//这里做除法操作时,如果b=0会产生错误

switch(p){

case 0:

return a+b;

case 1:

return a-b;

case 2:

return a*b;

case 3:

return a/b;

}

}

void writeBuffer(){

int tmp=0;

char str[4];//整数最多为4位

int str_len=0;

if((float)t_result==result){

for(int i=0;i

{

//把数字num[i]转换成字符类型

convertINTtoSTR(num[i],str,&str_len);

for(int j=str_len-1;j>=0;j--){

buffer[buffer_len++]=str[j];

}

buffer[buffer_len++]=op[sign[i]];

}

buffer[buffer_len-1]='=';

//把数字result转换成字符类型

convertINTtoSTR((int)result,str,&str_len);

for(int j=str_len-1;j>=0;j--){

buffer[buffer_len++]=str[j];

}

//添加换行符

buffer[buffer_len++]='\n';

//如果缓冲区快满了,就调用writefile()函数写入文件,并清空缓冲区

if(buffer_len>BLOCK_SIZE-50){

buffer[buffer_len]='\0';

writefile(fileout,buffer);

buffer_len=0;

}

}

}

void convertINTtoSTR(int num,char str[],int * str_len){

(*str_len)=0;

if(num<=0){

str[(*str_len)++]=0+'0';

return;

}

while(num){

str[(*str_len)++]=(num%10)+'0';

num=num/10;

}

}

void writefile(const char * filename ,const char * buffer){

FILE *fp=fopen(filename,"w");

if(!fp){

printf("error to open file");

return ;

}

//函数遇到'\0'结束

fputs(buffer,fp);

fclose(fp);

}

void showNum(int num[],int num_len){

for(int i=0;i

printf("%d ",num[i]);

}

printf("\n%d\n",result);

}

void showSign(int sign[],int sign_len){

for(int i=0;i

printf("%d ",sign[i]);

}

printf("\n");

}

void showBuffer(char * buffer){

printf("%s",buffer);

}