软件工程第三次作业

项目 链接
这个作业属于哪个课程 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 进行运算。。

屏幕截图 2025-10-22 103023

代码说明

关键代码:

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 执行计算,层层递进。

image

image
image
image

可改进方面

性能提升:

在 Evaluator 中添加表达式缓存,减少重复计算。
优化 generateExpr 的随机策略,减少无效重试。

错误处理:

增强 Checker 对文件读取错误的处理,添加详细异常提示。
支持 Answers.txt 格式验证,防止解析失败。

功能扩展:

添加运算优先级显示(如用括号清晰标记)。
支持更多运算类型(如括号嵌套深度控制)。

测试覆盖:

增加边界测试(如 r=1, 大量运算符)。
自动化测试用例生成,验证一致性。

posted @ 2025-10-22 10:47  朱一凡  阅读(10)  评论(0)    收藏  举报