结对项目--自动生成小学四则运算题目

这个作业属于哪个课程 [https://edu.cnblogs.com/campus/gdgy/Networkengineering1834]
这个作业要求在哪里 [https://edu.cnblogs.com/campus/gdgy/Networkengineering1834/homework/11148]
这个作业的目标 队友互相协作生成设计文档,实现代码编写和代码复审,最终实现一个可以自动生成小学四则运算题目

结对成员:

- 郑旭朋(3118005301)

- 卢进兴(3118005286)

一.github仓库

https://github.com/JJDD-ljx/Pair-Project

二.PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 10 10
· Estimate · 估计这个任务需要多少时间 10 10
Development 开发 440 1080
· Analysis · 需求分析 (包括学习新技术) 120 180
· Design Spec · 生成设计文档 20 10
· Design Review · 设计复审 10 10
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 10 10
· Design · 具体设计 10 10
· Coding · 具体编码 180 560
· Code Review · 代码复审 30 180
· Test · 测试(自我测试,修改代码,提交修改) 60 120
Reporting 报告 110 110
· Test Report · 测试报告 60 60
· Size Measurement · 计算工作量 20 20
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 30 30
Total · 合计 560 1200

三.设计与实现的过程

大体思路

  • 键盘录入题目数值的范围range和生成题目的数量questionNum

  • 用for循环来控制题目数量for(i=0;i<questionNum;i++)每次循环产生一条题目

    • 题目运算符+-/的个数随机生成(1-3个),用字符数组来装+-×÷
      char[] operator = {'+','-','×','÷'};
      int j(1/2/3) = r.nextInt(4);
      这样就随机产生运算符了,如j1随机得0时,operator[j1]含义是第一个运算符为+ ,当j3随机得3时,operator[j3]含义就是第三个运算符为÷

    • 每条题目的数值最多4个,随机4个分子和4个分母出来,然后通过函数Fraction(分子,分母)得到分数num1/2/3/4

    • 由于每次循环的运算符个数不同,开始分类讨论

      • 当运算符个数为1个时
        - 用字符串来表示式子 str="num1+operator[j1]+num2=" 然后输出题目 println("题目i:"+str)
        - 使用函数Calculator(str)得出题目的答案answer {由于+-×÷是字符,不是运算符不能直接计算,所以要新建函数Calculator来读取字符串并计算出结果}
        - 保存题目和答案到txt文件
      • 当运算符个数为2个时,同上,只有str="num1+operator[j1]+num2+operator[j2]+num3" 这里不同
      • 当运算符个数为3个时,同上,只有str="num1+operator[j1]+num2+operator[j2]+num3+operator[j3]+num4" 这里不同
  • 结束并返回for循环开始下一个题目生成

详细代码思路

- ①先创建一个分数类型Fraction:Fraction a = new Fraction(分子,分母)

  新建分数如Fraction a = new Fraction(x,y),输入(x,y)=(3,4)将得到 a = 3/4

  如果输入(5,1)将得到整数 a = 5,这样分数类型也包含整数了

  如果输入假分数(8,5)将得到带分数 a = 1'3/5
/*下面的代码是简略版,只提供思路*/
public class Fraction {
    public int molecule;     //分子
    public int denominator;   //分母
    
    //约分处理
    public void reduction(){
        int x=gcd(molecule,denominator);   //最大公因数
        molecule /=x;       //分子除公因数得最简分子
        denominator/=x;     //分母除公因数得最简分母        
    }
    
    //返回分数形式
    @Override
    public String toString() {
        if(isFraction()){      //是分数就返回真分数形式       
            if(molecule >denominator){          //带分数
                return (molecule /denominator)+"’"+ molecule %denominator+"/"+denominator;  
            }
            return molecule +"/"+denominator;   //真分数
        }
        else return ""+ molecule /denominator;  //整数    }

    //求最大公因数
    private static int gcd(int a,int b) {
        return (b==0)?a:gcd(b,a%b);
    }

- ②创建计算器类Calculator

  计算器类可以识别字符串并转化成数值和运算符,然后计算结果

  如 String str = "3/10 × 5 + 6 ÷ 1'3/4 =";
     answer = calculator(str);//计算字符串答案=12,不能直接answer = str得出答案,因为str的+-×÷是字符不是运算符不能直接计算
/*下面的代码是简略版,只提供思路*/
public class Calculator {
            final public static Calculator calculator = new Calculator();
            private Stack<Fraction> fractionStack = null;//数字栈,用于存储表达式中的数值
            private Stack<Character> operationStack = null;//符号栈,用于存储运算符和括号和带分数号
            
            //解析并计算四则运算表达式,返回计算结果
            public Fraction calculate(String numStr) {
                fractionStack = new Stack<Fraction>();// 初始化栈
                operationStack = new Stack<Character>();
                StringBuffer nowFractionNum = new StringBuffer();// 用于缓存数字,因为数字可能是多位的
                for (int i = 0; i < numStr.length(); i++) {// 从表达式的第一个字符开始处理
                    char nowChar = numStr.charAt(i); // 获取一个字符
                    if (isNumber(nowChar)) { // 若当前字符是数字
                        nowFractionNum.append(nowChar); // 加入到数字缓存中
                    } else { // 非数字的情况
                        String checkFractionNum = nowFractionNum.toString(); // 将数字缓存转为字符串
                        if (!checkFractionNum.isEmpty()) {
                            int num = Integer.parseInt(checkFractionNum); // 将数字字符串转为长整型数
                            fractionStack.push(new Fraction(num)); // 将数字压栈
                            nowFractionNum = new StringBuffer();  // 重置数字缓存
                        }
                         // 取出栈尾运算符进行相应运算,并把结果压栈用于下一次运算
                         switch (operationStack.pop()) {
                                case '’': fractionStack.push(Fraction.with(a , b));//处理带分数
                                    break;
                                case '+': fractionStack.push(Fraction.add(a , b));//处理普通运算符
                                    break;
                                case '-': fractionStack.push(Fraction.sub(a , b));
                                    break;
                                case '×': fractionStack.push(Fraction.mul(a ,  b));
                                    break;
                                case '/':
                                case '÷': fractionStack.push(Fraction.div(a ,  b));
                                    break;
                                default:
                                    break; 
                         } 
                        if (nowChar != '=') {
                            operationStack.push(nowChar); // 符号入栈 
                        } } }
                return fractionStack.pop();  // 返回计算结果       }         

- ③创建题目和答案txt文件

  下面代码是题目txt文件,答案文件代码是类似的
  QuestionWrite qw = new QuestionWrite();
  qw.Write(question);//这样就直接存入txt了
  AnswerWrite aw = new AnswerWrite();
  aw.Write(answer);   
public class QuestionWrite {
   static String all="";
   static int cnt=1;
   public static void save(){
       try{
           File file = new File("Exercises.txt");
           if(!file.exists()){
               file.createNewFile();
           }
           FileWriter fileWriter = new FileWriter(file.getAbsoluteFile());
           BufferedWriter bw = new BufferedWriter(fileWriter);
           bw.write(all);
           bw.close();
       } catch (IOException e) {
           e.printStackTrace();
       }
   }
   public void Write(String question){
       all+=(cnt+". "+question+"\n");
       cnt++;
   }
}

- ④main函数

/*下面的代码是简略版,只提供思路*/
 System.out.println("请输入题目中数值的范围");
 int range = scanner.nextInt();
 System.out.println("请输入你想生成的题目的数量");
 int questionNum = scanner.nextInt();

 //用for循环来出题目
 for(int i = 0;i<questionNum;i++){
      Random r = new Random();

      /* 运算符 */      
      int operatorNum = r.nextInt(3) + 1;   //随机生成运算符个数,每次1~3个
      char[] operator = {'+','-','×','÷'};  //用字符来表示运算符+-*/
      //用operator[j]来随机产生+-×÷
      int j1 = r.nextInt(4);//如果j1是1,那第一个运算符operator[j1]是-
      int j2 = r.nextInt(4);
      int j3 = r.nextInt(4);
  
      /* 数值 */
      //至多4个数值,建立4个分数的分子分母(整数可以看成分母为1的分数)
      int molecule1 =  r.nextInt(range);         //第1个分子
      int molecule2 =  r.nextInt(range);         
      int molecule3 =  r.nextInt(range);         
      int molecule4 =  r.nextInt(range);         
      int denominator1 = r.nextInt(range)+1;     //第1个分母,+1是为了防止为0
      int denominator2 = r.nextInt(range)+1;     
      int denominator3 = r.nextInt(range)+1;     
      int denominator4 = r.nextInt(range)+1;  
      //由分子分母生成分数(真分数,带分数,整数)
      Fraction num1 = new Fraction(molecule1,denominator1);
      Fraction num2 = new Fraction(molecule2,denominator2);
      Fraction num3 = new Fraction(molecule3,denominator3);
      Fraction num4 = new Fraction(molecule4,denominator4);    

      /*根据运算符个数的不同开始分类讨论*/     
      if(operatorNum == 1){ //当运算符数目是1个的时候
      //使用字符串输出题目
      String question = num1 + " " + operator[j1]+ " " + num2 + " = ";
      //除号后面不能是0
      if(operator[j1] == '÷' && num2.molecule == 0) {
            i--;
            continue;
      }
      //使用计算器算出题目答案
      Fraction answer=Calculator.calculator.calculate(question);
      //出现负数就重新产生题目
      if(answer.molecule < 0 || answer.denominator < 0) {
          i--;
          continue;
      }
      //输出题目
      System.out.println("题目" + (i+1) + ": " + question);
      //题目与答案存入目录文件里
      QuestionWrite qw = new QuestionWrite();
      qw.Write(question);
      AnswerWrite aw = new AnswerWrite();
      aw.Write(answer);
      } 
      
      //运算符为2时,同上,只有str这里不同
      if(operatorNum == 2) {
      String question = num1 + " " + operator[j1] + " "+ num2 + " " + operator[j2] + " " + num3 + " = ";
      } 

      //运算符为3时,同上,只有str这里不同
      if(operatorNum == 2) {
      String question = num1 + " " + operator[j1] + " " + num2 + " " + operator[j2] + " " + num3 + " " + operator[j3] + " " + num4 + " = ";
      } 

3.测试运行

  • 输出10个题目

  • 题目txt展示

  • 答案txt展示

  • 输出10000个题目,数值范围100

四.效能分析

修改了一下代码,取消键盘录入,自定义数值范围是10和生成题目数量是100

- 数据占用内存

- CPU占用频率

- 时间占用百分百

五.项目小结

这次的结对项目,合作显得非常重要,这期间思维的碰撞讨论,对于推进项目进度至关重要,多交流探讨的好处在于破开思维,在设计

和开发过程中容易陷入思维误区,同时会有很多没有考虑到的部分,在和队友的交谈中经常可以使思维得到解放,其中也包括思绪的

整理、编码规范的建议,相较于一个人轻松了太多了,让我和队友都对团队的合作与交流有了全新的认识。

posted @ 2020-10-12 01:08  简简~~单单  阅读(238)  评论(0编辑  收藏  举报