第三周结对项目

第三周项目:小学四则运算题目自动生成器

1. 作业信息

项目 内容
这个作业属于哪个课程 计科 24 级 56 班
这个作业要求在哪里 第三周结对项目
这个作业的目标 实现一个自动生成小学四则运算题目的命令行程序,并支持答案批改、Git 版本管理和技术文档整理。
项目成员 3124004049
完成方式 本项目由一人完成。
GitHub 项目地址 onychen/gdut-software-engineering-2026(项目目录:3124004049/pair_arithmetic/
开发环境 Windows 11、Python 3.13、Python 标准库

2. 项目简介与运行方式

程序提供两种命令行模式:

# 生成 10 道数值范围为 [0, 10) 的题目
python main.py -n 10 -r 10 --seed 42

# 批改题目文件与学生答案文件
python main.py -e Exercises.txt -a StudentAnswers.txt

生成模式会在当前工作目录写出 Exercises.txtAnswers.txt;批改模式会写出 Grade.txt。示例格式如下:

Correct: 8 (1, 3, 5, 6, 7, 8, 9, 10)

Wrong: 2 (2, 4)

实现的功能和约束如下。

编号 作业要求 实现方式
1 使用 -n 控制题目数量、使用 -r 控制范围 argparse 解析参数;生成模式缺少 -r 会显示帮助错误。
2 支持自然数、真分数与带分数 使用 fractions.Fraction;支持 3/52’3/8,也兼容 ASCII 单引号。
3 减法中间结果不为负 构造减法节点前比较左右子树,必要时交换,保证 left >= right
4 除法结果为真分数 仅接受严格满足 0 < left / right < 1 的除法节点。
5 每题不超过 3 个运算符 随机生成 1 至 3 个运算符的表达式树。
6 一次生成中题目不重复 +× 的左右子树规范串排序;-÷ 保留顺序后放入集合去重。
7 输出题目和标准答案文件 分别写入 Exercises.txtAnswers.txt
8 支持 10,000 道题 固定种子下实际生成 10,000 道互不重复题目。
9 支持批改 重新解析每一行题目,精确比较答案,输出 Correct / Wrong 题号列表。

3. PSP2.1 表格

单位:分钟。以下记录按本项目的个人完成过程整理。

PSP2.1 Personal Software Process Stages 预估耗时 实际耗时
Planning 计划 20 18
· Estimate 估计这个任务需要多少时间 20 18
Development 开发 285 296
· Analysis 需求分析(包括学习新技术) 35 32
· Design Spec 生成设计文档 30 36
· Design Review 设计复审 20 22
· Coding Standard 代码规范 10 8
· Design 具体设计 40 43
· Coding 具体编码 110 118
· Code Review 代码复审 15 14
· Test 测试(自我测试、修改代码、提交修改) 25 23
Reporting 报告 60 59
· Test Report 测试报告 25 24
· Size Measurement 计算工作量 10 10
· Postmortem & Process Improvement Plan 事后总结并提出过程改进计划 25 25
合计 365 373

4. 效能分析

4.1 测试方法与结果

使用固定随机种子执行以下命令,以便复现生成结果:

python tools\benchmark.py
python -m cProfile -s cumulative tools\benchmark.py

本机一次实际运行结果:

指标 结果
生成数量 10,000
规范键去重后的数量 10,000
普通计时耗时 0.3215 秒
cProfile 总函数调用次数 3,648,013
cProfile 总耗时 1.115 秒

cProfile 会插入统计开销,因此其总耗时不能与普通计时直接比较。累计耗时最高的业务函数是 generate_problems() 和递归的 make_candidate();随后是 Fraction 的构造、表达式求值和规范键生成。

4.2 分析与改进

题目生成的主要开销来自构建候选表达式并校验减法、除法限制。当前实现把约束放在每个二叉树节点的构造阶段:减法直接调整左右子树顺序,除法只保留真分数结果;不符合条件的候选立即丢弃,避免生成后再对整棵树进行额外的全树校验。

去重使用 set 保存表达式规范键,平均插入和查询复杂度为 O(1)。在固定范围下,整体生成过程对题目数量近似线性;当范围太小、无法提供足量互异题目时,程序达到重试上限后会明确报错,不会无限循环。

5. 设计实现过程

5.1 模块组织

文件 职责 主要接口
main.py 命令行入口、生成/批改模式分发、文件输出 build_parser()run()
expression.py 表达式树、求值、最小括号输出、规范键 Exprevaluatedrender()canonical()
fraction_utils.py 整数/真分数/带分数解析与格式化 parse_number()format_fraction()
generator.py 合法题目生成、去重和重试上限 make_candidate()generate_problems()
parser.py 递归下降表达式解析 tokenize()parse_expression()parse_answer()
grader.py 读取文件、逐题批改、生成报告 grade_files()GradeResult
tools/benchmark.py 10,000 题性能验证 main()
tests/test_project.py 自动化单元测试与边界测试 10 个测试用例

模块依赖关系:

main.py ──┬── generator.py ──┬── expression.py ── fraction_utils.py
          │                  └── fraction_utils.py
          └── grader.py ──── parser.py ────────── expression.py

5.2 核心设计

每道题用不可变的二叉表达式树 Expr 表示。叶子节点保存 Fraction,非叶子节点保存运算符与左右子树。因此求值、输出题目、生成答案和去重都针对同一份数据结构进行,避免多份字符串状态不一致。

为了处理题目要求的“有限次交换 +× 左右操作数”的重复定义,canonical() 会递归生成规范串:加法和乘法节点先比较两侧子树的规范串并排序;减法、除法保持左右顺序。这样 23 + 4545 + 23 会判重,3 + (2 + 1)1 + 2 + 3 也会判重,而 1 + 2 + 33 + 2 + 1 不会被误判为同题。

表达式输出会遵循优先级并保留必要括号。例如右子树与父节点同优先级但运算符不同的 8 + (5 - 3) 不会被错误输出为 8 + 5 - 3;解析后仍和原表达式的值一致。

6. 测试结果

运行命令:

python -m unittest discover -s tests -v

当前结果:10 个测试全部通过。

编号 测试内容 预期结果 结果
T01 真分数、带分数的解析与格式化 19/8 输出为 2’3/8 通过
T02 精确分数计算 1/6 + 1/8 = 7/24 通过
T03 加法交换律与嵌套交换去重 指定等价式判重,非等价式不判重 通过
T04 非法表达式 含非法字符时抛出格式错误 通过
T05 同优先级右子树括号 输出并重新解析后计算值不变 通过
T06 300 道随机生成 无重复、运算符不超过 3、每个节点满足约束 通过
T07 生成—解析闭环 100 道题重新解析后结果与原树一致 通过
T08 10,000 道题生成 题目数量为 10,000 通过
T09 混合答案批改 正确题和错误题的题号列表正确 通过
T10 缺失答案 缺失行对应题目判错且程序不崩溃 通过

7. 项目小结

本项目把“出题、求值、显示、解析、批改”统一在表达式树和精确有理数模型上。使用 Fraction 后,分数约分、带分数格式化和答案等价比较都不需要手工处理浮点误差;通过递归下降解析器,可以对输出题目重新计算并用于批改,形成完整闭环。

后续可继续扩展图形界面、难度分级或更多可配置项,但命令行版本已经覆盖作业的核心生成、去重、10,000 题和判题要求。

posted @ 2026-09-21 00:13  oNyChEn123  阅读(6)  评论(0)    收藏  举报