结对作业
第三次
结对项目
| 这个作业属于哪个课程 | < 网工1934-软件工程 > |
|---|---|
| 这个作业要求在哪里 | 作业要求 |
| 这个作业的目标 | 实现一个自动生成小学四则运算题目的命令行程序 |
成员: 3119005336 潘新坤 31190005333 刘宇杰
一、PSP表格
| *PSP2.1* | *Personal Software Process Stages* | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | ||
| · Estimate | · 估计这个任务需要多少时间 | 1090 | 1100 |
| Development | 开发 | ||
| · Analysis | · 需求分析 (包括学习新技术) | 100 | 120 |
| · Design Spec | · 生成设计文档 | 30 | 50 |
| · Design Review | · 设计复审 | 30 | 30 |
| · Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 60 | 80 |
| · Design | · 具体设计 | 50 | 100 |
| · Coding | · 具体编码 | 360 | 400 |
| · Code Review | · 代码复审 | 60 | 60 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 200 | 300 |
| Reporting | 报告 | ||
| · Test Repor | · 测试报告 | 50 | 50 |
| · Size Measurement | · 计算工作量 | 20 | 20 |
| · Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 30 |
| · 合计 | 1090 | 1240 |
二、设计过程:
第一阶段:先做生成普通的不带符号的式子,写一个把数字都做成真分子的规范类,四则运算的真分数算法
第二阶段:实现批量成题目,并实现IO流写入txt,增加生成括号方法,运用逆波兰算法,也是考虑到后面还要查重的实现,选择用的逆波兰算法
第三阶段:实现查重算法,实现了对用户答案的评分,对于减法的答案不小于等于零算法优化,对除法除数不为零算法优化,对乘法乘数不为零算法优化
第四阶段:测试阶段,对出现的bug进行优化改进。
三、算法流程图:

项目结构:

四、类分析:
| 类名 | 功能 |
|---|---|
| Main | 主类 |
| Calculation | 计算类,传入一个中缀表达式进行运算 |
| Check | 查重类 |
| GenerateExpression | 题目组件生成类,生成一堆数字和运算符及括号 |
| Expression | 题目生成类,将GenerateExpression类生成的组件整合成一道题目 |
| Fraction | 真分数规范类,内含四则运算 |
| TxtIO | IO类,实现txt文件的写入和读取 |
Fraction真分数规范类
Fraction类是整个程序的基础,要求里提到真分数要有运算方法,故我们把所有数字都定义为真分子,即每生成一个数都是由一个分子和一个分母组成,经过约分化简,表现出来的形式有3'1/2(三又二分之一),2/5(五分之二),3(三)这三种表现形式,该类内含四则运算

Calculation计算类:
传入一个中缀表达式,将该表达式里的内容用 List
学习视频:尚硅谷Java数据结构与java算法(Java数据结构与算法)_哔哩哔哩_bilibili
Check 查重类
从老师给出的要求中,查重的核心在检查计算过程的重复,故逆波兰算法完全符合要求
-
对于运算符
+ ×,首先是两个式子运算符相同,其次是参与运算的数相同,允许位置交换,例:1+2和2+1也可认为相同 -
对于运算符
- ÷,首先是两个式子运算符相同,其次是参与运算的数相同,运算数的顺序也相同,不允许位置交换,例:1÷2和1÷2可可认为相同,1÷2和2÷1不可认为相同 -
有一种特殊情况,即三个运算符,式子左右两个括号,若直接按照上面的思路处理,会出现错误,例:
(1+2)+(3+4)==> 逆波兰式:1 2 + 3 4 + +
运算顺序:1 2 + 3 4 + +
(3+4)+(1+2)==> 逆波兰式:3 4 + 1 2 + +
运算顺序:3 4 + 1 2 + +
-
判断原式中间运算符是否相同,不相同直接得出两式直接不相同的结果
-
判断A式左边括号内的式子与B式左边括号内的式子是否相同
- 相同的话,再判断A式右括号内的式子与B式右括号内的式子是否相同,得出结果
- 不相同的话,先判断原始中间运算符是否为减号或除号,若是,直接返回不相同的结果;若不是,接着判断A式左括号内的式子与B式右括号内的式子是否相同
- 相同的话,再判断A式右括号内的式子与B式左括号内的式子是否相同,得出结果
- 不相同的话,得出结果
GenerateExpression题目组件生成类
创建一个数字数组、运算符数组、左括号数组及括号数组,生成一道题目所需的数字和运算符及括号并分别存入前面四个数组。其中,生成的数字为Faction类型,运算符随机为"+","-","×","÷"。如果生成的题目重复或不能计算即重新生成题目。用Set集合的方法记录随机生成的左右括号位置对,即实现了去重的需求,对于无效括号,进行移除。
附括号生成及查重算法:
// 以下将括号的位置记录下来
// 假设运算符3,最多3个括号,所以应该是实际运算符个数+1 [0,4)
Set<String> indexSet = new TreeSet<>();
int bracketCounts = random.nextInt(actualOperationCounts + 1); // 括号个数
while (bracketCounts > 0) {
int leftIndex = random.nextInt(actualOperationCounts);
int rightIndex = random.nextInt(actualOperationCounts - leftIndex + 1) + leftIndex;
if (leftIndex == rightIndex) {
continue;
}
indexSet.add(leftIndex + ":" + rightIndex);
bracketCounts--;
}
indexSet.remove("0:" + actualOperationCounts); // 移除最外层括号(无效括号)
for (String temp : indexSet) {
String[] index = temp.split(":");
expression.getLeftBrackets()[Integer.parseInt(index[0])]++;
expression.getRightBrackets()[Integer.parseInt(index[1])]++;
}
//优化重复括号和开头末尾括号
//循环遍历左括号,除去(((Number)) + )的情况
for (i = 0; i < actualOperationCounts; i++) {
if (expression.getRightBrackets()[i] > 0) {
int num = Math.min(expression.getLeftBrackets()[i], expression.getRightBrackets()[i]);
expression.getLeftBrackets()[i] -= num;
expression.getRightBrackets()[i] -= num;
}
}
Expression题目生成类
将上述GenerateExpression类生成的数字、运算符、左右括号整合成一道题目
重写 toString():
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < leftBrackets[i]; j++) {
stringBuilder.append("( ");
}
stringBuilder.append(data[i].toString()+" ");
for (int j = 0; j < rightBrackets[i]; j++) {
stringBuilder.append(") ");
}
if (i < operators.length) {
stringBuilder.append(operators[i]+" ");
}
}
return stringBuilder.toString();
}
TxtIOIO类
实现了txt文件的清空、写入和读取三个方法,每次写入前,都给txt文件进行清空后再写入,避免了多次测试需要手动删除文件的内容。
值得一提的是,对于字符串"."的分割,需要照下面写法才可。
String[] temp = line.split("\\.");
Main主类
对各个工具类进行调用整合,对作业要求进行实现。
五、性能分析
测试了一万道的性能:


六、运行测试
传入参数:

一万道题目及答案

输入题目及答案评分:

七、项目小结
潘新坤:值得一说这是我第一次和同学共同合作开发一个项目,与别人合作写代码,需要提前构建好项目结构,并分配各自的任务,并且自己写的代码需要比平时更加规划,毕竟每个人的代码形式都有自己的风格,所以平时的交流也显得非常重要。
刘宇杰:因为我们是舍友关系,所有对很多算法可以当面讨论,当然这也我们第一次用GitHub合作做一个项目,在他的GitHub建了一个仓库,然后给了我权限。之后我们的合作是,一起讨论算法,我负责写规范类,他负责写工具类和算法的实现,最后的测试和博客由我负责。合作的过程中就越发感到代码规范的重要性,做好代码规范才能做大做强。

浙公网安备 33010602011771号