四则运算—基于控制台
四则运算题目自动生成——基于控制台(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是真的耗费时间。
这个表格的时间我其实我并没有很详细的算了,因为做的时候零零散散的,所以时间也没有很好的统计,只是大概估计了下。
测试这一块我觉得我做的不够好,可以考虑用下单元测试,就像写二叉树查重那块,我是整体写完了,才开始测试,所以出现了有的读空,也有其他的细节小问题,比如出栈数的操作对于减法除法应该反着来的,起初没考虑到,后面发现才改过来。
后来在进一步优化代码以及写写注释什么的,也花了一些时间。
表里面的有的还不太清楚,所以就没写了。
第一次写博客!偷偷标记!