小学生:你们是魔鬼吗?

一、项目说明

魔鬼成员:谢文柱  刘丽昀

 魔鬼地址: https://github.com/onetrue/Arithemetic

 实现一个自动生成小学四则运算题目的命令行程序。

     需求:

  1. 使用 -n 参数控制生成题目的个数,例如

   Myapp.exe -n 10

   将生成10个题目。

  2. 使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,例如

   Myapp.exe -r 10

   将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定,否则程序报错并给出帮助信息。

  3. 生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1 − e2的子表达式,那么e1 ≥ e2

  4. 生成的题目中如果存在形如e1 ÷ e2的子表达式,那么其结果应是真分数

  5. 每道题目中出现的运算符个数不超过3个。

  6. 程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+×左右的算术表达式变换为同一道题目

  例如,23 + 45 = 和45 + 23 = 是重复的题 目,6 × 8 = 和8 × 6 = 也是重复的题目。3+(2+1)1+2+3这两个题目是重复的,

  由于+是左结合的,1+2+3等价于(1+2)+3,也就是3+(1+2),也就是3+(2+1)。但是1+2+33+2+1是不重复的两道题,因为

  1+2+3等价于(1+2)+3,而3+2+1等价于(3+2)+1,它们之间不能通过有限次交换变成同一个题目。

  生成的题目存入执行程序的当前目录下的Exercises.txt文件,格式如下:

     1. 四则运算题目1

     2. 四则运算题目2

  ……

   其中真分数在输入输出时采用如下格式,真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8。

7. 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件,格式如下:

     1. 答案1

     2. 答案2

   特别的,真分数的运算如下例所示:1/6 + 1/8 = 7/24。

  8. 程序应能支持一万道题目的生成。

二、PSP

PSP2.1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

Planning

计划

 30

 30

· Estimate

· 估计这个任务需要多少时间

 10

 30

Development

开发

 600

 900

· Analysis

· 需求分析 (包括学习新技术)

 60

 120

· Design Spec

· 生成设计文档

 10

 10

· Design Review

· 设计复审 (和同事审核设计文档)

 60

 60

· Coding Standard

· 代码规范 (为目前的开发制定合适的规范)

 30

 30

· Design

· 具体设计

 30

 60

· Coding

· 具体编码

 1200

 900

· Code Review

· 代码复审

 60

 60

· Test

· 测试(自我测试,修改代码,提交修改)

 30

 30

Reporting

报告

 60

 60

· Test Report

· 测试报告

 20

 10

· Size Measurement

· 计算工作量

 10

 20

· Postmortem & Process Improvement Plan

· 事后总结, 并提出过程改进计划

 30

 60

合计

 

 

 

三、设计思路

  1. 要设计一个专门的结构体存储整个算式部分,结构体包含所有运算符和所有操作数。还有一个结构体专门存储答案。其中所有数都是分数形式表示,用一个包含两个整形的一位数组表示一个分数。
  2. 将整个软件逐步分成一个个很小的部分,逐一实现。先将生成器分成运算生成和结果计算两部分,运算生成再分为运算符生成和操作数生成,两个都用随机数随机生成。计算结果部分用循环判断运算符优先级并经行不同运算。
  3. 还需要一个输出表达函数将一个运算结构体转换为字符串,该函数需要判断每个分数是表示整数、真分数还是假分数,并转换为正确形式输出。
  4. 最后通过fopen等函数将表达式和结果分别输入到不同文件中。

四、具体代码实现

  1. 设计两个结构体专门存储整个算式和答案。
    struct Problem//四则运算问题结构体
    {
        int numoffuhao;
        int numofshu;
        char fuhao[3];
        int shu[4][2];
    };
    
    struct Answer//四则运算答案结构体
    {
        int fenzi;
        int fenmu;
    };
    

      

  2. 生成算式函数
    void createProblem(Problem *p,int i,int r)//随机生成一个问题
    {
        int fuhaoshu;//先随机确定符号数
        fuhaoshu=rand()%3;
        fuhaoshu+=1;
        p[i].numoffuhao=fuhaoshu;
        p[i].numofshu=fuhaoshu+1;
        int j;
        int yueshu;
        for(j=0;j<fuhaoshu;j++)//循环生成所有随机符号
        {
            p[i].fuhao[j]=createFuhao();
        }
        for(j=0;j<fuhaoshu+1;j++)//循环生成所有随机数
        {
            int k;
            k=rand()%2;
            if(k==0)
            {
                p[i].shu[j][0]=createSuijishu(r);
                p[i].shu[j][1]=1;
            }else
            {
                p[i].shu[j][0]=createSuijishu(r);
                p[i].shu[j][1]=createSuijishu(r);
                yueshu=gcd(p[i].shu[j][0],p[i].shu[j][1]);
                p[i].shu[j][0]/=yueshu;
                p[i].shu[j][1]/=yueshu;
            }
        }
    }
    

      生成具体符号和具体操作数的函数,以及计算最大公约数的函数

    char createFuhao()//随机生成运算符
    {
        int i;
        i=rand()%4;
        if(i==0) return '+';
        if(i==1) return '-';
        if(i==2) return '*';
        if(i==3) return '/';
    
    }
    
    int createSuijishu(int r)//随机生成不大于r的随机数
    {
        int i;
        do
            i=rand()%r;
            while(i==0);
        return i;
    }
    
    int gcd(int a,int b)
    {
        if(a>b)
            return (b>0)?gcd(b,a%b):a;
        return (a>0)?gcd(a,b%a):b;
    }
    

      

  3. 答案计算函数
    void countAnswer(Problem* p,int i,Answer* a)//计算问题的答案
    {
        int fuhaoshu;
        fuhaoshu=p[i].numoffuhao;
        char fuhaos[3];
        int shus[4][2];
        int temAns[2];
        int j,k,l;
        int flag;//判断符号优先级
    
        for(j=0;j<fuhaoshu;j++)//获取符号数组
        {
            fuhaos[j]=p[i].fuhao[j];
        }
        for(j=0;j<fuhaoshu+1;j++)//获取数字数组
        {
            shus[j][0]=p[i].shu[j][0];
            shus[j][1]=p[i].shu[j][1];
        }
        for(j=0;j<fuhaoshu;j++)//循环进行一次运算
        {
            flag=0;
            for(k=0;k<fuhaoshu-j;k++)//判断哪个运算优先
            {
                if(fuhaos[k]=='*'||fuhaos[k]=='/')
                {
                    flag=k;
                    break;
                }
            }
            jisuan(fuhaos[flag],shus[flag],shus[flag+1],temAns);
            shus[flag][0]=temAns[0];
            shus[flag][1]=temAns[1];
            for(k=flag+1;k<fuhaoshu-j;k++)
            {
                fuhaos[k-1]=fuhaos[k];
                shus[k][0]=shus[k+1][0];
                shus[k][1]=shus[k+1][1];
            }
    
        }
        a[i].fenzi=shus[0][0];
        a[i].fenmu=shus[0][1];
    }
    

      

  4. 算式结构体转换成字符串表达函数
    void printProblem(Problem* p,int i,char *s)//将问题编程字符串形式
    {
        char c[50];
        char tc[10];
        int j;
        for(j=0;j<p[i].numoffuhao;j++)
        {
            if(p[i].shu[j][1]==1)
            {
                itoa(p[i].shu[j][0],tc,10);
                strcat(c,tc);
            }else
            {
                if(p[i].shu[j][0]<p[i].shu[j][1])
                {
                    itoa(p[i].shu[j][0],tc,10);
                    strcat(c,tc);
                    strcat(c,"/");
                    itoa(p[i].shu[j][1],tc,10);
                    strcat(c,tc);
                }else
                {
                    int zhengshu,zhenfenzi,zhenfenmu;
                    zhenfenmu=p[i].shu[j][1];
                    zhengshu=p[i].shu[j][0]/zhenfenmu;
                    zhenfenzi=p[i].shu[j][0]%zhenfenmu;
                    itoa(zhengshu,tc,10);
                    strcat(c,tc);
                    strcat(c,"'");
                    itoa(zhenfenzi,tc,10);
                    strcat(c,tc);
                    strcat(c,"/");
                    itoa(zhenfenmu,tc,10);
                    strcat(c,tc);
                }
            }
            strcat(c," ");
            tc[0]=p[i].fuhao[j];
            tc[1]='\0';
            strcat(c,tc);
            strcat(c," ");
        }
        if(p[i].shu[j][1]==1)
            {
                itoa(p[i].shu[j][0],tc,10);
                strcat(c,tc);
            }else
            {
                if(p[i].shu[j][0]<p[i].shu[j][1])
                {
                    itoa(p[i].shu[j][0],tc,10);
                    strcat(c,tc);
                    strcat(c,"/");
                    itoa(p[i].shu[j][1],tc,10);
                    strcat(c,tc);
                }else
                {
                    int zhengshu,zhenfenzi,zhenfenmu;
                    zhenfenmu=p[i].shu[j][1];
                    zhengshu=p[i].shu[j][0]/zhenfenmu;
                    zhenfenzi=p[i].shu[j][0]%zhenfenmu;
                    itoa(zhengshu,tc,10);
                    strcat(c,tc);
                    strcat(c,"'");
                    itoa(zhenfenzi,tc,10);
                    strcat(c,tc);
                    strcat(c,"/");
                    itoa(zhenfenmu,tc,10);
                    strcat(c,tc);
                }
            }
        strcat(c," = ");
        strcpy(s,c);
    }
    

      

  5. 主函数
    int main(int argc,char *argv[])
    {
        srand(time(NULL));
        FILE *fp1,*fp2;
        if((fp1=fopen("F:\\university\\大三第一学期\\课程\\软件工程\\第二次作业\\ArithmeticProgram\\ArithmeticProgram\\bin\\Debug\\Exercises.txt","w"))==NULL)
        {printf("can not open file\n");
          exit(0);
        }
        if((fp2=fopen("F:\\university\\大三第一学期\\课程\\软件工程\\第二次作业\\ArithmeticProgram\\ArithmeticProgram\\bin\\Debug\\Answers.txt","w"))==NULL)
        {printf("can not open file\n");
         exit(0);
         }
        int n,r;
        if((strcmp(argv[1],"-n")==0)&&(strcmp(argv[3],"-r")==0))
        {
            n=atoi(argv[2]);
            r=atoi(argv[4]);
        }
        Problem *problems = (Problem*)malloc(n*sizeof(Problem));//创建一个问题数组,保存所有的问题
        Answer *answers = (Answer*)malloc(n*sizeof(Answer));//创建一个答案数组,保存所有答案
        int i;
        for(i=0;i<n;i++)//循环生成题目
        {
            char s[50];
            char s1[50]="";
            do
            {
                createProblem(problems,i,r);//生成一个问题,存在问题结构体中
                countAnswer(problems,i,answers);//计算这个问题的答案,存在答案结构体中
            }while(answers[i].fenzi<=0||answers[i].fenmu<=0);
            printProblem(problems,i,s);//将问题转换为字符串
    
            char tc[10];
           if(answers[i].fenmu==1)
            {
                itoa(answers[i].fenzi,tc,10);
                strcat(s1,tc);
            }else
            {
                if(answers[i].fenmu>answers[i].fenzi)
                {
                    itoa(answers[i].fenzi,tc,10);
                    strcat(s1,tc);
                    strcat(s1,"/");
                    itoa(answers[i].fenmu,tc,10);
                    strcat(s1,tc);
                }else
                {
                    int zhengshu,zhenfenzi,zhenfenmu;
                    zhenfenmu=answers[i].fenmu;
                    zhengshu=answers[i].fenzi/answers[i].fenmu;
                    zhenfenzi=answers[i].fenzi%answers[i].fenmu;
                    itoa(zhengshu,tc,10);
                    strcat(s1,tc);
                    strcat(s1,"'");
                    itoa(zhenfenzi,tc,10);
                    strcat(s1,tc);
                    strcat(s1,"/");
                    itoa(zhenfenmu,tc,10);
                    strcat(s1,tc);
                }
            }
    
    
    
    
    
            printf("%d\t%s%s\n",i+1,s,s1);
            char bianhao[10];
            itoa(i+1,bianhao,10);
            fputs(bianhao,fp1);
            fputs("\t",fp1);
            fputs(s,fp1);
            fputs("\n",fp1);
            fputs(bianhao,fp2);
            fputs("\t",fp2);
            fputs(s1,fp2);
            fputs("\n",fp2);
    
            strcpy(s1,"");
    
        }
        return 0;
    }
    

      

五、测试结果

输入命令

命令行结果

 

题目文件Exercise.txt

答案文件Answers.txt

六、项目小结

  1. 本次结对编程,我们采用先各自收集资料,思考方法,再一起线下讨论的方法。我们先根据题目自己独立思考编写思路,并上网收集资料,在线下讨论的时候,我们互相交流了思路,一开始谢文柱的思路是算术题是本质,操作数和运算符是核心,操作数本质上都是假分数,所以可以用特殊结构体存储算术题,并对结构体进行特殊运算和表达。刘丽昀的思路是表达式是核心,先判断数的类型(整数或分数),再根据不同的数的类型去实现表达式并且计算。最后进过激烈并深入的讨论,采用结构体实现会比较简洁,代码更容易实现,逻辑比较清晰严谨。在这一次讨论中我们能够获得多种实现方案,懂得在不同方案中选择更有优势的一个,并且在选择中学习对方思路中的优点,避开不足之处。
  2. 分工方面,谢文柱负责逻辑结构的整理,刘丽昀负责整体结构的梳理与调整。具体来讲就是刘丽昀负责编写软件的整体结构,并对其进行细分,而谢文柱这实现个别详细步骤的具体实现。在分工实现方面,我们懂得了,从另一个角度更能看出自己看不到的问题,从而能更快地解决问题。
  3. 在完成整个项目后,回顾整个项目,早期各自想思路时花费时间较多,也比较困难,在讨论之后的实现过程效率较单人思考会更高。首次合作,配合度也还不错,分工明确,效率高,但是由于单人思考时间花费的多了一些,导致后面的交流讨论不是很够,希望以后可以更合理的分配时间。

 

posted @ 2018-09-30 12:00  ONETRUE  阅读(231)  评论(1编辑  收藏  举报