四则运算—基于控制台

四则运算题目自动生成——基于控制台(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是真的耗费时间。

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

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

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

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

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


posted @ 2018-03-30 20:37  jimCome  阅读(284)  评论(0编辑  收藏  举报