我们老师最近布置了一项作业,让每个人用Java语言实现整数或是分数的四则运算功能。
具体需求如下:
1、程序可接收一个输入参数n,然后随机产生n道加减乘除(分别使用符号+-*÷来表示)练习题,每个数字在 0 和 100 之间,运算符在3个到5个之间。
2、每个练习题至少要包含2种运算符。同时,由于小学生没有分数与负数的概念,你所出的练习题在运算过程中不得出现负数与非整数,比如不能出 3÷5+2=2.6,2-5+10=7等算式。
3、练习题生成好后,将你的学号与生成的n道练习题及其对应的正确答案输出到文件“result.txt”中,不要输出额外信息,文件目录与程序目录一致。
4、支持有括号的运算式,包括出题与求解正确答案。注意,算式中存在的括号数必须大于2对,且不得超过运算符的个数。
5、支持真分数的出题与运算(只需要涵盖加减法即可),例如:1/6 + 1/8 + 2/3= 23/24。注意在实现本功能时,需支持运算时分数的自动化简,比如 1/2+1/6=2/3,而非4/6,且计算过程中与结果都须为真分数。
我认为,整个项目的难点就在于将中缀表达式转化为后缀表达式,并将其计算出来。在这里我运用了逆波兰表达式和调度场算法。
下面是我的部分代码
public int algorithm(String s) {
//放数字
Stack stack1 = new Stack<>();
//放操作符
Stack stack2 = new Stack<>();
//存放运算符优先级
HashMap hashmap = new HashMap<>();
hashmap.put("(", 0);
hashmap.put("+", 1);
hashmap.put("-", 1);
hashmap.put("*", 2);
hashmap.put("÷", 2);
for (int i = 0; i < s.length();) {
//设置可变长的字符串
StringBuffer digit = new StringBuffer();
//将式子字符串切割为c字符
char c = s.charAt(i);
//判断字符是否为10进制数字,将一个数加入digit
while (Character.isDigit(c)) {
digit.append(c);
i++;
c = s.charAt(i);
}
//当前digit里面已经无数字,即当前处理符号
if (digit.length() == 0){
switch (c) {
case '(': {
stack2.push(String.valueOf(c));
break;
}
//遇到右括号了计算,因为(的优先级最高
case ')': {
String stmp = stack2.pop();
while (!stack2.isEmpty() && !stmp.equals("(")) {
int a = stack1.pop();
int b = stack1.pop();
int sresulat = calculate(b, a, stmp);
if(sresulat<0)
return -1;
stack1.push(sresulat);
//符号指向下一个计算符号
stmp = stack2.pop();
}
break;
}
case '=': {
String stmp;
while (!stack2.isEmpty()) {
stmp = stack2.pop();
int a = stack1.pop();
int b = stack1.pop();
int sresulat = calculate(b, a, stmp);
if(sresulat<0)
return -1;
stack1.push(sresulat);
}
break;
}
default: {
String stmp;
while (!stack2.isEmpty()) {
stmp = stack2.pop();
//比较优先级
if (hashmap.get(stmp) >= hashmap.get(String.valueOf(c))) {
int a = stack1.pop();
int b = stack1.pop();
int sresulat =calculate (b, a, stmp);
if(sresulat<0)
return -1;
stack1.push(sresulat);
}
else {
stack2.push(stmp);
break;
}
}
//将符号压入符号栈
stack2.push(String.valueOf(c));
break;
}
}
}
else {
//处理数字
stack1.push(Integer.valueOf(digit.toString()));
continue;
}
i++;
}
//返回栈底得到答案
return stack1.peek();
}
private int calculate(int a, int b, String stmp) { //计算a stmp b的值
int res = 0;
char s = stmp.charAt(0);
switch (s) {
case '+': {
res = a + b;
break;
}
case '-': {
//判断是否产生负数
res = a - b;
break;
}
case '*': {
res = a * b;
break;
}
case '÷': {
if(b==0)
return -1;
//判断是否产生小数
else if(a%b!=0)
return -2;
else
res = a / b;
break;
}
}
return res;
}
这是算法部分
public class Create {
public String createProblem(){ //产生整数式子
Random r = new Random();
String[] opertor = {"+","-","*","÷"};
//操作符的个数
int operatorNum = 3+r.nextInt(3);
//新建数组来保存操作数
int[] number = new int[operatorNum+1];
//操作符的下标
int[] arr = index(operatorNum);
String s = new String();
for(int j=0;j=0){
s+=answer;
}else {
//递归
return createProblem();
}
return s;
}
public int[] index(int n){ //产生操作符的下标数组
Random random = new Random();
int similar=0;
int[] a = new int[n];
for(int j=0;j
产生整数式子
public class ProperFraction {
public String createProblem(){
Random r = new Random();
String[] operator = {"+","-"};
int operatorCount = 3+r.nextInt(3);
int[] index = index2(operatorCount);
int sumx = 1+r.nextInt(10);
int sumy = 1+r.nextInt(20);
int g = maxG(sumx,sumy);
sumx/=g;
sumy/=g;
int[] fenshu = huajian(sumx, sumy);
sumx = fenshu[0];
sumy = fenshu[1];
//第一个数
String s=sumx+"/"+sumy;
for(int i=0;i= fenmu) {
fenzi = 1 + r.nextInt(10);
fenmu = 1 + r.nextInt(20);
int g = maxG(fenzi, fenmu);
fenzi = fenzi / g;
fenmu = fenmu / g;
}
int[] fenshu = new int[2];
fenshu[0] = fenzi;
fenshu[1] = fenmu;
return fenshu;
}
}
产生分数式子
这三块便是整个项目最核心的部分便是这三个部分。
接下来就是项目功能的展示
在命令行进行测试
得到result.txt文件
下面是我的PSP
PSP2.1 |
任务内容 |
计划共完成需要的时间(h) |
实际完成需要的时间(h) |
Planning |
计划 |
2 |
3 |
· Estimate |
· 估计这个任务需要多少时间,并规划大致工作步骤 |
2 |
3 |
Development |
开发 |
46.5 |
69.5 |
· Analysis |
· 需求分析 (包括学习新技术) |
2 |
4 |
· Design |
· 具体设计 |
1 |
1 |
· Coding |
· 具体编码 |
38 |
46 |
· Code Review |
· 代码复审 |
0.5 |
0.5 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
5 |
18 |
Reporting |
报告 |
0.8 |
1 |
· Size Measurement |
· 计算工作量 |
0.3 |
0.5 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
0.5 |
0.5 |
改进:
我在生成括号的地方偷了个小懒,没有用生成随机数的方式来让式子随机生成括号的位置,我想,在以后,这是可以再进行改进的地方
个人感悟:
这次的项目让我感触颇多,同时也让我收获了许多。
由于很久没有写过Java程序了,以至于我都忘记了idea里的环境怎么配,只得一点一点从头再来。在开始写这个作业的时候,我有点无从下手,于是就一个一个地把需求在纸上罗列了出来,就是这样
这才使我的思路逐渐清晰。在代码基本完成,开始测试的时候又出了很多的问题,有时我在命令行测试的时候会出现一些莫名其妙的错误,比如找不到主类,数组下标越界,甚至直接告诉我我的内存不够了,在解决这些问题的时候真的是心力交瘁,有的时候甚至想,算了算了,直接随便找一份交上去算了,但是后来还是休息之后,继续和这些bug作斗争!
虽说这次作业耗费了很多的精力,但是在这次的项目中,我收获了宝贵的经验以及技术的大幅进步,哈哈哈哈哈,程序员不就是这样嘛,在一次又一次的实战中不断进步,不断成长!