自动生成小学四则运算题目的命令行工具设计与实现
在软件工程实践中,开发一个能够自动生成小学四则运算题目的工具是一个经典的算法挑战。该工具不仅需要支持分数的加减乘除,还要处理运算符优先级、括号嵌套,并确保生成的题目不重复且符合特定的数学约束(如减法结果不能为负,除法结果需为真分数)。本文将从核心架构、算法逻辑及异常处理等方面探讨该系统的实现方案。
核心架构设计
系统采用模块化设计,主要分为数值计算模块、表达式生成模块、结构化去重模块以及批改验证模块。通过定义抽象语法树(AST)来表示数学表达式,可以有效处理运算顺序及表达式的结构化分析。
1. 数值处理:RationalNumber 类
为了精确处理真分数和带分数,我们设计了自定义的分数类。该类负责维护分子的最简形式,并支持与带分数格式(如 1'1/2)的相互转换。
class RationalNumber:
def __init__(self, num, den=1):
if den == 0:
raise ZeroDivisionError("分母不可为零")
common = self._get_gcd(abs(num), abs(den))
self.num = num // common
self.den = den // common
if self.den < 0:
self.num = -self.num
self.den = -self.den
@staticmethod
def _get_gcd(a, b):
while b:
a, b = b, a % b
return a
def __add__(self, other):
return RationalNumber(self.num * other.den + other.num * self.den, self.den * other.den)
def __sub__(self, other):
res_num = self.num * other.den - other.num * self.den
if res_num < 0:
raise ValueError("减法结果不能为负数")
return RationalNumber(res_num, self.den * other.den)
def format_output(self):
if self.den == 1:
return str(self.num)
integer_part = abs(self.num) // self.den
remainder = abs(self.num) % self.den
if integer_part == 0:
return f"{self.num}/{self.den}"
return f"{integer_part}'{remainder}/{self.den}"
2. 表达式生成与树形结构
题目通过递归方式构建二叉树。每个叶子节点代表一个操作数,非叶子节点代表运算符。通过控制树的深度,可以限制运算符的数量(不超过3个)。
class ExpressionNode:
def __init__(self, op=None, left=None, right=None, val=None):
self.op = op
self.left = left
self.right = right
self.val = val
def calculate(self):
if self.op is None:
return self.val
l_val = self.left.calculate()
r_val = self.right.calculate()
if self.op == '+': return l_val + r_val
if self.op == '-': return l_val - r_val
if self.op == '*': return l_val * r_val
if self.op == '/':
if r_val.num == 0: raise ValueError("除数不能为零")
return RationalNumber(l_val.num * r_val.den, l_val.den * r_val.num)
算法关键点:查重与性能
题目去重是该项目的难点。简单的字符串比较无法识别 1 + 2 与 2 + 1 这种逻辑等价的题目。我们引入了结构化哈希机制:对于满足交换律的运算符(+ 和 *),在生成哈希前对左右子树进行排序归一化处理。这样,逻辑相同的表达式将映射到同一个哈希值,从而实现高效去重。
在性能优化方面,初版 GCD 算法采用递归遍历效率较低,后改为欧几里得算法。针对大规模题目生成(如 10,000 道题),通过减少随机数生成的冗余调用,显著提升了系统的响应速度。
异常处理与健壮性
在程序运行过程中,需严格监控以下异常场景:
- 运算合法性: 实时捕获
ZeroDivisionError(除零)和ValueError(减法负数结果),一旦捕获则舍弃当前子树并重新生成。 - IO 安全: 在读取题目文件和答案文件进行批改时,校验行数是否匹配,处理文件不存在或编码错误的情况。
- 重试机制: 为生成逻辑设置最大重试计数(如 1000 次),防止因参数限制过死导致程序陷入死循环。
功能演示与验证
该程序通过命令行交互,支持两个主要功能:生成题目和批改题目。
1. 题目生成
执行 python main.py -n 10 -r 10,程序将在当前目录创建 Exercises.txt(题目)和 Answers.txt(标准答案)。数值范围控制在 10 以内,且包含真分数和带分数运算。
2. 自动批改
执行 python main.py -e Exercises.txt -a UserAnswers.txt,程序将对比用户提交的答案与标准答案,并将统计结果输出至 Grade.txt。报告包含正确题目编号、错误题目编号及其汇总情况。
开发进度记录 (PSP 表)
| 阶段 | 预估耗时 (min) | 实际耗时 (min) |
|---|---|---|
| 需求分析与技术研究 | 40 | 35 |
| 具体设计与文档撰写 | 60 | 60 |
| 核心代码实现 | 120 | 130 |
| 性能调优与重构 | 30 | 45 |
| 测试与 Bug 修复 | 30 | 25 |
通过本次项目的开发,我们深入理解了 AST 在数学表达式处理中的应用,并在实际编码中权衡了递归算法的灵活性与大规模生成的性能需求。通过引入结构化哈希,解决了四则运算题目查重的技术难点,保证了题库的质量与多样性。
