github项目传送门:
项目要求
- 功能列表
- [完成] 使用 -n 参数控制生成题目的个数
- [完成] 使用 -r 参数控制题目中数值的范围, 。该参数可以设置为1或其他自然数。
- [完成] 生成的题目中计算过程不能产生负数
- [完成] 生成的题目中如果存在形如e1 ÷ e2的子表达式,那么其结果应是真分数。
- [完成] 程序一次运行生成的题目不能重复,生成的题目存入执行程序的当前目录下的Exercises.txt文件
- [完成] 每道题目中出现的运算符个数不超过3个
- [完成] 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件
- [完成] 程序应能支持一万道题目的生成。
- [未完成] 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计
PSP开发耗时
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
Planning | 计划 | 30 | 60 |
· Estimate | · 估计这个任务需要多少时间 | 30 | 60 |
Development | 开发 | 1000 | 2240 |
· Analysis | · 需求分析 (包括学习新技术) | 100 | 180 |
· Design Spec | · 生成设计文档 | 60 | 100 |
· Design Review | · 设计复审 (和同事审核设计文档) | 30 | 30 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 30 |
· Design | · 具体设计 | 120 | 150 |
· Coding | · 具体编码 | 600 | 1500 |
· Code Review | · 代码复审 | 50 | 60 |
· Test | · 测试(自我测试,修改代码,提交修改) | 150 | 60 |
Reporting | 报告 | 80 | 80 |
· Test Report | · 测试报告 | 30 | 60 |
· Size Measurement | · 计算工作量 | 20 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 20 |
合计 | 1110 | 2400 |
· 思路分析
- 生成表达式
使用对象存储子表达式,两个子表达式组成一个总表达式
表达式对象使用字符串类型存储操作数和操作符
控制表达式符号与数字之间保留一个空格,方便后续筛选
- 检查是否重复
每生成一个表达式都会添加到列表里面
通过遍历列表的表达式确定是否为相同的表达式子,如果是,重新生成表达。
- 存储表达式存储
将生成的中缀表达式转化为后缀表达式
根据运算优先级将操作数和符号存储在对应节点中
父节点存储运算符号,子节点存储操作数
例如: 3+2+1
- 计算表达式结果
根据表达式对应的二叉树进行计算,
将左右子节点进行操作之后把值赋给父节点
重复上述操作,直到根节点停止
根节点的值即为所求
并将所求值存储在答案列表里
- 写入文件
遍历表达式列表和答案列表
分别写入对应的文件
程序流程图
用户使用说明
举例:
-n [数值] 使用 -n 参数控制生成题目的个数。
-r [数值] 使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围。
代码
项目目录:
生成表达式:
- 生成操作数
public String generateOperand(int iRange) { String sOpreand=""; switch(randomer.nextInt(3)) { case 0://生成带分数 sOpreand = (randomer.nextInt(iRange)+1)+"’"+generatePrimes(iRange); break; case 1: sOpreand = generatePrimes(iRange); break; case 2: sOpreand =String.valueOf(randomer.nextInt(iRange)+1); break; } return sOpreand; }
- 生成分数(分子分母互质,无需约分)
b private String generatePrimes(int iRange) { //分子最大为8 int iNumerator=randomer.nextInt(iRange)+1; int iDenominator=iNumerator+randomer.nextInt(iRange)+1; //保证两个数互质 if(iNumerator==0) { //分子为0,分母直接使用 return iNumerator+"/"+iDenominator; } int comDivisor = getComDivisor(iNumerator, iDenominator); iNumerator/=comDivisor; iDenominator/=comDivisor; return iNumerator+"/"+iDenominator; }
- 数值互转及详细操作
1 package utils; 2 3 import exception.IllegalSituationException; 4 5 public class CalculateUtil { 6 7 //加法 8 9 /** 10 * 两个数的相加 11 * 12 * @param a 加数 13 * @param b 加数 14 * @return 结果 15 */ 16 public static String add(String a, String b) { 17 if (a.equals("0")||b.equals("0")){ 18 return a.equals("0")?b:a; 19 } 20 //两个都是自然数 21 if (isNaturalNumber(a) && isNaturalNumber(b)) { 22 return String.valueOf(Integer.valueOf(a) + Integer.valueOf(b)); 23 } 24 25 //两个都是分数 26 if (!isNaturalNumber(a) && !isNaturalNumber(b)) { 27 String m = getRealFraction(a); 28 String n = getRealFraction(b); 29 30 return simplify(addFractionAndFraction(m, n)); 31 32 } 33 //一个是自然数一个是分数 34 if (isNaturalNumber(a) && !isNaturalNumber(b)) { 35 String fraction = getRealFraction(b); 36 return simplify(addFractionAndNatural(a, fraction)); 37 } 38 39 //一个是分数一个是自然数 40 if (!isNaturalNumber(a) && isNaturalNumber(b)) { 41 String fraction = getRealFraction(a); 42 return simplify(addFractionAndNatural(b, fraction)); 43 } 44 45 return null; 46 47 } 48 49 /** 50 * 分数加自然数 ,int[0] 为分子, int[1] 为分母 51 * 52 * @param natural 自然数 53 * @param fraction 分数 54 * @return 结果 55 */ 56 private static String addFractionAndNatural(String natural, String fraction) { 57 int nat = Integer.valueOf(natural); 58 int[] numB = getDenominatorAndMolecule(fraction); 59 60 int molecule = nat * numB[1] + numB[0]; //分子 61 int denominator = numB[1]; // 分母 62 return molecule + "/" + denominator; 63 } 64 65 /** 66 * 两个分数相加 int[0] 为分子, int[1] 为分母 67 * 68 * @param a 加数 69 * @param b 加数 70 * @return 结果 71 */ 72 private static String addFractionAndFraction(String a, String b) { 73 int[] numA = getDenominatorAndMolecule(a); 74 int[] numB = getDenominatorAndMolecule(b); 75 76 int molecule = numA[0] * numB[1] + numB[0] * numA[1]; 77 int denominator = numA[1] * numB[1]; 78 79 return molecule + "/" + denominator; 80 81 } 82 83 //减法 84 85 /** 86 * 两个数的减法 87 * 88 * @param a 减数 89 * @param b 被减数 90 * @return 结果 91 */ 92 public static String subtract(String a, String b) { 93 if (a.equals("0")||b.equals("0")){ 94 return a.equals("0")? "-"+b:a; 95 } 96 //两个都是自然数 97 if (isNaturalNumber(a) && isNaturalNumber(b)) { 98 return String.valueOf(Integer.valueOf(a) - Integer.valueOf(b)); 99 }100 101 //两个都是分数102 if (!isNaturalNumber(a) && !isNaturalNumber(b)) {103 String m = getRealFraction(a);104 String n = getRealFraction(b);105 106 return simplify(subtractFractionAndFraction(m, n));107 108 }109 //一个是自然数一个是分数110 if (isNaturalNumber(a) && !isNaturalNumber(b)) {111 String fraction = getRealFraction(b);112 return simplify(subtractFractionAndFraction(naturalToFraction(a, fraction), fraction));113 }114 115 //一个是分数一个是自然数116 if (!isNaturalNumber(a) && isNaturalNumber(b)) {117 String fraction = getRealFraction(a);118 return simplify(subtractFractionAndFraction(fraction, naturalToFraction(b, fraction)));119 }120 121 return null;122 }123 124 /**125 * 自然数转分数 int[0] 为分子, int[1] 为分母126 *127 * @param natural 自然数128 * @param fraction 分数129 * @return 结果130 */131 private static String naturalToFraction(String natural, String fraction) {132 int[] numFrac = getDenominatorAndMolecule(fraction);133 int molecule = Integer.valueOf(natural) * numFrac[1]; //分子134 int denominator = numFrac[1]; // 分母135 return molecule + "/" + denominator;136 137 }138 139 /**140 * 分数减分数 int[0] 为分子, int[1] 为分母141 *142 * @param a 分数143 * @param b 分数144 * @return 结果145 */146 private static String subtractFractionAndFraction(String a, String b) {147 int[] numA = getDenominatorAndMolecule(a);148 int[] numB = getDenominatorAndMolecule(b);149 150 int molecule = numA[0] * numB[1] - numB[0] * numA[1]; //分子151 int denominator = numA[1] * numB[1]; //分母152 153 return molecule + "/" + denominator;154 }155 156 //乘法157 158 /**159 * 乘法160 *161 * @param a 乘数162 * @param b 乘数163 * @return 结果164 */165 public static String multiplies(String a, String b) {166 if (a.equals("0")||b.equals("0")){167 return String.valueOf(0);168 }169 //两个都是自然数170 if (isNaturalNumber(a) && isNaturalNumber(b)) {171 return String.valueOf(Integer.valueOf(a) * Integer.valueOf(b));172 }173 174 //两个都是分数175 if (!isNaturalNumber(a) && !isNaturalNumber(b)) {176 String m = getRealFraction(a);177 String n = getRealFraction(b);178 179 return simplify(multipliesFractionAndFraction(m, n));180 181 }182 //一个是自然数一个是分数183 if (isNaturalNumber(a) && !isNaturalNumber(b)) {184 String fraction = getRealFraction(b);185 return simplify(multipliesFractionAndFraction(naturalToFraction(a, fraction), fraction));186 }187 188 //一个是分数一个是自然数189 if (!isNaturalNumber(a) && isNaturalNumber(b)) {190 String fraction = getRealFraction(a);191 return simplify((multipliesFractionAndFraction(fraction, naturalToFraction(b, fraction))));192 }193 return null;194 }195 196 /**197 * 分数乘分数198 *199 * @param a 分数200 * @param b 分数201 * @return 结果202 */203 private static String multipliesFractionAndFraction(String a, String b) {204 int[] numA = getDenominatorAndMolecule(a);205 int[] numB = getDenominatorAndMolecule(b);206 207 int molecule = numA[0] * numB[0]; //分子208 int denominator = numA[1] * numB[1]; // 分母209 210 return molecule + "/" + denominator;211 }212 213 /**214 * 除法215 * @param a 除数216 * @param b 被除数217 * @return 结果218 */219 public static String divide(String a, String b) throws IllegalSituationException {220 if (a.equals("0")){221 return String.valueOf(0);222 }223 if (b.equals("0")){224 throw new IllegalSituationException("Argument b can’t be zero");225 }226 //两个都是自然数227 if (isNaturalNumber(a) && isNaturalNumber(b)) {228 return simplify(a + "/" + b);229 }230 231 //两个都是分数232 if (!isNaturalNumber(a) && !isNaturalNumber(b)) {233 String m = getRealFraction(a);234 String n = getRealFraction(b);235 return simplify(divideFractionAndFraction(m, n));236 237 }238 //一个是自然数一个是分数239 if (isNaturalNumber(a) && !isNaturalNumber(b)) {240 String fraction = getRealFraction(b);241 return simplify(divideFractionAndFraction(naturalToFraction(a, fraction), fraction));242 }243 244 //一个是分数一个是自然数245 if (!isNaturalNumber(a) && isNaturalNumber(b)) {246 String fraction = getRealFraction(a);247 return simplify(divideFractionAndFraction(fraction, naturalToFraction(b, fraction)));248 }249 250 return null;251 }252 253 /**254 * 分数除分数255 * @param a 除数256 * @param b 被除数257 * @return 结果258 */259 private static String divideFractionAndFraction(String a, String b) {260 int[] numA = getDenominatorAndMolecule(a);261 int[] numB = getDenominatorAndMolecule(b);262 263 int molecule = numA[0] * numB[1];264 int denominator = numA[1] * numB[0];265 266 return molecule + "/" + denominator;267 }268 269 270 /**271 * 获取分数的分子和分母272 *273 * @param a 分数274 * @return 结果,int[0] 为分子, int[1] 为分母275 */276 private static int[] getDenominatorAndMolecule(String a) {277 String numA[] = a.split("[/]");278 int numInt[] = new int[numA.length];279 for (int i = 0; i < numInt.length; i++) {280 numInt[i] = Integer.valueOf(numA[i]);281 282 }283 return numInt;284 }285 286 /**287 * 分数形式的转换288 *289 * @param s 分数290 * @return 结果291 */292 private static String getRealFraction(String s) {293 if (isFalseFraction(s)) { //1"1/2294 String numStr[] = s.split("[’/]");295 int numInt[] = new int[numStr.length];296 for (int i = 0; i < numInt.length; i++) {297 numInt[i] = Integer.valueOf(numStr[i]);298 299 }300 int denominator = numInt[0] * numInt[2] + numInt[1];301 return denominator + "/" + numStr[2];302 }303 304 return s;305 }306 307 /**308 * 判断是否为自然数309 *310 * @param s 数311 * @return 结果312 */313 private static boolean isNaturalNumber(String s) {314 return !s.contains("/");315 }316 317 /**318 * 判断是否为 假分数319 *320 * @param s 数321 * @return 结果322 */323 private static boolean isFalseFraction(String s) {324 return s.contains("’");325 }326 327 private static String simplify(String fraction){328 int[] num = getDenominatorAndMolecule(fraction);329 int molecule = num[0] ;330 int denominator = num[1] ;331 if (molecule==0){332 return String.valueOf(0);333 }334 if (molecule==denominator){335 return "1";336 }337 338 if (moleculedenominator){349 int i = gcd(molecule,denominator);350 molecule = molecule/i;351 denominator = denominator/i;352 353 if (denominator==1){354 return molecule+"";355 356 }357 358 return getWithFraction(molecule,denominator);359 360 }361 362 return null;363 364 }365 366 /**367 * 获取带分数 368 * @param molecule 分子369 * @param denominator 分母 370 * @return 结果371 */372 private static String getWithFraction(int molecule,int denominator){373 int withFraction = (molecule - (molecule%denominator)) / denominator;374 molecule = molecule%denominator;375 return withFraction+"’"+molecule+"/"+denominator;376 }377 378 /**379 * 求最大公约数,欧几里得方法380 * @param m 数1381 * @param n 数382 * @return 结果383 */384 private static int gcd(int m, int n) {385 return n == 0 ? m : gcd(n, m % n);386 }387 388 public static boolean isNegative(String num){389 return num.contains("-");390 }391 }
- 后缀表达式处理数据
public static String InfixToPostfix(String expression) { String str[] = expression.split("\\s+"); for (String s : str) { //四个操作符 if (isOperator(s)) { handleOperator(s); continue; } //左括号,入栈 if (s.equals("(")) { mStack.push(s); continue; } //右括号,弹出并输出,直到遇到左括号,左括号也弹出 if (s.equals(")")) { while (!mStack.peek().equals("(")) { mExp.append(mStack.pop()); mExp.append(" "); } mStack.pop(); continue; } mExp.append(s); mExp.append(" "); } //栈中还有数据,直接出栈输出 while (!mStack.empty()) { mExp.append(mStack.pop()); mExp.append(" "); } return mExp.toString(); }
- 输出表达式到Exercises.txt和输出答案到Answers.txt:
private static void doWork(int iQuestions,int iRange){ while (mList.size() != iQuestions) { String ex = Operation.generateQuestion(); Node tree = TreeUtil.createTree(StringUtil.InfixToPostfix(ex)); if (generate1(tree).isIllegal() && !isEquals(mResult)) { mResult.setExpression(ex); mList.add(mResult); } } //文件操作对象 FileOutputStream outSTr1 ; FileOutputStream outSTr2 ; BufferedOutputStream Buff1; BufferedOutputStream Buff2; long begin0 = System.currentTimeMillis(); try { outSTr1 = new FileOutputStream(new File(".\\Exercises.txt")); outSTr2 = new FileOutputStream(new File(".\\Answers.txt")); Buff1 = new BufferedOutputStream(outSTr1); Buff2 = new BufferedOutputStream(outSTr2); for(int i=0; i
测试运行
表达式的输出
-n 10 -e 10
9’9/10 + 6’6/11 × 9 - 6 = 62’89/1105 × 3’1/7 = 15’5/73’1/9 - 1/2 = 2’11/189 + 10/17 + 2 × 9 = 27’10/179 + 2’3/13 ÷ 1/4 = 17’12/133’5/7 + 8 - 2 = 9’5/76’2/5 ÷ 9’3/4 ÷ 10’1/2 = 256/40954’1/5 + 6/11 ÷ 9 = 4’43/1659’7/16 ÷ 3/7 - 5 = 17’1/482’6/13 × 2’5/14 = 5’73/91
- 随机输出10000条10范围以内的表达式和答案:
10000个答案
10000个表达式
总结
在与马仪生的合作中,我体验到了直接调用别人写好的接口的快乐。在本次实验中,我主要负责生成表达式及总结,仪生主要负责处理表达式。在与仪生的合作中,我了解到更多编码规范,必须对象类的报名定义为bean,一些固定的常量也需要放在const包中,调用的工具类放在util包里,把各种功能实现分开实现,最后集中使用,便于维护的同时也是代码更加清晰。