结对作业
| 这个作业属于哪个课程 | 软件工程 |
|---|---|
| 这个作业要求在哪里 | 结对编程项目作业 |
| 这个作业的目标 | 小学四则运算题目自动生成程序的实现+PSP表格使用+GitHub代码管理+双人结对编程练习 |
| GitHub项目地址 | https://github.com/DevilWizard/PairProject |
| 合作者 | 3119005431欧智昕、3119005417黄智权 |
1.项目展示:
1.1 题目生成


1.2 对错判断

2.前言:
2.1开发环境:
-
编程语言:JavaSE-10
-
IDE:IntelliJ IDEA Community Edition 2021.2.2
-
单元测试:JUnit
-
代码性能分析工具:JProfiler
-
代码管理:GitHub
2.2流程设计:

2.3项目结构:

项目分为主程序、ui界面、实体类和工具类四个大包
2.4核心算法:
逆波兰算法
1. 算法简介:
逆波兰表达式又叫做后缀表达式。逆波兰表示法是波兰逻辑学家J・卢卡西维兹(J・ Lukasiewicz)于1929年首先提出的一种表达式的表示方法。
2.算法适用场景:
假定给定一个只 包含 加、减、乘、除,和括号的算术表达式,你怎么编写程序计算出其结果?
问题是:在表达式中,括号,以及括号的多层嵌套 的使用,运算符的优先级不同等因素,使得一个算术表达式在计算时,运算顺序往往因表达式的内容而定,不具规律性。 这样很难编写出统一的计算指令。
使用逆波兰算法可以轻松解决这个问题。他的核心思想是将普通的中缀表达式转换为后缀表达式。
什么是中缀表达式?例如a+b,运算符在两个操作数的中间。这是我们从小学开始学习数学就一直使用的表达式形式。
什么是后缀表达式?例如a b + ,运算符在两个操作数的后面。后缀表达式虽然看起来奇怪,不利于人阅读,但利于计算机处理。
转换为后缀表达式的好处是:
1、去除原来表达式中的括号,因为括号只指示运算顺序,不是实际参与计算的元素。
2、使得运算顺序有规律可寻,计算机能编写出代码完成计算。
3.算法特点:
-
运算符号永远在数值的后面
如普通表达式为 2+3,因为运算符在数值中间,所以这种表达式也叫中缀表达式,而逆波兰表达式写法是 23+(将运算符写在数值后面)。 -
运算符的置后顺序是由运算顺序决定的
如(1+2)*(3+4),逆波兰表达式为 12+34+ *,而不是 1234++ *,因为运算顺序是先计算(1+2),然后计算(3+4),最后再将两个结果相乘。所以顺序就是 12+,然后 34+,最后一个 *。 -
举例:
| 普通表达式 | 逆波兰表达式 |
|---|---|
| 9-5+4 | 9,5,-,4,+ |
| (2+3)*5 | 2,3,+,5,* |
| (5-2)/2*4 | 5,2,-,2,/,4,* |
4.算法原理:
逆波兰算法的核心步骤就2个:
1、将中缀表达式转换为后缀表达式,例如输入的原始表达式是 3*(5+7) ,转换得到 3 5 7 + *
2、根据后缀表达式,按照特定的计算规则得到最终计算结果
下面详细介绍这个2步的操作。
中缀表达式转换为后缀表达式
你需要设定一个栈SOP,和一个线性表 L 。SOP用于临时存储运算符和左括号分界符( ,L用于存储后缀表达式。
遍历原始表达式中的每一个表达式元素
(1)如果是操作数,则直接追加到 L中。只有 运算符 或者 分界符( 才可以存放到 栈SOP中
(2)如果是分界符
Ⅰ 如果是左括号 ( , 则 直接压入SOP,等待下一个最近的 右括号 与之配对。
Ⅱ 如果是右括号),则说明有一对括号已经配对(在表达式输入无误的情况下)。不将它压栈,丢弃它,然后从SOP中出栈,得到元素e,将e依次追加到L里。一直循环,直到出栈元素e 是 左括号 ( ,同样丢弃他。
(3)如果是运算符(用op1表示)
Ⅰ如果SOP栈顶元素(用op2表示) 不是运算符,则二者没有可比性,则直接将此运算符op1压栈。 例如栈顶是左括号 ( ,或者栈为空。
Ⅱ 如果SOP栈顶元素(用op2表示) 是运算符 ,则比较op1和 op2的优先级。如果op1 > op2 ,则直接将此运算符op1压栈。
如果不满足op1 > op2,则将op2出栈,并追加到L,再试图将op1压栈,如果如果依然不满足 op1>新的栈顶op2,继续将新的op2弹出追加到L ,直到op1可以压入栈中为止。
也就是说,如果在SOP栈中,有2个相邻的元素都是运算符,则他们必须满足:下层运算符的优先级一定小于上层元素的优先级,才能相邻。
最后,如果SOP中还有元素,则依次弹出追加到L后,就得到了后缀表达式。
3.关键代码及说明:
本项目有两个关键点:一个是对表达式的计算,也就是上面讲到的逆波兰表达式;而另一个就是对分数的处理了。
逆波兰前面讲的已经很细了,这里就只贴关键代码不说明了,逆波兰关键代码:

下面来着重讲解分数处理和计算:
3.1对分数的处理
因为分数的表达较为复杂,所以用一个专用类Fraction来辅助处理表达式中的分数
3.1.1 构造函数

通过构造函数,统一将分数转为假分数,方便后续计算
3.1.2 分母非0检测

通过check函数,能够保证传入的分数分母不为0,保证了计算结果和题目的正确性
3.1.3 约分函数

利用约分函数,能够使得分数在最后出现时保证是最简形式
3.2 对分数的计算

在实际计算时,会利用类Fraction统一将自然数和真分数都转化为假分数,然后根据分数的加减乘除运算规则进行运算,将分数计算化繁为简,提高计算效率
利用3.1和3.2中的方法,便能快速地处理分数计算
4.效能分析:


从上图可以看到,表达式的生成、表达式的解析、表达式的计算是程序性能消耗大户,也是程序的核心所在,所以改进可以从这三方面入手。
5.模块单元测试及说明:
本程序最主要目的的是保证能够根据生成的题目计算出正确且符号格式要求的答案,因此现对计算模块进行测试说明,测试用例如下:

测试覆盖了各种极端情况,比如除数为0,计算结果大于1(要用真分数表示)等情形,测试结果如下:

从结果看,本程序能够很好地保证计算结果准确率。
6.项目小结:
成员1(黄智权):
第一次两人合作编程完成一个小项目,总体感觉不错,合作愉快,基本上完成任务。在着手实现之前,我们花费了必要的时间来进行初步的规划和较为具体的分工。
在合作的过程中,我们彼此充分交换想法,共同探讨方案的可行性,在努力完成自己的分工任务的同时,也密切关注对方的进度。当遇到难题无法以自己的力量解
决时,我们能寻求对方的帮助,集思广益,共同解决难题。在完成项目的过程中,也学习到了一些新的知识与技能。
个人反思:
- 个人水平有限,关键算法(逆波兰算法)由另一位同学完成。本人完成了运算符的随机生成、运算数的随机生成、表达式的随机生成模块。
- 图形化界面尚且不熟练,也是交给另一位同学完成。
- git相关操作不熟练,在代码管理时耽误了不少时间。
成员2(欧智昕):
这是我第一次和智权同学合作完成软工项目,从开始的构思和分工,到最后的实现,这个过程虽然有点辛苦,但看到最终的结果还是很欣慰的,希望下次再多多合作!
7.PSP表:
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 25 | 30 |
| Estimate | 估计这个任务需要多少时间 | 15 | 20 |
| Development | 开发 | 300 | 360 |
| Analysis | 需求分析 (包括学习新技术) | 45 | 60 |
| Design Spec | 生成设计文档 | 20 | 25 |
| Design Review | 设计复审 | 15 | 20 |
| Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 20 | 28 |
| Design | 具体设计 | 60 | 75 |
| Coding | 具体编码 | 300 | 360 |
| Code Review | 代码复审 | 30 | 45 |
| Test | 测试(自我测试,修改代码,提交修改) | 60 | 100 |
| Reporting | 报告 | 40 | 50 |
| Size Measurement | 计算工作量 | 10 | 15 |
| Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 30 | 40 |
| 合计 | 970 | 1228 |
------------恢复内容结束------------

浙公网安备 33010602011771号