四则运算系统

news/2025/3/17 12:47:13/文章来源:https://www.cnblogs.com/huanghi4833/p/18776021
这个作业属于哪个课程 软件工程2班
这个作业要求在哪里 结对项目
这个作业的目标 团队共同实现一个自动生成小学四则运算题目的命令行程序

合作人员

  • 朱雅子 3223004823
  • 黄海怡 3223004296

项目地址Github链接

一、PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 30 30
· Estimate 估计这个任务需要多少时间 10 10
Development 开发 240 180
· Analysis 需求分析 (包括学习新技术) 40 30
· Design Spec 生成设计文档 30 10
· Design Review 设计复审 30 40
· Coding Standard 代码规范 (制定开发规范) 10 10
· Design 具体设计 30 20
· Coding 具体编码 120 90
· Code Review 代码复审 30 20
· Test 测试(自我测试,修改代码,提交修改) 60 60
Reporting 报告 60 30
· Test Report 测试报告 30 30
· Size Measurement 计算工作量 10 10
· Postmortem 事后总结, 并提出过程改进计划 20 20
Total 合计 670 600

二、项目简介

实现功能

开发一个具有图形界面的四则运算题目生成工具,支持自定义题目数量、数值范围和真分数选项,能够自动生成题目文件(Exercises.txt)和答案文件(Answers.txt),并提供答案批改功能(生成Grade.txt)。

使用方法

点击main函数运行,弹出窗口。点击是否要真分数,输入生成的式子数量和范围。点击生成按钮,就可以在Exercises.txt看见式子,在Answers.txt看见答案。(注:如果弹出提示范围不够大需要重新输入范围)生成后会弹出窗口提示。可以点击批改按钮选择要批改的txt文件。(测试,可以把Answers.txt的内容复制粘贴到要批改的答案文档,修改要测试的答案号码来测试批改功能)批改完成后会弹出提示窗口,批改结果可以在Grade.txt中查看。

项目结构

MathGeneratorGUI
├── init() # 初始化GUI窗口
├── create_widgets() # 创建界面组件(输入框、按钮等)
├── generate_expression() # 递归生成表达式树
├── normalize_expression()# 规范化表达式(排序操作数)
├── generate_number() # 生成数字(自然数或真分数)
├── calculate() # 计算表达式值
├── validate_input() # 验证用户输入合法性
├── generate() # 主生成流程(写入文件)
├── grade_answers() # 批改答案功能
└── 辅助方法
├── _format_answer() # 格式化答案输出
├── _parse_answer() # 解析答案字符串
└── _read_answer_file()# 读取答案文件

三、效能分析

性能的改进

思路

考虑到以下优点,将运算式子的生成模式改进为采用二叉树的递归算法,从有想法到实现耗时一小时。

  • 二叉树具有天然的层级优势,可控制剩余运算符数量
  • 将随机数与随机运算符分成两部分

效果


分析:根据代码分析,耗时最大的函数是generate_expression及其调用的normalize_expression。
改进方法

  1. 优化除法条件判断
    问题:允许分数时,除法要求结果不能为整数,导致多次重试
    改进:允许除法结果为整数,简化条件判断
  2. 优化查重数据结构
    问题:字符串集合查重效率低
    改进:使用数值哈希(如表达式结果)辅助查重

四、代码设计

函数设计

流程图

程序主流程

核心函数流程

1、generate_expression

2、normalize_expression

3、Generate

五、核心代码

generate_expression递归生成四则运算表达式,确保数学合法性并避免重复。

def generate_expression(self, remaining_ops, range_limit, allow_fraction, generated):MAX_RETRIES = 100for _ in range(MAX_RETRIES):try:if remaining_ops == 0:# 生成原子表达式(单个数字)# 递归终止条件(最低一层,都是数字)num = self.generate_number(range_limit, allow_fraction)expr = str(num)if expr in generated:continuegenerated.add(expr)return expr, numop = random.choice(['+', '-', '×', '÷'])left_ops = random.randint(0, remaining_ops - 1)right_ops = remaining_ops - 1 - left_opsleft_exp, left_val = self.generate_expression(left_ops, range_limit, allow_fraction, generated)right_exp, right_val = self.generate_expression(right_ops, range_limit, allow_fraction, generated)# 生成左右子树立即规范化left_exp = self.normalize_expression(left_exp)right_exp = self.normalize_expression(right_exp)if op == '-' and left_val < right_val:left_exp, right_exp = right_exp, left_expleft_val, right_val = right_val, left_valif op == '÷':for _ in range(MAX_RETRIES):if right_val != 0 and not (allow_fraction and (left_val / right_val).denominator == 1):breakright_exp, right_val = self.generate_expression(right_ops, range_limit, allow_fraction,generated)else:continue# 按数值排序操作数if op in ['+', '×']:if left_val > right_val:left_exp, right_exp = right_exp, left_expleft_val, right_val = right_val, left_valexpr = f"{left_exp} {op} {right_exp}"else:expr = f"{left_exp} {op} {right_exp}"if remaining_ops > 1:expr = f"({expr})"# 直接返回规范化后的表达式if expr in generated:continuegenerated.add(expr)value = self.calculate(op, left_val, right_val)return expr, valueexcept (ValueError, ZeroDivisionError):continueraise RuntimeError(f"无法生成合法表达式,建议扩大数值范围(当前r={range_limit})")

normalize_expression规范化表达式结构,消除交换律导致的重复。

def normalize_expression(self, expr): #让所有表达式规范化,便于查重# 移除所有括号expr = expr.replace('(', '').replace(')', '')def sort_sub_expressions(e):# 递归拆分表达式并排序if '×' in e or '÷' in e:# 处理乘除优先parts = []current = []ops = []for c in e:if c in ['×', '÷']:parts.append(''.join(current).strip())ops.append(c)current = []else:current.append(c)parts.append(''.join(current).strip())# 对乘法项排序(数值从小到大)for i in range(len(parts)):if i < len(ops) and ops[i] == '×':parts[i] = self.normalize_expression(parts[i])parts[i + 1] = self.normalize_expression(parts[i + 1])# 按数值排序left = self._parse_number(parts[i])right = self._parse_number(parts[i + 1])if left > right:parts[i], parts[i + 1] = parts[i + 1], parts[i]# 重组表达式sorted_expr = parts[0]for i in range(len(ops)):sorted_expr += f' {ops[i]} {parts[i + 1]}'return sorted_exprelse:# 处理加法add_parts = e.split('+')# 转换为数值排序add_nums = [self._parse_number(p) for p in add_parts]sorted_parts = sorted(zip(add_nums, add_parts), key=lambda x: x[0])return '+'.join([p[1] for p in sorted_parts])return sort_sub_expressions(expr)

六、测试

以下是以测试代码形式重组的10个关键测试用例,附带正确性验证说明。所有测试基于unittest框架,需与主程序文件main.py配合使用

 # 测试用例1:边界值输入验证def test_min_valid_input(self):with patch.object(self.gui, 'n_entry') as mock_n, \patch.object(self.gui, 'r_entry') as mock_r:mock_n.get.return_value = "1"mock_r.get.return_value = "10"self.assertEqual(self.gui.validate_input(), (1, 10))# 测试用例2:非法字符输入检测def test_invalid_character_input(self):with patch.object(self.gui, 'n_entry') as mock_n, \patch.object(self.gui, 'r_entry') as mock_r:mock_n.get.return_value = "5a"mock_r.get.return_value = "20"self.assertIsNone(self.gui.validate_input())# 测试用例3:带分数运算逻辑def test_mixed_number_calculation(self):result = self.gui.calculate('÷', Fraction(7, 2), Fraction(1, 2))self.assertEqual(result, Fraction(7))# 测试用例4:表达式去重验证def test_expression_normalization(self):expr1 = self.gui.normalize_expression("3+5")expr2 = self.gui.normalize_expression("5+3")self.assertEqual(expr1, expr2)# 测试用例5:除零保护测试def test_division_by_zero_protection(self):with self.assertRaises(ValueError):self.gui.calculate('÷', Fraction(3, 4), Fraction(0))# 测试用例6:文件格式验证def test_output_file_formatting(self):test_exercises = {1: "3+2",2: "1/2 × 4",3: "(5-2) ÷ 3"}with patch.object(self.gui, 'generate_exercises', return_value=test_exercises):self.gui.generate()with open("Exercises.txt", "r") as f:lines = f.readlines()self.assertTrue(lines[0].startswith("[1] "))# 测试用例7:括号优先级验证def test_bracket_priority(self):expr = "(1/2 + 3) × 4"normalized = self.gui.normalize_expression(expr)self.assertIn("×(4)", normalized)  # 验证乘法符号位置# 测试用例8:答案格式容错def test_answer_format_parsing(self):with self.assertRaises(ValueError):self.gui._parse_answer("2’3/4")  # 错误的中文撇号# 测试用例9:大数据量压力测试def test_large_scale_generation(self):with patch.object(self.gui, 'n_entry') as mock_n, \patch.object(self.gui, 'r_entry') as mock_r:mock_n.get.return_value = "10000"mock_r.get.return_value = "100"self.gui.generate()self.assertEqual(len(self.gui.exercises), 10000)# 测试用例10:运算符分布验证def test_operator_distribution(self):operators = set()for _ in range(100):expr = self.gui.generate_expression(10, True)ops = {c for c in expr if c in '+-×÷'}operators.update(ops)self.assertEqual(operators, {'+', '-', '×', '÷'})def tearDown(self):self.test_dir.cleanup()if __name__ == "__main__":
unittest.main(verbosity=2)

思路:

七、项目小结

项目亮点

1、数学规则严谨性(确保减法结果非负、除法分母合法)
2、高效查重机制(通过规范化表达式实现O(1)时间复杂度的重复判断)
3、用户友好性(图形界面操作简单,支持错误提示和状态反馈)
4、数据结果高效性(通过递归生成和规范化机制,本项目能够高效生成符合数学规则的题目,适用于小学数学教育场景)

通过项目积累的经验

这个项目不仅让我们巩固了二叉树、递归这样的技术方法,也让我们对一整套开发流程有了更深刻的理解。开发不仅仅是一个人的事,也是团队里的每个人都要参与讨论编写的,从程序到测试到文档,我们应当合理分工、用心完成。

结对感受

黄海怡to朱雅子:非常给力的队友,执行力强,思维缜密,代码水平高,教会了我许多!
朱雅子to黄海怡:两个人工作减少了工作量,可以专注在自己想做的领域。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/900334.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【每日一题】20250317

小时候以为人生是踏歌而行,后来才明白其实脚下踏着的是利刃和刀锋。【每日一题】(不定项)已知曲线 \(C_1\):\(y=\cos x\),\(C_2\):\(\displaystyle y=\sin(2x+\frac{2\pi}{3})\). (1) 下列结论正确的是 A. 把 \(C_1\) 上各点的横坐标伸长到原来的 \(2\) 倍,纵坐标不变,…

asyncio协程

目录使用协程重要的几个函数使用异步方法爬取Microsoft Bing图片asyncio.Queue(maxsize=) 说明1: 正常的程序都是从上到下依次执行的,如果遇到了要等待的地方,就会阻塞,等待相应的代码执行完毕后,再往下执行。 说明2: 协程(Coroutine) 是一种特殊的函数,它可以在执行过…

生产管理到底是啥?生产计划又该怎么做?

你是不是经常听到“生产管理”这四个字,但总觉得它挺虚的? 其实,生产管理说白了,就是为了让工厂或企业的生产顺利进行,保证产品按时、高质量、低成本地交付。这里面涉及到人、机器、原材料、时间、成本等各种因素,怎么把这些东西合理安排,就是生产管理的核心。 而生产计…

【日记】带着半边脸的眼影跑了小半个新区县城……(2302 字)

正文这个周末,充实到仿佛跟工作日的自己像是两个人。事情一件件来说吧。周五太过遥远,记不清了;周六几乎全天都在研究化妆;周天又几乎全在跳舞。第一件事,研究化妆。周五的晚上,基本上所有的化妆品就都到了。拆了好多好多。周六上午就试了一下所有的化妆品。不得不说,男…

AMBA总线学习(二)---AHB-Lite

AHB-Lite的硬件架构可以分为四部分,分别是Master、Slave、Decoder、MUX。 Maeter信号:Name Destination DescriptionHADDR[31:0] slave和decoder 32bit的地址总线(不是严格限制为32bit)HBURST[2:0] slave 突发传输类型HMASTERLOCK slave 用来实现原子操作的HPROT[3:0] slav…

可视化图解算法:判断链表中是否有环(环形链表)

判断链表是否存在环有个**小技巧**:**快慢指针法**。定义2个指针变量(即快慢指针),初始化时快慢指针都指向头节点,每次快指针每次移动 2 个节点,慢指针每次移动 1 个节点。如果 快指针指向的节点为null或者快指针指向节点的下一个节点为空,则链表没有环;如果快慢指针相…

新书上线 |《零门槛AIGC应用实战——Serverless+AI 轻松玩转高频AIGC场景》免费下载

《零门槛AIGC应用实战——Serverless+AI 轻松玩转高频AIGC场景》电子书正式上线!多种精选 AI 部署方案带你深入了解 Serverless+AI 最新趋势、AI 应用的架构设计与详细的部署教程等。函数计算 AI 技术解决方案助您一键上云,高效部署。《零门槛AIGC应用实战——Serverless+AI …

linux中安装intel wifi ax101驱动

目录前奏安装在Ubuntu中FAQ 资料参考 获取帮助 前奏 rambo@ub24-1:~$ cat /etc/os-release PRETTY_NAME="Ubuntu 24.04.1 LTS" NAME="Ubuntu" VERSION_ID="24.04" VERSION="24.04.1 LTS (Noble Numbat)" ........rambo@ub24-1:~$ cat…

桌面级CPU、显卡天梯图

手动更新,收藏所有(来源:秋刀鱼半藏)《史上最全桌面级CPU天梯图》专用更新帖(公测版) 版本:V6.077,时间:2024.11 《史上最全桌面级显卡天梯图》专用更新帖(公测版) 更新时间:2025.03.06

Qwen2-VL华为卡300i duo环境搭建推理测试

引子 前面也写过华为300i duo DeepSeek的环境搭建&推理测试,感兴趣的同事请移步(https://blog.csdn.net/zzq1989_/article/details/146149684?spm=1001.2014.3001.5501)。多模态华为卡的适配倒是一直没有出过相关的博客。刚好遇到需要Qwen2-VL的配置,安装。OK, 那就让…

EWM528-2G4NW20SX系列LORA MESH无线组网模块深度测评

1.LORA MESH模块产品简介 EWM528-2G4NW20SX、EWM528-2G4NW27SX系列LORA MESH无线组网模块基于先进的无线通信技术打造。在输出功率方面,LORA MESH模块能提供稳定且适配多种场景的功率支持,保障信号的有效传输范围。空中速率表现出色,可满足大量数据快速传输的需求,提升工作…

SQLService 导入excel数据生成一个数据表

SQLService 导入excel数据生成一个数据表 1、先选择导入的数据库右键任务=》选择导入数据 2、选择导入的数据源excel 3、选择excel的的地址和格式。 如果不知道格式的可以excel、另存改格式。 4、选择选择OLE DB Provider ,点击 下一步(Next) 5、选择默认下一步。 6、勾选 源…