结对作业

第三次

结对项目

这个作业属于哪个课程 < 网工1934-软件工程 >
这个作业要求在哪里 作业要求
这个作业的目标 实现一个自动生成小学四则运算题目的命令行程序

成员: 3119005336 潘新坤 31190005333 刘宇杰

Github项目链接

一、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. 对于运算符+ ×,首先是两个式子运算符相同,其次是参与运算的数相同,允许位置交换,例:1+22+1 也可认为相同

  2. 对于运算符- ÷,首先是两个式子运算符相同,其次是参与运算的数相同,运算数的顺序也相同,不允许位置交换,例:1÷21÷2 可可认为相同,1÷22÷1 不可认为相同

  3. 有一种特殊情况,即三个运算符,式子左右两个括号,若直接按照上面的思路处理,会出现错误,例:
    (1+2)+(3+4)==> 逆波兰式:1 2 + 3 4 + +
    运算顺序:1 2 + 3 4 + +

(3+4)+(1+2)==> 逆波兰式:3 4 + 1 2 + +
运算顺序:3 4 + 1 2 + +

  1. 判断原式中间运算符是否相同,不相同直接得出两式直接不相同的结果

  2. 判断A式左边括号内的式子与B式左边括号内的式子是否相同

    1. 相同的话,再判断A式右括号内的式子与B式右括号内的式子是否相同,得出结果
    2. 不相同的话,先判断原始中间运算符是否为减号或除号,若是,直接返回不相同的结果;若不是,接着判断A式左括号内的式子与B式右括号内的式子是否相同
      1. 相同的话,再判断A式右括号内的式子与B式左括号内的式子是否相同,得出结果
      2. 不相同的话,得出结果

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建了一个仓库,然后给了我权限。之后我们的合作是,一起讨论算法,我负责写规范类,他负责写工具类和算法的实现,最后的测试和博客由我负责。合作的过程中就越发感到代码规范的重要性,做好代码规范才能做大做强。

posted @ 2021-10-25 22:55  于木  阅读(53)  评论(0)    收藏  举报