四则运算 Java 杨辉鹏,郑冠华
GitHub链接:https://github.com/yanghuipeng/arithmetic
项目相关要求
-
使用 -n 参数控制生成题目的个数,例如
-n 10 将生成10个题目。
-
使用 -r 参数控制题目中数值(自然数、真分数)的范围,例如
-r 10
将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定,否则程序报错并给出帮助信息。
-
生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1 − e2的子表达式,那么e1 ≥ e2。
-
生成的题目中如果存在形如e1 ÷ e2的子表达式,那么其结果应是真分数。
-
每道题目中出现的运算符个数不超过3个。
-
程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。
生成的题目存入执行程序的当前目录下的Exercises.txt文件,格式如下:
-
四则运算题目1
-
四则运算题目2
-
其中真分数在输入输出时采用如下格式,真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8。
-
在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件,格式如下:
1.答案1
2.答案2
特别的,真分数的运算如下例所示:1/6 + 1/8 = 7/24。
-
程序应能支持一万道题目的生成。
-
程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,输入参数如下:
Myapp.exe -e <exercisefile>.txt -a <answerfile>.txt
统计结果输出到文件Grade.txt,格式如下:
Correct: 5 (1, 3, 5, 7, 9)
Wrong: 5 (2, 4, 6, 8, 10)
其中“:”后面的数字5表示对/错的题目的数量,括号内的是对/错题目的编号。为简单起见,假设输入的题目都是按照顺序编号的符合规范的题目。
PSP开发耗时
PSP2.1
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
30 | 50 |
· Estimate |
· 估计这个任务需要多少时间 |
30 | 50 |
Development |
开发 |
1100 | 2260 |
· Analysis |
· 需求分析 (包括学习新技术) |
80 | 180 |
· Design Spec |
· 生成设计文档 |
60 | 100 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
30 | 40 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
30 | 30 |
· Design |
· 具体设计 |
100 | 150 |
· Coding |
· 具体编码 |
600 | 1500 |
· Code Review |
· 代码复审 |
50 | 60 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
150 | 200 |
Reporting |
报告 |
80 | 80 |
· Test Report |
· 测试报告 |
30 | 40 |
· Size Measurement |
· 计算工作量 |
20 | 20 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
30 | 20 |
合计 |
1210 | 2390 |
解题思路:
-
生成随机操作数
-
取随机操作符
-
生成表达式
- 计算
-
判重
-
结果写入文件
设计实现过程
-
Fraction分数类定义一个分数及分数的基本操作
- Expression表达式类定义一个表达式及计算表达式的结果
-
Function输出功能类输出结果
代码
项目目录:
主函数:(启动程序)Main.java
package TwoProject; import java.io.*; import java.util.Scanner; public class Main { public static void main(String[] args) throws IOException { // TODO 自动生成的方法存根 int titleNum; // 定义题目个数 int parameterRange; // 定义参数范围 System.out.println("**** -n [数值] 使用 -n 参数控制生成题目的个数"); System.out.println("**** -e <exercisefile>.txt -a <answerfile>.txt 对给定的题目文件和答案文件,判定答案中的对错并进行数量统计"); System.out.print("请输入命令:"); Scanner s = new Scanner(System.in); String m = s.nextLine(); String str[] = m.split("\s"); switch (str[0]) { case "-n": titleNum = Integer.valueOf(str[1].toString()); System.out.println("**** -r [数值] 使用 -r 参数控制题目中数值(自然数、真分数)的范围(数值应大于1)"); System.out.print("请输入命令:"); Scanner ss = new Scanner(System.in); String n = ss.nextLine(); String strr[] = n.split("\s"); if (strr[0].equals("-r")) { parameterRange = Integer.valueOf(strr[1].toString()); Function function = new Function(titleNum, parameterRange); function.outputExercises(); break; } else { System.out.println("输入的命令有误!"); break; } case "-e": Function function0 = new Function(); String gradePath = "Grade.txt"; function0.outputGrade(str[1], str[3], gradePath); break; default: System.out.println("输入的命令有误!"); break; } s.close(); } }
分数类 Fraction
package TwoProject; public class Fraction { private int a; // 定义分子 private int b; // 定义分母 public Fraction(String string) { // 将数字的字符串形式转换为整数、分数形式 string = string.trim(); // 清除前后空格 int c = string.indexOf("'"); // 求带分数标志“'”的索引 int d = string.indexOf("/"); // 求分数标志“/”的索引 if (c != -1) { // 数字为带分数 int z = Integer.valueOf(string.substring(0, c)); b = Integer.valueOf(string.substring(d + 1)); a = z * b + Integer.valueOf(string.substring(c + 1, d)); } else if (d != -1) { // 数字为真分数 b = Integer.valueOf(string.substring(d + 1)); a = Integer.valueOf(string.substring(0, d)); } else { // 数字为整数 a = Integer.valueOf(string); b = 1; } build(a, b); } public Fraction(int a, int b) { build(a, b); } // 数字形式转换为字符串形式 public String toString() { if (b == 1) { return String.valueOf(a); } else { int i = a / b; if (i != 0) { return String.format("%d'%d/%d", i, a - b * i, b); } else { return String.format("%d/%d", a, b); } } } // 给定分子分母构造分数 private void build(int a, int b) { if (b == 0) { throw new RuntimeException("分母不能为0"); } int c = comfactor(a, b); this.a = a / c; this.b = b / c; } // 求公因子 private int comfactor(int a, int b) { int mod = a % b; return mod == 0 ? b : comfactor(b, mod); } // 加法 a + b public Fraction add(Fraction fraction) { return new Fraction(this.a * fraction.b + fraction.a * this.b, this.b * fraction.b); } // 减法 a - b public Fraction subtraction(Fraction fraction) { return new Fraction(this.a * fraction.b - fraction.a * this.b, this.b * fraction.b); } // 乘法 a x b public Fraction multiplication(Fraction fraction) { return new Fraction(this.a * fraction.a, this.b * fraction.b); } // 除法a/b public Fraction division(Fraction fraction) { return new Fraction(this.a * fraction.b, b * fraction.a); } public double getDouble() { return a / b; } }
表达式类Expression
package TwoProject; import java.util.List; import java.util.ArrayList; import java.util.concurrent.ThreadLocalRandom; /** * * @author 辉鹏 * @param parameterRange * 参数范围 * * */ public class Expression { int parameterRange; char[] symbol = { '+', '-', '×', '÷' }; boolean isWrong = false; String s; ThreadLocalRandom random = ThreadLocalRandom.current(); public Expression(String string) { s=string.trim(); expreGenerate(s); } public Expression(int parameterRange) { super(); this.parameterRange = parameterRange; int n = random.nextInt(1, 4); char[] symchar = new char[n]; char[] symchar1 = new char[n]; for (int i = 0; i < n; i++) { int j = random.nextInt(0, 4); symchar[i] = symbol[j]; } for (int i = 0; i < n; i++) { if (symchar[i] == '+' || symchar[i] == '-') { symchar1[i] = '1'; } if (symchar[i] == '×' || symchar[i] == '÷') { symchar1[i] = '2'; } } String string = symchar1.toString().trim(); if (n == 1) { String[] str0 = new String[4]; str0[0] = createFraction().toString(); str0[1] = String.valueOf(symchar[0]); str0[2] = createFraction().toString(); str0[3] = "="; StringBuffer stringBuffer = new StringBuffer(); for (int i = 0; i < 4; i++) { stringBuffer = stringBuffer.append(str0[i] + " "); } s = stringBuffer.toString(); } else if (n == 2) { if (string == "12" || string == "22") { Object[] str1 = new String[6]; str1[0] = createFraction().toString(); str1[1] = String.valueOf(symchar[0]); str1[2] = createFraction().toString(); str1[3] = String.valueOf(symchar[1]); str1[4] = createFraction().toString(); str1[5] = "="; StringBuffer stringBuffer = new StringBuffer(); for (int i = 0; i < 6; i++) { stringBuffer = stringBuffer.append(str1[i] + " "); } s = stringBuffer.toString(); } else { String[] str2 = new String[8]; str2[0] = createFraction().toString(); str2[1] = String.valueOf(symchar[0]); str2[2] = "("; str2[3] = createFraction().toString(); str2[4] = String.valueOf(symchar[1]); str2[5] = createFraction().toString(); str2[6] = ")"; str2[7] = "="; StringBuffer stringBuffer = new StringBuffer(); for (int i = 0; i < 8; i++) { stringBuffer = stringBuffer.append(str2[i] + " "); } s = stringBuffer.toString(); } } else { if (string == "221" || string == "222") { String[] str3 = new String[10]; str3[0] = createFraction().toString(); str3[1] = String.valueOf(symchar[0]); str3[2] = createFraction().toString(); str3[3] = String.valueOf(symchar[1]); str3[4] = "("; str3[5] = createFraction().toString(); str3[6] = String.valueOf(symchar[2]); str3[7] = createFraction().toString(); str3[8] = ")"; str3[9] = "="; StringBuffer stringBuffer = new StringBuffer(); for (int i = 0; i < 10; i++) { stringBuffer = stringBuffer.append(str3[i] + " "); } s = stringBuffer.toString().trim(); } else if (string == "111" || string == "112") { Object[] str4 = new String[10]; str4[0] = createFraction().toString(); str4[1] = String.valueOf(symchar[0]); str4[2] = "("; str4[3] = createFraction().toString(); str4[4] = String.valueOf(symchar[1]); str4[5] = createFraction().toString(); str4[6] = ")"; str4[7] = String.valueOf(symchar[2]); str4[8] = createFraction().toString(); str4[9] = "="; StringBuffer stringBuffer = new StringBuffer(); for (int i = 0; i < 10; i++) { stringBuffer = stringBuffer.append(str4[i] + " "); } s = stringBuffer.toString().trim(); } else if (string == "121" || string == "122") { Object[] str5 = new String[10]; str5[0] = "("; str5[1] = createFraction().toString(); str5[2] = String.valueOf(symchar[0]); str5[3] = createFraction().toString(); str5[4] = ")"; str5[5] = String.valueOf(symchar[1]); str5[6] = createFraction().toString(); str5[7] = String.valueOf(symchar[2]); str5[8] = createFraction().toString(); str5[9] = "="; StringBuffer stringBuffer = new StringBuffer(); for (int i = 0; i < 10; i++) { stringBuffer = stringBuffer.append(str5[i] + " "); } s = stringBuffer.toString().trim(); } else { Object[] str6 = new String[8]; str6[0] = createFraction().toString(); str6[1] = String.valueOf(symchar[0]); str6[2] = createFraction().toString(); str6[3] = String.valueOf(symchar[1]); str6[4] = createFraction().toString(); str6[5] = String.valueOf(symchar[2]); str6[6] = createFraction().toString(); str6[7] = "="; StringBuffer stringBuffer = new StringBuffer(); for (int i = 0; i < 8; i++) { stringBuffer = stringBuffer.append(str6[i] + " "); } s = stringBuffer.toString().trim(); } } expreGenerate(s); } // 随机生成一个分数 Fraction createFraction() { return new Fraction(random.nextInt(1, parameterRange), random.nextInt(1, 10)); } // 返回一个表达式 private String expreGenerate(String s) { return s; } // 表达式计算 private Fraction calculate(String symbol0, Fraction left0, Fraction right0) { String symbol = symbol0; Fraction left = left0; Fraction right = right0; switch (symbol) { case "+": return left.add(right); case "-": if (left.getDouble() <= right.getDouble()) { isWrong = true; return new Fraction(1, 1); } else { return left.subtraction(right); } case "×": return left.multiplication(right); default: return left.division(right); } } // 表达式结果计算方法 public String getResult() { String string = s; String[] strings = string.split("\s+"); List<String> list = new ArrayList<String>(); for (int i = 0; i < strings.length; i++) { list.add(strings[i]); } if (string.indexOf("(") != -1) { for (int i = 0; i < list.size(); i++) { if (list.get(i).equals("=")) break; if (list.get(i).equals("(")) { list.set(i, calculate(list.get(i + 2), new Fraction(list.get(i + 1)), new Fraction(list.get(i + 3))) .toString()); for (int j = 0; j < 4; j++) { list.remove(i + 1); } } } } if (string.indexOf("×") != -1 || string.indexOf("÷") != -1) { for (int i = 0; i < list.size(); i++) { if (list.get(i).equals("=")) break; if (list.get(i).equals("×") || list.get(i).equals("÷")) { list.set(i - 1, calculate(list.get(i), new Fraction(list.get(i - 1)), new Fraction(list.get(i + 1))) .toString()); for (int j = 0; j < 2; j++) { list.remove(i); } i--; } } } if (string.indexOf("+") != -1 || string.indexOf("-") != -1) { for (int i = 0; i < list.size(); i++) { if (list.get(i).equals("=")) break; if (list.get(i).equals("+") || list.get(i).equals("-")) { list.set(i - 1, calculate(list.get(i), new Fraction(list.get(i - 1)), new Fraction(list.get(i + 1))) .toString()); for (int j = 0; j < 2; j++) { list.remove(i); } i--; } } } return list.get(0); } public String toString() { return s; } }
输出功能类Function
package TwoProject; import java.io.BufferedReader; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.util.HashSet; import java.util.Set; /** * @param titleNum * 题目个数 * @param parameterRange * 参数范围 */ public class Function { int titleNum; int parameterRange; Set<String> set = new HashSet<>(); public Function() { } public Function(int titleNum, int parameterRange) { this.titleNum = titleNum; this.parameterRange = parameterRange; } public void outputExercises() { try { PrintWriter exercisesWriter = new PrintWriter(new FileWriter("Exercises.txt")); PrintWriter answerWriter = new PrintWriter(new FileWriter("Answers.txt")); for (int i = 1; i < titleNum + 1;) { Expression expression = new Expression(parameterRange); String string = expression.getResult(); if (expression.isWrong == false && !(set.contains(string))) { set.add(string); exercisesWriter.println(i + ". " + expression.toString()); answerWriter.println((i + ". " + string)); i++; } } exercisesWriter.close(); answerWriter.close(); } catch (IOException e) { e.printStackTrace(); } System.out.println("已经生成题目和答案"); } /** * 输出结果 * * @param exercisePath * 表达式文件路径 * @param answerPath * 结果文件路径 * @param gradePath * 输出结果文件路径 */ public void outputGrade(String exercisePath, String answerPath, String gradePath) { try { BufferedReader exercisesReader = new BufferedReader(new FileReader(exercisePath)); BufferedReader answerReader = new BufferedReader(new FileReader(answerPath)); PrintWriter gradeWriter = new PrintWriter(new FileWriter(gradePath)); String exercises, answer; int c = 0, w = 0; StringBuilder correct = new StringBuilder("Correct: %d ("); StringBuilder wrong = new StringBuilder("Wrong: %d ("); while ((exercises = exercisesReader.readLine()) != null && (answer = answerReader.readLine()) != null) { int exercisesPoint = exercises.indexOf("."); int answerPoint = answer.indexOf("."); if (exercisesPoint != -1 && answerPoint != -1) { int i = Integer.valueOf(exercises.substring(0, exercisesPoint).trim()); Expression expression = new Expression(exercises.substring(exercisesPoint + 1)); Fraction ans = new Fraction(answer.substring(answerPoint + 1)); if (expression.getResult().equals(ans.toString())) { c++; correct.append(" " + i); if (c % 30 == 0) { correct.append(" "); } } else { w++; wrong.append(" " + i); if (w % 30 == 0) { wrong.append("-r "); } } } } gradeWriter.println(String.format(correct.append(" )").toString(), c)); gradeWriter.println(String.format(wrong.append(" )").toString(), w)); gradeWriter.flush(); exercisesReader.close(); answerReader.close(); gradeWriter.close(); } catch (IOException e) { e.printStackTrace(); } System.out.println("已经比较完成,并生成输出文件"); } }
测试
表达式的输出
随机输出10条20范围以内的表达式和答案
表达式: 结果:
对比上面输出的10条表达式和答案,结果
然后故意修改第2,5,6,7道题的答案,再重新运行一遍,结果如下
随机输出10000条20范围以内的表达式和答案: