一 、Github项目地址:https://github.com/mushan520/Four-fundamental-rules-java.git
或 https://github.com/SAH2019/as
二、PSP表格:
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 45 |
· Estimate | · 估计这个任务需要多少时间 | 60 | 45 |
Development | 开发 | 1260 | 1515 |
· Analysis | · 需求分析 | 60 | 90 |
· Design Spec | · 生成设计文档 | 60 | 60 |
· Design Review | · 设计复审 | 30 | 45 |
· Coding Standard | · 代码规范 | 30 | 60 |
· Design | · 具体设计 | 90 | 90 |
· Coding | · 具体编码 | 900 | 1080 |
· Code Review | · 代码复审 | 30 | 30 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 | 90 |
Reporting | 报告 | 100 | 140 |
· Test Report | · 测试报告 | 30 | 50 |
· Size Measurement | · 计算工作量 | 30 | 50 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 40 | 40 |
合计 | 1420 | 1700 |
三:实现过程
1.因为涉及到分数的相关运算,所以单独设计了一个分数类Fraction,里面实现了分数的加减乘除计算以及分数的显示,以供给其他的类和方法调用
2.FormulaMaker中展示了整个项目的实现过程:1)随机生成整数或者分数-->2)随机生成四则运算的符号-->3)将生成的运算数和符号连接成算式-->4)对算式进行结果的计算-->5)分别输出结果和算式到文件中。
3.我们所做的程序和老师的要求还有一定的差距,比如分数的表示,目前我们的程序生成的结果,都是以分数形式表示的,比如0/2 ,2/1或者是8/3这种本该用2‘2/3表示的结果,无法正确的按要求显示,这是我们技术上的不成熟导致的,很遗憾。
4.以下的导图显示了程序的方法结构。
四:代码说明
1. FormulaMaker :主要的一个类:用来完成生成随机的算式,以及算式的计算,还有算式和答案的输出
1 package creater; 2 3 import java.io.File; 4 import java.io.FileWriter; 5 import java.io.PrintStream; 6 import java.util.HashSet; 7 import java.util.Random; 8 import java.util.Stack; 9 10 public class FormulaMaker { 11 private int numRange; 12 private int questionsNum; 13 14 public FormulaMaker(int numRange, int questionsNum) { 15 this.numRange = numRange; 16 this.questionsNum = questionsNum; 17 makeFormula(); 18 HashSetset = makeFormulas(); 19 Fraction[] arr = getAnswers(set); 20 outputFormula(set); 21 outputAnswers(arr); 22 23 } 24 25 // 获取随机整数 26 public int getRandomNumber() { 27 Random rand = new Random(); 28 int RandomNum = rand.nextInt(this.numRange); 29 if (RandomNum == 0) 30 RandomNum = RandomNum + 1; 31 return RandomNum; 32 } 33 //获取随机分数 34 public Fraction getRandomFraction() { 35 Fraction a = new Fraction(getRandomNumber(), getRandomNumber()); 36 return a; 37 38 } 39 40 // 获取随机运算符号 41 public String getRandomSign() { 42 Random rand = new Random(); 43 String[] operations = { "+", "-", "*", "/" }; 44 return operations[rand.nextInt(4)]; 45 } 46 47 public String makeFormula() { 48 String formula = ""; 49 for (int i = 0; i < 4; i++) { 50 if (i >= 3) { 51 { 52 Random rand = new Random(); 53 int a = rand.nextInt(2); 54 switch (a) { 55 case 0: 56 formula += this.getRandomNumber(); 57 break; 58 case 1: 59 formula += getRandomFraction(); 60 break; 61 } 62 } 63 continue; 64 } 65 Random rand = new Random(); 66 int a = rand.nextInt(2); 67 switch (a) { 68 case 0: 69 formula += this.getRandomNumber() + " " + this.getRandomSign() + " "; 70 break; 71 case 1: 72 formula += this.getRandomFraction() + " " + this.getRandomSign() + " "; 73 break; 74 } 75 76 } 77 return formula; 78 } 79 80 // 生成算式集合 81 public HashSet makeFormulas() { 82 HashSet set = new HashSet (); 83 while (set.size() < this.questionsNum) { 84 String formula = this.makeFormula(); 85 set.add(formula); 86 } 87 return set; 88 } 89 90 91 // 比较运算优先级 92 public int compare(String operator1, String operator2) { 93 int res = 0; 94 switch (operator1) { 95 case "+": 96 case "-": 97 if (operator2.equals("+") || operator2.equals("-") || operator2.equals("*") || operator2.equals("/")) { 98 res = 1; 99 } else { 100 res = -1; 101 } 102 break; 103 case "*": 104 case "/": 105 if (operator2.equals("*") || operator2.equals("/")) { 106 res = 1; 107 } else { 108 res = -1; 109 } 110 break; 111 } 112 return res; 113 } 114 115 116 // 生成算式结果 117 public Fraction getAnswer(String formula) { 118 119 int length = 0; 120 String[] formulaArr = formula.split(" "); 121 String operators = "+-*/"; 122 Stack opNumbers = new Stack (); 123 Stack opOperators = new Stack (); 124 opOperators.add("#");// 字符栈中存储个#号,防止栈空 125 while (length < formulaArr.length) { 126 String op = formulaArr[length++]; 127 if (operators.indexOf(op) > -1) {// 若是运算符,判断优先级 128 String sign = opOperators.peek(); 129 int priority = compare(op, sign);// 要入栈的跟栈顶的相比 130 if (priority >= 0) {// 如果要入栈的运算符高或者相等,出栈两个数字,和之前的运算符,计算后,将数字入栈,将字符入栈 131 opNumbers.add(compute(opOperators, opNumbers)); 132 opOperators.add(op); 133 } else {// 入栈运算符优先级低,直接入栈 134 opOperators.add(op); 135 } 136 continue; 137 } 138 // 若是数字,则入栈 139 if (op.matches("\\d+/\\d+")) { 140 String[] strs = op.split("/"); 141 int fenZi = Integer.parseInt(strs[0]); 142 int fenMu = Integer.parseInt(strs[1]); 143 opNumbers.add(new Fraction(fenZi, fenMu)); 144 } else { 145 int fenZi = Integer.parseInt(op); 146 opNumbers.add(new Fraction(fenZi, 1)); 147 } 148 149 } 150 while (opOperators.peek() != "#") { 151 opNumbers.add(compute(opOperators, opNumbers)); 152 } 153 Fraction lastResult = null; 154 String resultArr = opNumbers.pop()+""; 155 if (resultArr.matches("-?\\d+/\\d+")) { 156 String[] strs2 = resultArr.split("/"); 157 int lastFenZi = Integer.parseInt(strs2[0]); 158 int lastFenMu = Integer.parseInt(strs2[1]); 159 lastResult=changeFraction(lastFenZi,lastFenMu); 160 161 } 162 return lastResult; 163 164 } 165 166 167 //给分数约分 168 public Fraction changeFraction(int a,int b) { 169 if(a < 0) { 170 a = -a; 171 int min = a < b ? a : b; 172 for (int i = min;i >= 1;i --) { 173 if (a % i == 0 && b % i == 0) { 174 a=a/i; 175 b=b/i; 176 break; 177 } 178 179 } 180 return new Fraction(-a,b) ; 181 } 182 183 int min = a < b ? a : b; 184 for (int i = min;i >= 1;i --) { 185 if (a % i == 0 && b % i == 0) { 186 a=a/i; 187 b=b/i; 188 break; 189 } 190 191 } 192 return new Fraction(a,b) ; 193 194 } 195 196 197 198 // 算式求值 199 public Fraction compute(Stack opOperators, Stack opNumbers) { 200 Fraction num2 = opNumbers.pop(); 201 Fraction num1 = opNumbers.pop(); 202 String _op = opOperators.pop(); 203 Fraction result = null; 204 switch (_op) { 205 case "+": 206 result = num1.add(num2); 207 break; 208 case "-": 209 result = num1.sub(num2); 210 break; 211 case "*": 212 result = num1.multiply(num2); 213 break; 214 case "/": 215 result = num1.div(num2); 216 break; 217 } 218 return result; 219 } 220 221 222 // 生成算式结果数组 223 public Fraction[] getAnswers(HashSet set) { 224 Fraction[] arr = new Fraction[set.size()]; 225 int i = 0; 226 for (String str : set) { 227 arr[i++] = getAnswer(str); 228 } 229 return arr; 230 } 231 232 // 输出算式到文件 233 public String outputFormula(HashSet set) { 234 File file = new File("Exercises.txt"); 235 try { 236 int b = 1; 237 PrintStream ps1 = new PrintStream(file); 238 for (String str : set) { 239 240 ps1.println(b + ". " + str + "="); 241 b++; 242 } 243 ps1.close(); 244 } catch (Exception e) { 245 System.out.println("Error" + e.getMessage()); 246 System.exit(0); 247 } 248 return file.getAbsolutePath(); 249 } 250 251 // 输出答案到文件 252 public String outputAnswers(Fraction[] arr) { 253 File file = new File("Answers.txt"); 254 try { 255 PrintStream ps2 = new PrintStream(file); 256 for (int i = 0; i < arr.length; i++) { 257 ps2.print(i + 1 + ". "); 258 ps2.println(arr[i]); 259 } 260 ps2.close(); 261 } catch (Exception e) { 262 System.out.println("Error" + e.getMessage()); 263 System.exit(0); 264 } 265 return file.getAbsolutePath(); 266 } 267 268 }
2. Fraction 分数类,计算分数的加减乘除以及显示分数:
1 package creater; 2 3 public class Fraction { 4 5 int fenzi; 6 int fenmu; 7 8 public Fraction(int fenzi, int fenmu) { 9 if (fenmu == 0) { 10 throw new IllegalArgumentException("分母不能为0"); 11 } 12 this.fenzi = fenzi; 13 this.fenmu = fenmu; 14 } 15 16 // 分数的加法 17 public Fraction add(Fraction other) { 18 int fm = this.fenmu * other.fenmu; 19 int fz = this.fenzi * other.fenmu + other.fenzi * this.fenmu; 20 return new Fraction(fz, fm); 21 } 22 23 // 分数的减法 24 public Fraction sub(Fraction other) { 25 int fm = this.fenmu * other.fenmu; 26 int fz = this.fenzi * other.fenmu - other.fenzi * this.fenmu; 27 return new Fraction(fz, fm); 28 } 29 30 // 分数的乘法 31 public Fraction multiply(Fraction other) { 32 int fm = this.fenmu * other.fenmu; 33 int fz = this.fenzi * other.fenzi; 34 return new Fraction(fz, fm); 35 } 36 37 // 分数的除法 38 public Fraction div(Fraction other) { 39 40 int fm = this.fenmu * other.fenzi; 41 int fz = this.fenzi * other.fenmu; 42 return new Fraction(fz, fm); 43 } 44 45 // 分数的显示 46 public String toString() { 47 48 49 50 return fenzi + "/" + fenmu; 51 } 52 53 54 }
3.creater 类包含主方法,程序的开始。
1 package creater; 2 3 import java.util.Scanner; 4 5 public class creater { 6 7 public interface Fraction { 8 9 } 10 11 public static void main(String[] args) { 12 // TODO 自动生成的方法存根 13 long startTime = System.currentTimeMillis(); 14 Scanner choose = new Scanner(System.in); 15 System.out.println("************************************欢迎来到四则运算生成器******************************************"); 16 System.out.println("请选择生成题目数量:"); 17 int questionsNum = choose.nextInt(); 18 System.out.println("请输入算术范围:"); 19 int numRange = choose.nextInt(); 20 21 new FormulaMaker(numRange, questionsNum); 22 System.out.println("题目文件生成完毕!"); 23 long endTime = System.currentTimeMillis(); 24 System.out.println("程序运行时间:" + (endTime - startTime) + "ms"); //输出程序运行时间 25 } 26 27 }
五:测试结果
结果一:测试结果比较小的时候(如生成20道题,算数在10以内)
控制台显示内容:
1.生成的算式文件--Exercises.txt
2.生成的答案文件--Answers.txt
结果二:程序能支持一万道题目的生成
控制台显示内容:
1.生成的算式文件--Exercises.txt
2.生成的答案文件--Answers.txt
六:项目小结
本次的项目和上次相比有许多不同的地方,在项目完成后,做出以下结论:第一:这一次是两个人一同做一个项目,这样会比一个人单独做会简单一些。毕竟合作的作用就是1+1>2 。但是两个人合作也会有一些弊端,比如两个人的分工合作的调度是一个问题。第二:这是第二次的作业,明显的比上一次有了更多的做项目的经验,但是却发现了新的问题,就是编程知识的不足,这是项目经验弥补不了的,比如在大脑中构思好了某一个方法的流程和结构,但是总会在一些小细节上卡住,虽然说是小细节,但是影响也是巨大的。毕竟程序这种东西错了一个标点符号都是不可行的。第三:程序的方法之间会有紧密的联系,牵一发而动全身,因此后期的修改工作也是比较复杂。总而言之,提升自己的知识储备是当务之急!
项目作者:朱伟彬3117004639 商爱虎3117004626