Chinaunix首页 | 论坛 | 博客
  • 博客访问: 155703
  • 博文数量: 17
  • 博客积分: 357
  • 博客等级: 一等列兵
  • 技术积分: 706
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-12 16:49
文章分类

全部博文(17)

文章存档

2013年(3)

2012年(14)

我的朋友

分类: C/C++

2012-09-29 20:51:06

最近看到一个有趣的老问题,填运算符。给定五个数字 比如 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()函数来写文件,这里还有个情况,就是满足情况的记录很少一直没有触发
     写文件操作,这时我们就需要在程序结束的时候,把缓冲区的内容写入文件。

程序源代码:


 

点击(此处)折叠或打开

  1. #include<stdio.h>
  2. #define BLOCK_SIZE 1024
  3. #define MAX_LEN 20

  4. /**********************全局数据区****************************/
  5. const char *filein="in.txt"; //输入文件
  6. const char *fileout="out.txt"; //输出文件

  7. int num[MAX_LEN]; //存储从文件读取的整数
  8. int num_len=0; //存储整数数组的实际长度

  9. int sign[MAX_LEN]; //存储整数中间的符号
  10. int sign_len=0; //存储符号的实际长度

  11. int result=0; //存储期望结果
  12. float t_result=0; //存储实际结果

  13. char op[]={'+','-','*','/'}; //
  14. char buffer[BLOCK_SIZE]; //输出记录的缓冲区
  15. int buffer_len=0; //buffer的实际长度



  16. /**********************全局数据区****************************/



  17. /**********************函数声明开始****************************/
  18. void read(const char * filename,int num[],int *num_len,int *result);
  19. void generator(int sign_point);
  20. void getResult();
  21. float calc(float a,int p,float b);
  22. void writeBuffer();
  23. void convertINTtoSTR(int num,char str[],int * str_len);
  24. void writefile(const char * filename ,const char * buffer);

  25. void showNum(int num[],int num_len);
  26. void showSign(int sign[],int sign_len);
  27. void showBuffer(char * buffer);
  28. /**********************函数声明结束****************************/
  29. int main()
  30. {
  31.   
  32.   read(filein,num,&num_len,&result);
  33.   showNum(num,num_len);
  34.   generator(0);
  35.   showBuffer(buffer);
  36.   buffer[buffer_len]='\0';
  37.   writefile(fileout,buffer);
  38.   getchar();
  39.   return 0;
  40. }


  41. /**********************函数定义开始****************************/


  42. /******************************************************
  43. *函数功能:从文件中读取整数
  44. *参数说明:*filename 文件名
  45. * num[] 存储整数的数组
  46.            num_len 数组长度
  47.            *result 期望结果
  48. *******************************************************/
  49. void read(const char * filename,int num[],int *num_len,int *result){
  50.     int c;
  51.     int tmp=0;
  52.     FILE *fp=fopen(filename,"r+");
  53.     if(!fp){
  54.          printf("error to open file ");
  55.          return ;
  56.     }
  57.     while((c=fgetc(fp))!=EOF)
  58.     {
  59.           if(c==32)
  60.           {
  61.                num[(*num_len)++]=tmp;
  62.                tmp=0;
  63.                
  64.           }else if(c>='0' && c<='9'){
  65.                 tmp=tmp*10+c-'0';
  66.           }
  67.     }
  68.    (*result)=tmp;
  69.     fclose(fp);
  70.     //(*num_len)=(*num_len)+1;
  71.     sign_len=(*num_len)-1;
  72. }
  73. /******************************************************
  74. *函数功能:递归产生所以可能的符号组合
  75. *参数说明:sign_point 当前符号位置指针
  76. *******************************************************/
  77. void generator(int sign_point){
  78. //负责产生一种运算符可能的排列情况
  79. //参数 sign_point 指示当前指针在数组sign中的位置
  80. //程序采用递归方式,
  81. //    如果sign_point==len则说明sign_point已经指向数组尾部,也就是说现在sign数组中的状态代表了一种可能的运算符排列情况
  82. //    否则
  83. //        a所指向的那个sign[sign_point]元素(运算符)有{+,-,*,/}四种可能,分别用{0,1,2,3}来表示
  84. //            当sign[sign_point]运算符确定后,指针继续下移一位,再次调用generator函数

  85.      if(sign_point==sign_len){
  86.                               
  87.          showSign(sign,sign_len);
  88.          getResult();
  89.          printf("reslut=%d,t_result=%f\n",result,t_result);
  90.          writeBuffer();
  91.      
  92.      }else{
  93.            for(int i=0;i<4;i++){
  94.                    sign[sign_point]=i;
  95.                    generator(sign_point+1);
  96.            }
  97.      }
  98.      
  99. }
  100. /******************************************************
  101. *函数功能:计算表达式的实际结果
  102. *******************************************************/
  103. void getResult(){
  104.      
  105.     //为了方便计算,假设在num[0]前还有个0+,即是0+num[0]
  106.     float left=0;
  107.     int op_cur=0;
  108.     float right=num[0];
  109.     
  110.     for(int i=0;i<sign_len;i++){
  111.         if(sign[i]<2){
  112.             //如果当前符号为加减,就计算前面的两个数的运算值        
  113.             left=calc(left,op_cur,right);
  114.             op_cur=sign[i];
  115.             right=num[i+1];
  116.         }else{
  117.            //如果当前符号为乘除,因为乘除优先级高,就计算当前符号位后面的数和right的运算值
  118.             right=calc(right,sign[i],num[i+1]);
  119.         
  120.         }
  121.     }
  122.     //计算最后的结果
  123.     t_result=calc(left,op_cur,right);
  124.      
  125. }
  126. /******************************************************
  127. *函数功能:计算两个数的四则运算值
  128. *******************************************************/
  129. float calc(float a,int p,float b){
  130. //这里做除法操作时,如果b=0会产生错误
  131.     switch(p){
  132.         case 0:
  133.             return a+b;
  134.         case 1:
  135.             return a-b;
  136.         case 2:
  137.             return a*b;
  138.         case 3:
  139.             return a/b;
  140.     }
  141. }
  142. /******************************************************
  143. *函数功能:把符合条件的记录写入缓冲区

  144. *******************************************************/

  145. void writeBuffer(){
  146.      int tmp=0;
  147.      char str[4];//整数最多为4位
  148.      int str_len=0;
  149.      
  150.      if((float)t_result==result){
  151.          
  152.          for(int i=0;i<num_len;i++)
  153.          {
  154.                  //把数字num[i]转换成字符类型
  155.                  convertINTtoSTR(num[i],str,&str_len);
  156.                  for(int j=str_len-1;j>=0;j--){
  157.                          buffer[buffer_len++]=str[j];
  158.                  }
  159.                 
  160.                  buffer[buffer_len++]=op[sign[i]];
  161.          }
  162.          
  163.          buffer[buffer_len-1]='=';
  164.           //把数字result转换成字符类型
  165.            convertINTtoSTR((int)result,str,&str_len);
  166.            for(int j=str_len-1;j>=0;j--){
  167.                buffer[buffer_len++]=str[j];
  168.            }
  169.           //添加换行符
  170.          buffer[buffer_len++]='\n';
  171.          
  172.          //如果缓冲区快满了,就调用writefile()函数写入文件,并清空缓冲区
  173.          if(buffer_len>BLOCK_SIZE-50){
  174.                  buffer[buffer_len]='\0';
  175.                  writefile(fileout,buffer);
  176.                  buffer_len=0;
  177.          }
  178.      }
  179.      
  180. }

  181. /******************************************************
  182. *函数功能:把整数转换为字符数组 (倒序存储的)
  183. *参数说明:num 要转换的整数
  184. * str 字符数组
  185.            str_len 转换后的字符数组长度
  186. *******************************************************/

  187. void convertINTtoSTR(int num,char str[],int * str_len){
  188.      
  189.      (*str_len)=0;
  190.      if(num<=0){
  191.         str[(*str_len)++]=0+'0';
  192.         return;
  193.      }
  194.      while(num){
  195.                 str[(*str_len)++]=(num%10)+'0';
  196.                 num=num/10;
  197.          }
  198.          
  199. }
  200. /******************************************************
  201. *函数功能:写缓冲区内容到文件
  202. *******************************************************/
  203. void writefile(const char * filename ,const char * buffer){
  204.      
  205.      FILE *fp=fopen(filename,"w");
  206.      
  207.      if(!fp){
  208.      printf("error to open file");
  209.      return ;
  210.      }
  211.      //函数遇到'\0'结束
  212.      fputs(buffer,fp);
  213.      
  214.      fclose(fp);
  215. }


  216. /**********************函数定义结束****************************/



  217. /****************辅助函数,用于输出数据区内容*****************/

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

  219.      for(int i=0;i<num_len;i++){
  220.              printf("%d ",num[i]);
  221.      }
  222.      printf("\n%d\n",result);
  223. }

  224. void showSign(int sign[],int sign_len){
  225.      for(int i=0;i<sign_len;i++){
  226.              printf("%d ",sign[i]);
  227.      }
  228.      printf("\n");
  229. }

  230. void showBuffer(char * buffer){
  231.      
  232.      printf("%s",buffer);
  233. }

  234. /****************辅助函数,用于输出数据内容*****************/


 

阅读(3825) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~