欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

四则运算—基于控制台

程序员文章站 2022-07-07 23:29:20
四则运算题目自动生成——基于控制台(java) "个人作业——四则运算题目生成程序(基于控制台)" 项目已提交到码云: "UMLProject" 需求分析: 关于输入、根据提示依次输入: 数字范围(样例:10) 题目数量(样例:10) 生成的题目: 如果存在形如e1 ÷ e2的子表达式,那么其结果应 ......

四则运算题目自动生成——基于控制台(java)


个人作业——四则运算题目生成程序(基于控制台)

项目已提交到码云:UMLProject

需求分析:

  • 关于输入、根据提示依次输入:
    • 数字范围(样例:10)
    • 题目数量(样例:10)
  • 生成的题目:
    • 如果存在形如e1 ÷ e2的子表达式,那么其结果应是真分数。
    • 每道题目中出现的运算符个数不超过3个。
    • 整数表示为1,真分数表示为1/2,假分数表示为2’1/2
  • 程序支持:
    • 一万道题目生成
    • 生成题目的同时计算出答案,分别保存本地txt文件。
    • 题目进行查重,去除重复。
    • 对个人答案的批改

功能设计:

控制台实现,功能相对简单,实现按要求生成题目,根据用户选择进行是否批改答案。并显示批改结果。


设计实现

1. 生成题目问题:

通过RandomQuestion类最终生成题目,RandomNum类和RandomSign类分别生成操作数和运算符。

RandomSign类:

public RandomSign(){//随机生成符号
        int random_sign;
        random_sign = (int)(Math.random()*4);
        switch(random_sign){
            case 0 : this.setRandom_sign("+");break;
            case 1 : this.setRandom_sign("-");break;
            case 2 : this.setRandom_sign("*");break;
            case 3 : this.setRandom_sign("÷");break;
        }
    }

RandomNum类:

public class RandomNum {//随机生成一个数 a'b/c
    private int random_numerator;//a'b/c 的格式拆分成 a b c 随机生成组合
    private int random_denominator;
    private int random_front;
    
    public String toStringSpit(){//进行区分 组成一个整体分数 
        if(this.getRandom_front()==0){
            if(this.getRandom_numerator() == this.getRandom_denominator()){
                return "1";
            }else return this.getRandom_numerator() + "/" + this.getRandom_denominator();//真分数
        }
        if(this.getRandom_denominator() == this.getRandom_numerator()){
            return Integer.toString(this.getRandom_front()+1);//整数
        }
        if(this.getRandom_numerator() == 0) return Integer.toString(this.getRandom_front());
        return this.getRandom_front() + "'" + this.getRandom_numerator() + "/" + this.getRandom_denominator();//假分数
    }
    
    public void SplitANum(String str){//把9‘1/5反向 拆分成类(此处默认所有数为如此格式)
        String[] parts;
        int temp;
        parts = str.split("'");
        temp = Integer.parseInt(parts[0]);
        this.random_front = temp;
        parts = parts[1].split("/");
        temp = Integer.parseInt(parts[0]);
        this.random_numerator = temp;
        temp = Integer.parseInt(parts[1]);
        this.random_denominator = temp;
    }
    
    public String toString(){//默认
        return this.getRandom_front() + "'" + this.getRandom_numerator() + "/" + this.getRandom_denominator();//都返回假分数
    }

RandomQuestion类:
生成括号参考(就不贴出来了):参考

public RandomQuestion(int qnum_range){//生成题目 用ArrayList存取
        int sign_num = (int)(Math.random()*4+1);//骰子判断生成题目长度
        //this.randomquestion = null;
        int flag = 0;
        RQ(qnum_range,sign_num,flag);//递归实现题目生成 具体就不贴了
    }

2. 答案计算:

中缀转换后缀 对后缀表达式进行求答案。

转后缀遵循规则:参考

  • 遇到操作数,直接输出;
  • 栈为空时,遇到运算符,入栈;
  • 遇到左括号,将其入栈;
  • 遇到右括号,执行出栈操作,并将出栈的元素输出,直到弹出栈的是左括号,左括号不输出;
  • 遇到其他运算符’+”-”*”/’时,弹出所有优先级大于或等于该运算符的栈顶元素,然后将该运算符入栈;
  • 最终将栈中的元素依次出栈,输出。

ChangeToRPN类:

public class ChangeToRPN {//a+b*c+(d*c+f)g -> abc*+de*f+g*+  中缀转后缀
    public ArrayList<Object> changetoRPN(ArrayList<Object> rq){
        ArrayList<Object> rpn = new ArrayList<Object>();
        Stack sk = new Stack();
        String temp_stackpop;
        for(int i = 0; i <= rq.size()-2; i++){
            if(IsSign(rq.get(i).toString())){//判断是不是符号
                if(sk.getTop() == -1 || rq.get(i).toString() == "("){//栈空 和(  直接入栈
                    sk.push(rq.get(i).toString());
                }else{
                    if(rq.get(i).toString() == ")"){
                        while(sk.getTop() != -1 && sk.top().toString() != "("){
                            temp_stackpop = sk.pop().toString();
                            if(temp_stackpop != "(") rpn.add(temp_stackpop);
                        }
                    }else{//遇到别的操作符 判断优先级
                        while(sk.getTop() != -1 && GetPriority(sk.top().toString(),true) 
                                      >= GetPriority(rq.get(i).toString(),false)){
                            temp_stackpop = sk.pop().toString();
                            if(temp_stackpop != "(") rpn.add(temp_stackpop);
                        }
                        sk.push(rq.get(i));
                    }
                }
            }else rpn.add(rq.get(i));
        }
        while(sk.getTop()!=-1){
            temp_stackpop = sk.pop().toString();
            if(temp_stackpop != "(") rpn.add(temp_stackpop);
        }
        return rpn;
    }
    
    //flag true->栈内  符号优先级
    int GetPriority(String operator, boolean flag)
    {
        if (operator == "+" || operator == "-")
        {
            if (flag) return 3;
            else return 2;
        }
        else if (operator == "*" || operator == "÷")
        {
            if (flag) return 5;
            else return 4;
        }
        else if (operator == "(")
        {
            if (flag) return 1;
            else return 6;
        }
        else if (operator == ")")
        {
            if (flag) return 6;
            else return 1;
        }
        return 0;
    }
}

不得不讲一下,逆波兰式在挺早的时候就学过了,然而还是完全忘了。。最终还是复习了下,才想起来整体流程。

Answer类:

public String GetAnswer(ArrayList<Object> rpn){
         Stack sk = new Stack();
         String rpn_temp;
         String num1,num2;
         String sk_temp;
         for(int i = 0; i <= rpn.size()-1; i++){
             rpn_temp = rpn.get(i).toString();
             if(IsSign(rpn_temp)){
                 num2 = sk.pop().toString();//注意出栈顺序 这边应该是 num2 后 num1
                 num1 = sk.pop().toString();
                 sk_temp = TwoNumCount(num1,num2,rpn_temp);//对两个数求值
                 if(sk_temp != "-1") sk.push(sk_temp);
                 else return "-1";//返回-1表示该式子求值失败 调用处continue
             }else sk.push(rpn_temp);
         }

         String result = sk.pop().toString();
         RandomNum rq = new RandomNum();
         rq.SplitANum(result);
         return rq.toStringSpit();
      }
    }

3. 题目查重:

Tree类:
5+6-2 —>56+2- 的转化存储过程:
四则运算—基于控制台

public class Tree {
    private double result;//存取当前节点计算的值
    private String value;//存取当前节点值
    private Tree lchild;
    private Tree rchild;
    
    //生成树中的规则
    public boolean Compare(Tree tree1, Tree tree2){//true——>tree1 左边
        ChangeToRPN rpn = new ChangeToRPN();
        int flag = 0;
        if(tree1.getResult() > tree2.getResult()) flag = 1;
        else if(tree1.getResult() < tree2.getResult()) flag = 2;
        else flag = 3;
        
        if(flag == 1) return true; //值大的为左
        else if(flag == 2) return false;
        else if(tree1.getLchild() == null && tree2.getLchild() == null) return true;//2 2相等但是没有孩子
        else if(rpn.GetPriority(tree1.getValue().toString(), true) // 值相等 运算符大的 左
                > rpn.GetPriority(tree2.getValue().toString(), true)) return true;
        else if(rpn.GetPriority(tree1.getValue().toString(), true) // 值和运算符都等  则子树的左子树值大的  左
                == rpn.GetPriority(tree2.getValue().toString(), true)){
            
            if(tree1.getLchild().getResult() > tree2.getLchild().getResult()) flag = 1;
            else if(tree1.getLchild().getResult() < tree2.getLchild().getResult()) flag = 2;
            else flag = 3;
            
            if(flag == 1) return true; 
            else if(flag == 2) return false;
            else return true;
        }
        return false;//tree2应为左
    } 
}

4. 运行测试

四则运算—基于控制台
四则运算—基于控制台

5. PSP

PSP2.1 Personal Software Process Stages Time Senior Student Time
Planning 计划 10 10
· Estimate 估计这个任务需要多少时间 10 10
Development 开发 600 900
· Analysis 需求分析 (包括学习新技术) 30 30
· Design Spec 生成设计文档 - -
· Design Review 设计复审 - -
· Coding Standard 代码规范 - -
· Design 具体设计 - -
· Coding 具体编码 480 600
· Code Review 代码复审 240 120
· Test 测试(自我测试,修改代码,提交修改) 200 120
Reporting 报告 240 180
· 测试报告 50 50
· 计算工作量 10 10
· 并提出过程改进计划 - -

6. 总结

做这个项目还是花了很多时间的,大多是下课后过去图书馆然后做这个了。做的时候碰到挺多问题的吧,最大的是一开始没有构思好具体思路,到了做着的时候发现行不通,后来还是再构想后,决定重新开始做。

具体编码这部分虽然一开始就觉得会花很久,结果真正做的时候发现,出现bug是真的耗费时间。

这个表格的时间我其实我并没有很详细的算了,因为做的时候零零散散的,所以时间也没有很好的统计,只是大概估计了下。

测试这一块我觉得我做的不够好,可以考虑用下单元测试,就像写二叉树查重那块,我是整体写完了,才开始测试,所以出现了有的读空,也有其他的细节小问题,比如出栈数的操作对于减法除法应该反着来的,起初没考虑到,后面发现才改过来。

后来在进一步优化代码以及写写注释什么的,也花了一些时间。

表里面的有的还不太清楚,所以就没写了。

第一次写博客!偷偷标记!