这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/CSGrade22-12 |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/CSGrade22-12/homework/13221 |
这个作业的目标 | 实现一个自动生成小学四则运算题目的命令行程序。 |
地址 | https://github.com/Nethtra/SimpleOperation |
成员 | 温宗宝3122004790 王天一3122004789 |
1.PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 60 |
· Estimate | · 估计这个任务需要多少时间 | 60 | 60 |
Development | 开发 | 900 | 1040 |
· Analysis | · 需求分析 (包括学习新技术) | 240 | 240 |
· Design Spec | · 生成设计文档 | 60 | 60 |
· Design Review | · 设计复审 | 30 | 30 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 30 |
· Design | · 具体设计 | 60 | 60 |
· Coding | · 具体编码 | 300 | 360 |
· Code Review | · 代码复审 | 60 | 60 |
· Test | · 测试(自我测试,修改代码,提交修改) | 120 | 200 |
Reporting | 报告 | 120 | 120 |
· Test Repor | · 测试报告 | 30 | 30 |
· Size Measurement | · 计算工作量 | 30 | 30 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 60 | 60 |
· 合计 | 1080 | 1220 |
2.性能分析
程序在处理大量数据时,生成题目和批改答案的速度明显变慢,特别是在处理大量分数运算时。
改进思路
1.减少不必要的对象创建:在原始代码中,每次分数运算都会创建新的 FractionNum 对象,而这些对象在完成运算后很快就会被丢弃。我们通过复用对象,减少了垃圾回收的频率,提升了内存使用效率。
2.优化分数运算逻辑:分数加减乘除的计算过程中,我们发现 gcd(最大公约数)运算频繁调用,导致性能下降。因此,我们通过将 gcd 的计算缓存化,减少了重复的运算。
3.减少文件读写操作:文件的写操作较为频繁,特别是在批量生成题目时。我们通过批量写入操作,减少了文件的频繁打开和关闭,提高了 I/O 的效率。
性能分析图示
最耗时的函数:通过分析,我们发现程序中消耗最大的函数是 FractionNum.add()。由于在大量生成题目和批改过程中,加法操作频繁出现,这一函数的调用次数非常高。通过缓存化 gcd 计算的结果后,我们显著减少了该函数的执行时间。
文件写操作的耗时:FileOutputStream.write() 也是一个主要的性能瓶颈。我们通过批量写入数据来减少这一操作的调用次数,优化了文件写入的效率。
3.设计实现过程
核心类:
FractionNum:用于处理分数,包括加减乘除、分解等操作。
Operation 及其子类(如 OperationAdd, OperationSub, OperationMul, OperationDiv):分别处理不同的运算符操作,生成分数的加、减、乘、除表达式。
RandomUnit:生成随机数和随机布尔值,用于生成随机题目。
Result:用于将生成的练习题和答案保存到文件中,并进行批改。
Restrict:限制题目中数值的范围。
Calculator:负责对表达式进行计算。
各类之间通过对象传递数据,调用方法实现题目的生成与计算。其中关键点是通过 FractionNum 类完成分数的运算逻辑封装,保证题目生成和表达式解析的准确性。
关键函数的流程可通过如下步骤表示:
1.获取用户输入,选择生成题目数量和数值范围。
2.随机生成表达式(加减乘除)。
3.解析表达式并计算结果。
4.将练习题和答案保存至文件。
5.对用户的解答进行批改,输出正确与错误的题目
4.关键代码展示
关键代码展示:
public class FractionNum {
// 分数的加法
public static FractionNum add(FractionNum a, FractionNum b){
return new FractionNum(a.numerator * b.denominator + b.numerator * a.denominator, b.denominator * a.denominator);
}
// 带分数处理的格式化输出
@Override
public String toString() {
if (isFraction()) {
if (numerator > denominator) {
return (numerator / denominator) + "’" + numerator % denominator + "/" + denominator;
}
return numerator + "/" + denominator;
} else {
return "" + numerator / denominator;
}
}
}
在 FractionNum 类中,定义了处理加法和带分数的格式化输出。这样,生成的题目可以直接通过分数的加法逻辑得到结果,并以清晰的格式显示给用户。这个类还包含了其他操作,如减法、乘法、除法的实现。
public class OperationAdd extends Operation {
public OperationAdd(FractionNum value) {
FractionNum left = new FractionNum();
FractionNum right = new FractionNum();
value.splitFractionAdd(left, right); // 将分数拆分为两个加数
this.left = new Expression(left);
this.right = new Expression(right);
}
@Override
public String toString() {
return this.left.toString() + " + " + this.right.toString();
}
}
OperationAdd 类用于实现加法运算的生成,自动将分数拆分为左右两部分,并生成最终的题目表达式。
5.测试运行
@Test
public void basicTest() {
String[][] argsArray = {
{"-r", "0", "-n", "1000"},
{"-r", "-n", "10000"},
{"-r", "100", "-n", "10000"},
};
for (String[] args : argsArray) {
testCommandParse(args);
}
}
6.项目小结
这次项目合作过程中,我们通过合理分工提升了开发效率。遇到的主要挑战是如何让生成的表达式看起来更加自然。通过多次测试和调优,我们逐步改进了算法,确保生成的题目有合理的难度和结构。结对合作过程中,我们学到了互相支持与沟通的重要性。