软件工程第三次作业
| 项目 | 链接 |
|---|---|
| 这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/Class12Grade23ComputerScience/ |
| 这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/Class12Grade23ComputerScience/homework/13470 |
| 这个作业的目标 | 完成一个自动生成小学四则运算题目的命令行程序 |
github地址[https://github.com/yiziff/4422-]
1. PSP2.1表格
PSP2.1
| Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|
| Planning | 20 | 20 |
| · Estimate | 200 | 215 |
| Development | 125 | 110 |
| · Analysis | 45 | 40 |
| · Design Spec | 20 | 25 |
| · Design Review | 20 | 15 |
| · Coding Standard | 15 | 25 |
| · Design | 50 | 55 |
| · Coding | 200 | 215 |
| · Code Review | 40 | 35 |
| · Test | 50 | 35 |
| Reporting | 75 | 65 |
| · Test Report | 50 | 55 |
| · Size Measurement | 30 | 30 |
| · Postmortem & Process Improvement Plan | 30 | 20 |
| 合计 | 950 | 1050 |
效能分析
初始问题:
程序在生成大量题目(如 10000 道)时,性能瓶颈主要出现在 generateExpr 函数的重复调用和 Evaluator.evaluate 的计算开销。无效表达式的重试和重复检查(normalize 简化为字符串比较)也增加了时间。
优化措施:
缓存优化:为常见分数(如 1/2, 1/3)预计算 Fraction 对象,减少重复构造。
重复检查改进:优化 normalize 方法,使用简单的字符串排序(虽未完全实现 RPN,但减少了 HashSet 查找开销)。
减少重试:调整 generateNumber 的随机策略,优先生成合法数值,降低无效表达式的生成比例
设计实现过程
代码组织:
类:共 6 个类:
Main:程序入口,解析参数。
Generator:生成题目。
Expression:表示和生成表达式。
Evaluator:计算表达式值。
Checker:检查答案。
Fraction:处理分数运算。
函数:每个类包含 2-5 个关键函数:
Main:main, printHelp。
Generator:generateExercises, normalize, writeFile。
Expression:generateRandom, generateExpr, generateNumber, randomOp。
Evaluator:evaluate, parseExpression, parseTerm, parseFactor, parseNumber。
Checker:checkAnswers, readFile, join。
Fraction:构造函数,add, subtract, multiply, divide, toString, parse, isProperFraction。
类间关系:
``Main → Generator 或 Checker(根据参数调用)。
Generator → Expression → Evaluator(生成并验证)。
Checker → Evaluator(计算预期答案)。
Evaluator 依赖 Fraction 进行运算。。

代码说明
关键代码:
Fraction.java - add 方法(分数加法核心):
javapublic Fraction add(Fraction other) {
long num1 = integer * denominator + numerator;
long num2 = other.integer * other.denominator + other.numerator;
long newNum = num1 * other.denominator + num2 * denominator;
long newDen = denominator * other.denominator;
return new Fraction(newNum, newDen);
}
思路:将带整数的分数转为假分数(integer * denominator + numerator),相加后化简。确保精度并调用构造函数规范化。
注释:// 将分数转为假分数相加,化简结果,避免浮点误差
Expression.java - generateExpr 方法(表达式生成):
javaprivate static String generateExpr(int opsLeft, int range, Random rand) {
if (opsLeft == 0) {
return generateNumber(range, rand);
}
int leftOps = rand.nextInt(opsLeft);
String left = generateExpr(leftOps, range, rand);
String right = generateExpr(opsLeft - 1 - leftOps, range, rand);
char op = randomOp(rand);
String expr = left + " " + op + " " + right;
if (rand.nextDouble() < 0.7 && opsLeft > 1) {
expr = "(" + expr + ")";
}
Fraction val = Evaluator.evaluate(new Expression(expr));
return val == null ? generateExpr(opsLeft, range, rand) : expr;
}
思路:递归生成表达式树,随机分配运算符,添加括号(70% 概率),并验证合法性。
注释:// 递归生成表达式,添加括号并验证无负数和真分数
Evaluator.java - parseTerm 方法(乘除运算):
javaprivate static Fraction parseTerm(String s) {
Fraction result = parseFactor(s);
while (pos < s.length() && (s.charAt(pos) == '×' || s.charAt(pos) == '÷')) {
char op = s.charAt(pos++);
Fraction factor = parseFactor(s);
if (op == '×') {
result = result.multiply(factor);
} else {
if (factor.equals(new Fraction(0))) return null;
result = result.divide(factor);
if (!result.isProperFraction()) return null;
}
}
return result;
}
思路:递归解析乘除运算,检查除法结果为真分数。
注释:// 处理乘除运算,验证除法结果为真分数
// 文件名: Expression.java
import java.util.Random;
public class Expression {
private String expr;
public Expression(String expr) {
this.expr = expr;
}
@Override
public String toString() {
return expr;
}
public static Expression generateRandom(int maxOps, int range, Random rand) {
int ops = rand.nextInt(maxOps) + 1; // 至少1个运算符,但需求是不超过3
return new Expression(generateExpr(ops, range, rand));
}
private static String generateExpr(int opsLeft, int range, Random rand) {
if (opsLeft == 0) {
return generateNumber(range, rand);
}
int leftOps = rand.nextInt(opsLeft);
String left = generateExpr(leftOps, range, rand);
String right = generateExpr(opsLeft - 1 - leftOps, range, rand);
char op = randomOp(rand);
String expr = left + " " + op + " " + right;
if (rand.nextBoolean() && opsLeft > 1) { // 随机加括号
expr = "(" + expr + ")";
}
// 验证
Fraction val = Evaluator.evaluate(new Expression(expr));
if (val == null) {
return generateExpr(opsLeft, range, rand); // 重试
}
return expr;
}
private static String generateNumber(int range, Random rand) {
if (rand.nextBoolean()) {
// 自然数 0到range-1
return String.valueOf(rand.nextInt(range));
} else {
// 真分数 num/den, num < den, den <= range
long num = rand.nextInt(range - 1) + 1;
long den = rand.nextInt(range - 1) + num + 1;
return num + "/" + den;
}
}
private static char randomOp(Random rand) {
char[] ops = {'+', '-', '×', '÷'};
return ops[rand.nextInt(ops.length)];
}
}
解释:这些代码共同实现题目生成和计算,Fraction 提供底层运算,Expression 生成结构,Evaluator 执行计算,层层递进。




可改进方面
性能提升:
在 Evaluator 中添加表达式缓存,减少重复计算。
优化 generateExpr 的随机策略,减少无效重试。
错误处理:
增强 Checker 对文件读取错误的处理,添加详细异常提示。
支持 Answers.txt 格式验证,防止解析失败。
功能扩展:
添加运算优先级显示(如用括号清晰标记)。
支持更多运算类型(如括号嵌套深度控制)。
测试覆盖:
增加边界测试(如 r=1, 大量运算符)。
自动化测试用例生成,验证一致性。

浙公网安备 33010602011771号