四则运算
成员 | 谢希哲(3223004776)、贺海伦(3223004773) |
---|---|
这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/ |
这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/homework/13479 |
这个作业的目标 | 实现一个自动生成小学四则运算题目的命令行程序,并练习如何对程序进行性能测试和改进 |
我们的github账号 | https://github.com/scissssor/3223004776 https://github.com/Playerhh/python-calculator |
1 PSP表格(包括预估与实际耗时)
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
· Estimate | · 估计这个任务需要多少时间 | 10 | 15 |
Development | 开发 | ||
· Analysis | · 需求分析(包括学习新技术) | 40 | 40 |
· Design Spec | · 生成设计文档 | 20 | 10 |
· Design Review | · 设计复审 | 20 | 15 |
· Coding Standard | · 代码规范(为目前的开发制定合适的规范) | 10 | 20 |
· Design | · 具体设计 | 45 | 90 |
· Coding | · 具体编码 | 150 | 180 |
· Code Review | · 代码复审 | 30 | 25 |
· Test | · 测试(自我测试,修改代码,提交修改) | 30 | 90 |
Reporting | 报告 | ||
· Test Report | · 测试报告 | 120 | 120 |
· Size Measurement | · 计算工作量 | 20 | 20 |
· Postmortem & Process Improvement Plan | · 事后总结,并提出过程改进计划 | 10 | 15 |
合计 | 505 | 640 |
2 效能分析
2.1 初始性能分析图:

最耗时函数:generate_expression(优化前)
def generate_expression(self, max_operators: int) -> ExpressionNode:"""递归生成表达式"""if max_operators == 0:return ExpressionNode(value=self.generate_number())# 增加更多运算符选择,减少除法的概率operators = ['+', '×', '-', '-', '+', '×'] # 重复加法和乘法,减少减法if max_operators > 1: # 只有在有多个运算符时才考虑除法operators.extend(['÷'])operator = random.choice(operators)# 更灵活地分配运算符数量if max_operators == 1:left_operators = 0right_operators = 0else:left_operators = random.randint(0, max_operators - 1)right_operators = max_operators - 1 - left_operatorsleft_expr = self.generate_expression(left_operators)right_expr = self.generate_expression(right_operators)# 对于减法和除法,确保条件满足if operator == '-':left_val = left_expr.evaluate()right_val = right_expr.evaluate()if left_val < right_val:left_expr, right_expr = right_expr, left_exprelif operator == '÷':left_val = left_expr.evaluate()right_val = right_expr.evaluate()# 放宽除法条件:允许结果为1,但不允许大于1if left_val >= right_val:# 重新生成右表达式,使其大于左表达式for _ in range(10): # 最多尝试10次right_expr = self.generate_expression(right_operators)right_val = right_expr.evaluate()if right_val > left_val and right_val != FractionNumber(0):breakelse:# 如果无法生成合适的右表达式,改用乘法operator = '×'return ExpressionNode(operator=operator, left=left_expr, right=right_expr)
性能瓶颈分析:
- 表达式生成效率低,重复率高:频繁调用函数generate_expression
- 字符串转换效率低:频繁转换分数格式
- 题目约束条件高,使满足的表达式组合有限
改进思路:
- 增加加法和乘法的概率(没有负数和真分数的限制),减少减法除法使用(避免产生负数和出现分数)
- 优化函数generate_expression,预先生成运算符组合,提取验证逻辑,减少重复代码
generate_expression(优化后)
def generate_expression(self, max_operators: int) -> ExpressionNode:"""递归生成表达式"""if max_operators == 0:return ExpressionNode(value=self.generate_number())# 修正运算符池的定义operators_pool = {1: ['+', '×', '-', '+', '×'], # 1个运算符2: ['+', '×', '-', '+', '×'], # 2个运算符3: ['+', '×', '-', '÷', '+', '×'] # 3个运算符}# 确保 max_operators 在有效范围内if max_operators not in operators_pool:max_operators = min(max_operators, max(operators_pool.keys()))operator = random.choice(operators_pool[max_operators])# 更灵活地分配运算符数量if max_operators == 1left_operators = 0right_operators = 0else:left_operators = random.randint(0, max_operators - 1)right_operators = max_operators - 1 - left_operatorsleft_expr = self.generate_expression(left_operators)right_expr = self.generate_expression(right_operators)# 对于减法和除法,确保条件满足if operator in ['-', '÷']:return self._validate_operation(operator, left_expr, right_expr, right_operators)return ExpressionNode(operator=operator, left=left_expr, right=right_expr)
2.2 优化后性能分析图:

3 设计实现过程
代码采用面向对象的设计思想,主要包含以下类:
arithmetic.py
├── FractionNumber (分数处理类)
├── ExpressionNode (表达式树节点类)
├── ProblemGenerator (题目生成器类)
├── Grader (题目批改器类)
└── main() (主程序入口)
3.1 总体流程图

3.2 关键函数流程图
1.表达式计算流程图

2.批改系统流程图

4 关键代码说明
4.1 表达式实现(ExpressionNode 类)
设计思路: 使用二叉树结构表示算术表达式,在计算过程中同时验证算术规则(减法不产生负数,除法结果为真分数)
class ExpressionNode:def evaluate(self) -> FractionNumber:"""递归计算表达式值,同时验证运算规则"""if self.is_leaf():return self.valueleft_val = self.left.evaluate()right_val = self.right.evaluate()if self.operator == '-':# 确保不产生负数if left_val < right_val:raise ValueError("Subtraction result cannot be negative")return left_val - right_valelif self.operator == '÷':# 确保结果是真分数if left_val >= right_val:raise ValueError("Division result must be a proper fraction")return left_val / right_val
4.2 题目生成与去重机制
设计思路: 通过规范化表达式形式(对交换律运算排序)实现题目去重,确保生成的题目在数学本质上不重复
def generate_unique_problem(self, max_attempts=1000) -> Tuple[str, str]:"""生成不重复题目的核心算法"""for _ in range(max_attempts):try:operators_count = random.randint(1, 3)expression = self.generate_expression(operators_count)# 检查计算过程是否有效result = expression.evaluate()# 规范化表达式形式用于去重normalized_form = expression.normalize_form()if normalized_form in self.generated_forms:continue # 跳过重复题目self.generated_forms.add(normalized_form)return f"{expression.to_string()} =", str(result)except (ValueError, ZeroDivisionError):continue # 规则验证失败,重新生成
5 测试运行
5.1 生成的题目与计算结果
文件exercises.txt与Answers.txt

5.2 批改结果
文件grade.txt

生成20个题目与答案后批改,检查可得所有题目满足作业需求:没有产生负数、除法结果为真分数、运算符个数不超过3个、生成的题目没有重复。
检查答案与批改结果可得,批改结果正确
6 项目小结
一开始读题目觉得有点点绕,后面我两就说先把流程图理出来吧。然后也查了csdn上面一些案例,代码不停报错就不停问ai一点点修改,反正最后也许应该是弄出来了。感觉两个人一起做确实比上一次一个人做起来轻松很多。不只是任务量,有商有量的话心态也会好一点。
在此感谢hhl同学对我的大力支持,我有点容易焦虑又没耐心,但是hhl会告诉我时间还很够啊已经理清楚啦balabala的,有效缓解!
一开始要求的实现有点复杂,尤其是查重复要全面且快捷,后面测试与效率分析的再修改有点难度和麻烦,感谢xxz的耐心测试,xxz做事非常耐心且全面,认真研究课题使我们能够更好的敲代码和博客,提供了非常大的帮助,完成这份项目她功不可没!!!
两个人的合作使我学习到了很多,共同作业也完成的更轻松,遇到难题两个人共同解决的解决的过程也使我们减少了很多负面情绪,相互开导非常有效!