211606301 蔡振翼 211605240 谢孟轩
一、预估与实际
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
? Estimate | ? 估计这个任务需要多少时间 | 5 | 5 |
Development | 开发 | ||
? Analysis | ? 需求分析 (包括学习新技术) | 30 | 60 |
? Design Spec | ? 生成设计文档 | 10 | 10 |
? Design Review | ? 设计复审 | 5 | 10 |
? Coding Standard | ? 代码规范 (为目前的开发制定合适的规范) | 60 | 60 |
? Design | ? 具体设计 | 120 | 150 |
? Coding | ? 具体编码 | 600 | 1200 |
? Code Review | ? 代码复审 | 10 | 20 |
? Test | ? 测试(自我测试,修改代码,提交修改) | 60 | 30 |
Reporting | 报告 | ||
? Test Repor | ? 测试报告 | 30 | 20 |
? Size Measurement | ? 计算工作量 | 10 | 5 |
? Postmortem & Process Improvement Plan | ? 事后总结, 并提出过程改进计划 | 20 | 10 |
合计: | 1055 | 1570 |
二、需求分析
以下需求以人教版小学数学课本为准
一年级:
(1) 内容包含一百以内的加减法,大多数例题的答案也不存在超过一百的数字
(2)加减混合运算为两步计算试题
(3)不涉及负数。
二年级:
(1) 开始涉及万以内的加减法
(2) 乘法和除法都是表内乘除法
(3)除法中除数是9以内的,被除数为一百以内。
(4)出现三位参数的混合运算,两步计算试题
(5)含有余数的除法运算不涉及混合运算。
三年级:
(1)出现小数加减法,参数至多两位
(2)乘法开始出现两位数乘以两位数的乘法
(3)除法要求除数至多为两位数,被除数最多为三位
家长要求:
(1)运算符在2~4个
(2)可以加括号
(3)减法运算的结果不能有负数
(4)除法运算除数不能为0,不能有余数
(5) 至少存在两个不相同的运算符
经过分析,我认为,这个程序应当:
(1)对一二年级的代码进行完善补全
三年级应家长要求:
(1)小数部分的题目忽略不计
(2)计算参数在3~5个
(3)数字大小在1000以内,不存在负数
(4)舍弃含有余数的除法算数题
三、设计
1. 设计思路
说明你如何设计这个程序
设计思路①:穷举所有题目,将每道题目分别储存在String类型的数组中。按输入要求随机选择序号抽取题目按要求完成输出。这样避免了抽取重复题目等问题,但题目量巨大并不适合。
设计思路②:随机生成数字与符号利用逆波兰函数求得题目的答案写入文本。
设计思路③:随机本次抽取题目所需要的运算符的个数,随机抽取数字和符号进行运算,并把答案存入answer数组。抽取下一个符号,比较与上一个符号的运算等级判断是否需要加扩号重复如上不骤。一旦达到运算符要求结束题目写入文档。
2. 实现方案
写出具体实现的步骤
- 准备工作:先在Github上创建仓库,克隆到本地,之后在仓库中创建文件夹,编写代码之后通过git传到github
- 技术关键点:
- 括号的选取
- 除法无余数
- 查重的实现
- 有耐心看代码= =
四、编码
问题1:如何随机括号的位置(哪里需要括号,生成的括号是否合理,是否必要)
解决过程:从哪里需要括号,生成的括号是否合理,是否必要的角度进行思考,认为当乘除符号遇到加减符号时我们需要给加减的部分添加括号来保证运算顺利进行。这也是我们开始思考整体代码的开端。尽管在最后认真读了一遍例题才发现这一点并不必要。
问题2:如何公平随机生成多个运算符
解决过程:因为我们发现加法运算的数字在万以内,而乘法除法的数字在千以内,添加随机要求会导致题目中生成四个符号的概率并不平均。于是我们改小了加法和减法的范围,这个改变最明显的效果就是大量减少了“0÷(A+B)”这类题目的产生。没有办法达到绝对的公平只能尽力去靠近。
问题3:如何简化题目不重复
解决过程:解决这个问题的第一个想法是思路①,穷举所有题目进行抽取。之后我们通过百度查找各种解决“查重问题”方法。例如利用hashmap的映射。并不是看的很懂,但从中找到了解决问题的灵感。存储题目中的参数和答案到list中通过list.contains()方法进行查重。玩过24点的只到或许这并不是一个特别完美的方法,但这个方法从一方面上杜绝了这一现象,将错误查重的概率减到了很小。
问题4:如何解决除法方法无余数的运算
解决过程:最初我们采取逆向思维,随机除数和商相乘得到被除数来输出题目保证得到题目和答案不存在余数,解决了当我们随机除号在前时的问题。随机除号在我们已经得到的数字的后面,如何随机出适合的除数我们在挣扎过穷举被除数的所有因数时,得到Q群里一个同学提供的方法。随机除数,得到的余数用被除数减去它形成新的表达式解决了问题。
问题5:分工
解决过程:队友专门负责编写输入以及查重方面的代码,对其进行调试,而我负责运算方面。运算方面的代码对我来说有点颇困难,在她完成她的代码之后,我们互相讨论运算代码中除法的难点,一起解决了它。
1. 调试日志
记录编码调试的日志,请记录下开发过程中的 debug 历程
- 在二年级除法运算中,因为需要保留上一次的要求,所以导致这次的余数不好解决
解决方案:用一个数组存余数,在题数的for循环下判断,如果是二年级的除法,就在这个答案的后面增加余数
- 三年级除法运算的括号问题
解决方案:先规定运算符的优先级后,随机抽取运算符,这个运算符比上个运算符优先级弱的话,在上个运算符的前后加上括号
- 三年级除法要求没余数
解决方案:随机一个除数,将被除数与它相模,如果有余数则将被除数减去余数
2. 关键代码
请展示一段程序的关键代码,并解释代码的作用
以下代码是运算的代码,包括调用运算方法以及运算方法的代码:
//加法
private static void add(int i,StringBuffer word,int[] answer, int grade,int[] sub, int m) {
// TODO 自动生成的方法存根
if(grade==1) {
num1 = (int) (Math.random()*(answer[i]-1));
sub[m]=num1;
}else {
num1 = (int) (Math.random()*99);
sub[m]=num1;
}
word.insert(word.length()," + "+num1);
answer[i] = answer[i]+num1;
}
//减法
private static void sub(int i,StringBuffer word, int[] answer,int[] sub, int m) {
// TODO 自动生成的方法存根
num1 = (int) (Math.random()*(answer[i]-1));
sub[m]=num1;
word.insert(word.length(),"-"+num1);
answer[i] = answer[i]-num1;
}
//乘法
private static void mul(int i,StringBuffer word, int[] answer, int grade,int[] sub, int m){
// TODO 自动生成的方法存根
if(grade==2) {
num1 = (int) (Math.random()*9);
sub[m]=num1;
}
else {
num1 = (int) (Math.random()*99);
sub[m]=num1;
}
word.insert(word.length(), "x"+num1);
answer[i]= answer[i]*num1;
}
//除法
private static String div(int i,int j,int f, StringBuffer word, int[] answer,int grade,int[] sub, int m, int[] remb, int k) {
// TODO 自动生成的方法存
if(grade==2) {
num1 = 1+(int)(Math.random()*9);
rem=answer[i]%num1;
remb[k] = rem;
sub[m]=num1;
word.insert(word.length(),"÷"+num1);
answer[i]=answer[i]/num1;
}
else{
test = 1+(int)(Math.random()*2);
if(answer[i]==0 && f-j<=2) return "false";
if((test==2 || answer[i]==0) && (f-j>2)) {
num1 = 1+(int)(Math.random()*(answer[i]-1)); //除数
if(answer[i]>99) {num1 = 1+(int)(Math.random()*99);}
rem=answer[i]%num1;
sub[m]=num1;
if(rem>0) {word.insert(0, "(");
word.insert(word.length(), "-"+rem+") ÷"+num1);
answer[i]=(answer[i]-rem)/num1;
}else {
word.insert(word.length(),"÷"+num1);
answer[i]=answer[i]/num1;}
return "j++";
}
num1 = (int)(Math.random()*((1000/answer[i])+1)); //被除数
sub[m]=num1*answer[i];m++;
word.insert(0,(num1*answer[i])+"÷");
answer[i] = num1;
return "true";
}
return null;
}
private static void operation(int n, int grade) {
// TODO 自动生成的方法存根
//str数组储存了运算符的等级和符号;answer数组包含式子答案;word数组储存题目;f是随机运算符的个数;num是随机的第一个数;g是选择第g个运算符;i是第i道题;j是当前符号数量
int k=0;//设置等级
int[] str= {0,0,1,1}; // 0为+-,1为*/
int[] answer = new int[n]; //存答案的数组
int[] sub = new int[6];//存数字的数组
int[] remb = new int[n];
File file = new File("out.txt");
try {
p = new PrintWriter(new FileOutputStream(file.getAbsolutePath()));
out = new FileOutputStream(file);
id = new StringBuffer("");
for(int i=0;i<n;i++) {//出n道题目的循环
int m=0; //存数字个数
int level=1;
if(grade==1) {
g = (int)(Math.random()*(str.length-2));
answer[i]=(int) (Math.random()*99);
sub[m]=answer[i];m++;
word= new StringBuffer(answer[i]+"");
if(g==1) {
add(i,word,answer,g,sub,m);m++;
}
else {
sub(i,word, answer,sub,m);m++;
}
}
else if(grade==2) {
g = (str.length-2)+(int)(Math.random()*str.length);
if(g==3) {
answer[i]=(int) (Math.random()*99);
sub[m]=answer[i];m++;
word= new StringBuffer(answer[i]+"");
div(i,0,0,word, answer,grade,sub,m,remb,k);
m++;k++;
}
else {
answer[i]=(int) (Math.random()*9);
sub[m]=answer[i];m++;
word= new StringBuffer(answer[i]+"");
mul(i,word, answer,grade,sub,m);
m++;
}
}
else {//三年级
f = 2+(int) (Math.random()*3); //f:随机抽取运算符的个数
g = (int)(Math.random()*(str.length-1));//第一个运算符
answer[i]= (int) (Math.random()*99);
sub[m]=answer[i];m++;
word= new StringBuffer(answer[i]+"");
final int g1=g;
for(int j=0;j<f;j++) {
if(level<str[g] && j>f-2 && j>=2) break;//在需要加括号的情况下运算符不够用
if(level<str[g]) {
//加括号
word.insert(0, "(");
word.insert(word.length(), ")");
j++;
}
level=str[g];
//随机选择符号
if(g==0) {
add(i,word, answer,g,sub,m);m++;
}
else if(g==1) {
sub(i,word, answer,sub,m);m++;
}
else if(g==2) {
if(div(i,j,f,word, answer,grade,sub,m,remb,k).equals("j++")){
j=j+2;
}else if(div(i,j,f,word, answer,grade,sub,m,remb,k).equals("false")) {
i--;break;
}
m++;k++;
}
else {
mul(i,word, answer,grade,sub,m);m++;
}
g = (int)(Math.random()*(str.length));
if(answer[i]>=100) {
g = (int)(Math.random()*(str.length-1));
}
while(g==g1 && j==0)
{
g = (int)(Math.random()*(str.length));
if(answer[i]>=100) {
g = (int)(Math.random()*(str.length-1));}
}
}
}
word.insert(0, "("+(i+1)+")");
if(grade==2 && g==3 && remb[k-1]!=0) {
id.insert(id.length(), word+"="+answer[i]+"..."+remb[k-1]+"\n");
}
else {
id.insert(id.length(), word+"="+answer[i]+"\n");
}
word.insert(word.length(),"\n");
System.out.println(word);
System.out.println(answer[i]);
p.write(word.toString());
}
p.write("\n");
p.write(id.toString());
p.close();
out.close();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
3. 代码规范
请给出本次实验使用的代码规范:
- 第一条:常量命名全部大写,单词键用下划线隔开,力求语义表达完整,不要嫌名字长。
- 第二条:类型与中括号紧挨相连来定义数组。
- 第三条:左小括号和字符之间不出现空格;同样的,有小括号和字符之间也不出现空格。详见第5条下面正例提示。
- 第四条: if/for/while/switch/do等保留字与括号之间都必须加空格。
- 第五条:采用4个空格缩进,禁止使用tab字符
五、测试
请思考并记录你认为必要的测试点,并记录测试用例与测试结果
测试用例 | 结果 |
---|---|
java MathExam6301 -n 10 -grade 1 | 将1年级的加减法正确地输入到out.txt文件中 |
java MathExam6301 -n 10 -grade 2 | 将2年级的乘除法正确地输入到out.txt文件中 |
java MathExam6301 -n 10 -grade 3 | 将3年级的混合四则运算法正确地输入到out.txt文件中 |
java MathExam6301 -grade 1 -n 10 | 程序正常运行 |
java MathExam6301 | 输入的参数应为4个 |
java MathExam6301 -n as -grade sfas | 输入的年级数与题数应为数字 |
java MathExam6301 1 1 | 查找不到-n或者-grade |
java MathExam6301 -n 1 -grade 5 | 输入年级应在一到三年级 |
java MathExam6301 -n 1000000 -grade 1 | 输入题目数量应该在1~1000 |
六、总结
不得不说,这一次结对作业对我的帮助还是很大的。分工之后,双方有更多的时间沟通交流,在敲代码初期便把准备工作做好了;打代码中,不会的问题还可以互相交流,虽然我的问题真多,可是慢慢学之后我又进步了。
一度地敲代码,让我只想用呵呵两字。又能表示自己的吐槽之意,也表示我的开心想法。打不出来,一直盯着电脑,眼睛都快炸了,依旧 卖呆咯。但是,突破困难之后,舒服。。。。是真的舒服。
我还有很长的路要走,我一直在拖队友的后腿,不甘心,真的很不甘心。
最后,送上我和我队友的表情包哈!
原文地址:https://www.cnblogs.com/caizhenyi/p/9672195.html