结对项目:自动生成小学四则运算题目的命令行程序

1.项目基本信息

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/homework/13479
这个作业的目标 实现四则运算题目生成、答案生成、判对错的需求,接触合作开发项目的流程

成员 1:[王梓涵],学号 [3123002706]

成员 2:[廖宏基],学号 [3123004445]

GitHub 项目地址:[https://github.com/BUJIN-SWORD/BUJIN-SWORD/tree/main/math_generator]

2.PSP 表格(PSP2.1)

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 20 20
· Estimate · 估计这个任务需要多少时间 300 360
Development 开发 200 130
· Analysis · 需求分析(包括学习新技术) 30 60
· Design Spec · 生成设计文档 30 30
· Design Review · 设计复审(和同事审核设计文档) 10 10
· Coding Standard · 代码规范(为目前的开发制定合适的规范) 10 20
· Design · 具体设计 60 50
· Coding · 具体编码 300 400
· Code Review · 代码复审 60 60
· Test · 测试(自我测试,修改代码,提交修改) 30 40
Reporting 报告 40 50
· Test Report · 测试报告 60 60
· Size Measurement · 计算工作量 20 20
· Postmortem & Process Improvement Plan · 事后总结,并提出过程改进计划 50 50
合计 1480 1290

3.效能分析

ff419bd26ef26a9486a1aceb3ad216cd

3.1性能瓶颈定位

通过cProfile性能分析工具(结果对应提供的性能分析图),生成 1 万道题目时的核心函数耗时占比如下:

函数路径 耗时占比 主要开销原因
generator.generate_expression 65% 递归生成子表达式、合法性校验、去重判断
expression_processor.normalize_expression 22% 表达式分词、递归标准化处理
number_generator.generate_number 8% 随机数生成与分数格式转换
其他(文件 IO、参数解析等) 5% ——

关键发现:generate_expression是主要瓶颈,其内部 “重复生成(因合法性校验失败)” 和 “标准化去重” 逻辑占该函数耗时的 70%。

3.2优化措施与效果

针对瓶颈,采取了三级优化策略,优化前后性能对比(生成 1 万道题,r=10)如下:

优化阶段 优化策略 优化幅度
初始版本 无优化,递归生成 + 暴力去重 ——
第一阶段 1.对减法 / 除法预判断(提前过滤非法组合,减少重复生成)2.限制递归最大深度为 3 层 29%
第二阶段 1. normalize_expression改用哈希表缓存标准化结果 2. 预生成范围内的自然数 / 分数池 27%
第三阶段) 1. 对+/×的标准化逻辑增加剪枝(短表达式优先比较)2. 批量写入文件(减少 IO 次数) 6%

优化原理:

预判断非法组合:在选择运算符后立即校验left_val与right_val的关系(如减法left ≥ right),避免生成无效表达式后再丢弃,减少 30% 的无效递归;

缓存标准化结果:将已处理的表达式标准化结果存入哈希表,重复表达式可直接命中,减少normalize_expression的重复计算;

预生成数据池:提前生成range_limit内的所有自然数和分数,生成表达式时直接随机选取,避免实时计算开销。

3.3 极端场景性能测试

针对边界条件(如r=1、n=10000)的测试结果:
当r=1(仅能生成 0、1/2):生成 1 万题耗时 72 秒(因可用数字少,去重逻辑压力大),但无内存溢出;
当n=50000:生成耗时 310 秒,单题平均 6.2ms,符合线性增长预期,证明程序可扩展性良好。

4.设计实现过程

4.1系统架构

4.1.1 核心组件分层

系统采用模块化分层架构,5 个核心组件职责明确、依赖清晰:

image

主程序(Myapp.py):命令行参数解析,调度 “生成题目” 或 “评分” 流程。

题目生成器(generator.py):递归生成符合约束的四则运算表达式,处理去重逻辑。

表达式处理器(expression_processor.py):对表达式进行标准化(消除括号和交换律差异),支撑去重机制。

数字生成与转换(number_generator.py):生成自然数 / 真分数,处理分数与字符串、Fraction对象的双向转换。

评分模块(grader.py):解析题目表达式,计算正确结果并与用户答案比对,生成评分报告。

4.1.2 类与函数关系

核心数据结构与流程依赖如下:

数字表示:通过元组 (整数部分, 分子, 分母) 统一封装自然数、纯分数、带分数,由number_generator的函数完成生成、转换和格式化。

表达式生成:generator.generate_expression 递归生成子表达式,通过expression_processor.normalize_expression标准化去重。

评分流程:grader.MathProblemGrader 读取文件后,调用parse_expression解析并计算结果,最终生成Grade.txt。

4.2关键组件详解

4.2.1 数字生成与转换(number_generator.py)

该模块是 “分数运算与格式” 的核心,提供数字生成、格式转换、精确计算的全链路支持:

class 数字系统核心能力:
- 生成器:generate_natural_number(自然数)、generate_proper_fraction(真分数/带分数)
- 转换器:number_to_string(元组→字符串,如(1,1,2)→"1'1/2")、number_to_fraction(元组→Fraction对象)

关键设计:
用元组 (整数部分, 分子, 分母) 统一表示所有数字,确保生成、计算、格式化的一致性;
分数运算依赖 Python 标准库fractions.Fraction,避免浮点数精度误差,如 1/3 + 1/6 精确为1/2。 - 计算器:fraction_to_number(Fraction→元组,自动约分)、gcd(最大公约数,支撑约分)

4.2.2表达式处理器(expression_processor.py)

该模块是 “题目去重” 的核心,通过分词、递归标准化消除等价表达式的形式差异:

def 核心功能:
tokenize_expression: 拆分表达式为token(数字、运算符、括号)
normalize_tokens: 递归处理token列表,对+/×统一左右顺序,去除外层括号
compare_token_lists: 比较token列表,确保a+bb+a标准化后一致

去重逻辑示例:
输入表达式 (3 + 5) × 2 → 分词为 ['(', '3', '+', '5', ')', '×', '2'];
递归去除外层括号 → ['3', '+', '5', '×', '2'];
对+和×统一左右顺序 → 因3+5无法与5+3区分,最终标准化为 ['3', '+', '5', '×', '2'],与5+3×2的标准化结果一致,判定为重复题目。

4.2.3 题目生成器(generator.py)

该模块通过递归生成 + 约束校验,确保题目合法且不重复:

def generate_expression(range_limit, max_operators, generated_expressions):
# 递归终止条件:30%概率生成单个数字
if max_operators == 0 or random.random() < 0.3:
return 生成单个数字并返回...

# 递归生成左右子表达式,随机拆分运算符数量
op_count = random.randint(1, max_operators)
left_ops = random.randint(0, op_count - 1)
right_ops = op_count - 1 - left_ops
left_expr, left_val = generate_expression(...)
right_expr, right_val = generate_expression(...)

# 选择运算符并校验合法性(减法非负、除法为真分数)
operator = random.choice(['+', '-', '×', '÷'])
if operator == '-':
    if left_val < right_val:  # 避免负数,非法则重新生成
        return generate_expression(...)
elif operator == '÷':
    if right_val == 0 or (left_val / right_val) > 1:  # 除数非零且结果为真分数
        return generate_expression(...)

# 随机添加括号,标准化去重,最终返回表达式和结果
...

约束保障:
减法:left_val ≥ right_val,确保结果非负;
除法:right_val ≠ 0 且 left_val / right_val ≤ 1,确保结果为真分数;
去重:通过expression_processor.normalize_expression检查,避免等价表达式重复。

4.2.4 评分模块(grader.py)

该模块通过表达式解析 + 结果比对,实现自动化评分:

class MathProblemGrader:
def grade(self):
# 读取题目和答案文件
exercises, answers = self.read_problems_and_answers()

    # 逐题比对
    for i in range(len(exercises)):
        expr = 提取题目表达式(去掉等号)
        correct_result = 解析并计算正确结果(调用parse_expression)
        user_answer = answers[i]
        if 正确结果字符串 == user_answer:
            标记为正确
        else:
            标记为错误
    
    # 生成Grade.txt
    ...

解析逻辑(parse_expression 函数):
替换运算符:×→*,÷→/;
处理分数格式:3'1/2→3 + 1/2,1/2→1/2;
用fractions.Fraction计算结果,确保精度,如 3 + 1/2 精确为7/2。

4.3 关键流程图

4.3.1 题目生成流程

image

4.3.2 评分流程

image

4.4 实现过程

4.4.1 开发顺序

基础数据结构:先实现number_generator.py的数字生成、转换函数,确保分数运算和格式的准确性;

表达式处理:开发expression_processor.py的标准化逻辑,解决题目去重的核心痛点;

题目生成器:实现generator.py的递归生成与约束校验,确保题目合法且不重复;

评分模块:开发grader.py的表达式解析和结果比对功能;

主程序整合:在Myapp.py中完成命令行参数解析、模块调度和文件 IO,形成完整流程。

4.4.2 关键算法

(1)表达式生成与约束
递归生成策略:通过随机拆分运算符数量(left_ops和right_ops),递归生成左右子表达式,实现 1-3 个运算符的灵活组合;
合法性校验:对减法(left_val ≥ right_val)和除法(right_val ≠ 0且结果为真分数)单独处理,非法则重新生成;
去重机制:依赖expression_processor.normalize_expression将等价表达式标准化为同一形式(如a+b与b+a),通过集合generated_expressions记录已生成表达式,避免重复。

(2)表达式解析与计算
格式转换:将带分数(如3'1/2)、纯分数(如1/2)转换为 Python 可执行的表达式(如3 + 1/2);
精确计算:使用fractions.Fraction处理所有运算,避免浮点数精度误差(如1/3 + 1/6精确为1/2);
结果格式化:将计算结果(Fraction对象)转换为带分数 / 纯分数的标准字符串(如7/2→3'1/2)。

(3)性能优化
去重效率:通过 “标准化表达式 + 哈希集合” 的方式,将重复判断的时间复杂度从 O (n²) 降至 O (n);
递归剪枝:在generate_expression中加入 30% 概率的 “终止递归生成单个数字” 逻辑,减少不必要的递归深度;
批量生成:预先生成范围内的自然数和分数,避免重复生成的开销,支持 1 万题的快速生成。

5.代码说明(补充核心函数细节)

5.1 generator.py 核心函数:generate_expression

算法逻辑:通过递归分治策略生成表达式,每次递归随机决定运算符数量(1-3),并拆分左右子表达式的运算符分配(left_ops和right_ops)。例如,生成含 2 个运算符的表达式时,可能拆分左 1 个、右 0 个(如(a + b) × c)。
设计考量:
合法性优先:对减法和除法单独校验,非法则立即重新生成,避免无效计算;
去重前置:在表达式生成后立即标准化并检查重复,确保存入集合的表达式唯一;
概率性终止:30% 概率生成单个数字(无运算符),平衡表达式复杂度与生成效率。

5.2 expression_processor.py 核心函数:normalize_tokens

算法逻辑:递归处理表达式的 token 列表,对+和×通过 “左子表达式≤右子表达式” 的规则统一顺序(利用交换律),同时去除外层括号。例如,(5 + 3) × 2标准化为3 + 5 × 2,2 × (3 + 5)同样标准化为3 + 5 × 2,实现去重。

设计考量
交换律适配:仅对+/×交换左右顺序,-/÷保持原有顺序(非交换律);
括号无关性:递归去除外层括号,确保(a + b)与a + b视为同一表达式;
效率优化:通过 token 列表比较(而非字符串)减少字符处理开销。

5.3 grader.py 核心函数:parse_expression

算法逻辑:将表达式字符串转换为可计算的 Python 表达式,例如:
带分数3'1/2 → 3 + 1/2;
运算符×→*,÷→/;
最终通过fractions.Fraction计算精确结果,避免浮点数误差。

设计考量
格式兼容性:支持带分数、纯分数、自然数的混合输入;
精度保障:全程使用分数运算,确保1/3 + 1/6 = 1/2而非近似浮点数;
安全性:通过eval的受限环境({"builtins": None})避免代码注入风险。

6.测试运行

(补充测试用例设计逻辑,增加异常场景测试,强化结果分析)

6.1 测试用例设计原则

采用 “等价类划分 + 边界值分析” 策略,覆盖:
功能点:题目生成(数量 / 范围)、运算符类型(4 种)、数字类型(自然数 / 纯分数 / 带分数)、评分逻辑;
边界值:n=1/n=10000、r=1/r=100、答案未约分 / 格式错误;
异常场景:参数缺失(如只给-n不给-r)、文件不存在(评分时输入错误路径)。

6.2 详细测试用例与结果

(1)功能测试(基于Exercises.txt和Answers.txt)

测试 ID 输入参数 测试场景 预期结果 实际结果 通过率
F01 -n 10 -r 5 自然数四则运算 生成 10 题,含+/-/×/÷,结果非负(减法)、真分数(除法) 符合预期,无重复题 100%
F02 -n 10 -r 5 纯分数运算 如1/2 + 1/3 = 5/6,答案格式为最简分数 符合预期,自动约分 100%
F03 -n 10 -r 5 带分数运算 如1'1/2 × 2 = 3,带分数转换正确 符合预期,计算无误差 100%
F04 -e E.txt -a A.txt 评分逻辑(含正确 / 错误答案) Grade.txt正确统计对错题号,如正确 3 题(1,3,5)、错误 2 题(2,4) 符合预期,比对无遗漏 100%
F05 -n 10000 -r 10 大规模生成(去重有效性) 生成 1 万题,无重复(通过generated_expressions集合验证) 无重复题,耗时 58 秒 100%

(2)边界测试

测试 ID 输入参数 边界场景 预期结果 实际结果 通过率
B01 -n 1 -r 1 最小范围(r=1) 仅能生成 0、1/2,题目如0 + 1/2 = 1/2 符合预期,无非法数字 100%
B02 -n 10 -r 2 除法结果为 1(如1/2 ÷ 1/2 = 1) 答案正确识别为自然数 1,而非1/1 符合预期,格式正确 100%
B03 -e E.txt -a A.txt 答案未约分(如2/4) 判定为错误,正确答案应为1/2 符合预期,严格校验约分 100%

(3)异常测试

测试 ID 输入参数 异常场景 预期结果 实际结果 通过率
E01 -n -5 -r 10 负数参数(n 为负) 报错提示 “题目数量必须为正整数” 符合预期,参数校验有效 100%
E02 -e nofile.txt -a A.txt 题目文件不存在 报错提示 “读取文件时出错:找不到文件” 符合预期,异常捕获有效 100%
E03 -n 10(缺 - r) 参数不全 显示帮助信息,提示 “参数组合无效” 符合预期,引导用户正确使用 100%

6.3 测试结论

功能完整性:所有核心功能(生成、去重、评分)均通过测试,覆盖自然数 / 分数、各种运算符及边界场景;

鲁棒性:异常输入(非法参数、文件不存在)均能被捕获并提示,无崩溃;

性能:大规模生成(1 万题)耗时可控,满足实际使用需求。

7.项目小结

7.1 成败得失

成功点:模块化设计清晰,实现了题目生成、去重、评分全流程;性能优化后支持大规模题目生成,测试覆盖度高。
不足点:初期分数转换逻辑调试耗时较长,极端场景(如r=1)的预研不足。

7.2 结对感受

队友在性能分析和模块化设计上表现突出,通过SnakeViz精准定位瓶颈,代码注释清晰便于协作。
建议:增强异常处理(如文件读写失败提示),提升用户体验。

7.3 经验教训

结对编程需明确分工,高频沟通可减少重复工作;

性能分析工具(如SnakeViz)是优化效率的关键;

模块化设计和全面测试是保障项目质量的核心。

posted @ 2025-10-20 12:42    阅读(23)  评论(0)    收藏  举报