第二次作业☺简单查重
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数组。抽取下一个符号,比较与上一个符号的运算等级判断是否需要加扩号重复如上不骤。一旦达到运算符要求结束题目写入文档。
这个函数包含1个类8个方法。通过命令行进行输入,在主函数中调用input()方法对输入的内容进行判定是否出错。接着调用operation()方法开始进行题目的随机和答案的储存。随机题目的过程中调用四个运算的方法完成出题并写入文本。写入文本之前调用examine()方法对题目进行查重。
2. 实现方案
- 准备工作:先在Github上创建仓库,克隆到本地,之后在仓库中创建文件夹,编写代码之后通过git传到github
- 技术关键点:
- 括号的选取
- 除法无余数
- 查重的实现
四、编码
问题1:如何随机括号的位置
解决过程:从哪里需要括号,生成的括号是否合理,是否必要的角度进行思考,认为当乘除符号遇到加减符号时我们需要给加减的部分添加括号来保证运算顺利进行。这也是我们开始思考整体代码的开端。尽管在最后认真读了一遍例题才发现这一点并不必要。
问题2:如何公平随机生成多个运算符
解决过程:因为我们发现加法运算的数字在万以内,而乘法除法的数字在千以内,添加随机要求会导致题目中生成四个符号的概率并不平均。于是我们改小了加法和减法的范围,这个改变最明显的效果就是大量减少了“0÷(A+B)”这类题目的产生。没有办法达到绝对的公平只能尽力去靠近。
问题3:是否给自己放个假来个巨无霸
解决过程:周五当晚就去了麦当劳,不好吃,不如写代码(ノ´д`)
问题4:如何简化题目不重复
解决过程:解决这个问题的第一个想法是思路①,穷举所有题目进行抽取。之后我们通过百度查找各种解决“查重问题”方法。例如利用hashmap的映射。并不是看的很懂,但从中找到了解决问题的灵感。存储题目中的参数和答案到list中通过list.contains()方法进行查重。玩过24点的只到或许这并不是一个特别完美的方法,但这个方法从一方面上杜绝了这一现象,将错误查重的概率减到了很小。
问题5:如何多次输入
解决过程:这是上一份作业我给我自己留下的一个问题,重新输入,递归调用自身的确可以解决这个问题。
问题6:如何解决除法方法无余数的运算
解决过程:最初我们采取逆向思维,随机除数和商相乘得到被除数来输出题目保证得到题目和答案不存在余数,解决了当我们随机除号在前时的问题。随机除号在我们已经得到的数字的后面,如何随机出适合的除数我们在挣扎过穷举被除数的所有因数时,得到Q群里一个同学提供的方法。随机除数,得到的余数用被除数减去它形成新的表达式解决了问题。
问题7:如何解决混合运算
解决过程:为了避免出现随机的符号全是同一个的情况,即使概率很小。我们在随机第二个符号时会将它与第一个符号进行比较,如果相同再次进行随机。更优化随机的方法目前还处于理论阶段。如下调试日志。
问题8:如何分工及过程
解决过程:两人一起讨论主思路定下程序主基调,开始各自负责部分代码。我负责编写输入以及查重方面的方法,队友负责运算的总体构架和数据测试。在复杂或者出现歧义的代码上两人开始讨论研究新的算法等等以及解决运行过程中出现的bug。
1. 调试日志
在调试日志这个部分我想简单讲一下我们在写这个思路上遇到的问题和缺陷。
这个思路我们遇到的第一个难点是思考这个思路出题的局限性。第一个无法先计算两个同级的运算符,例如(A+B)X(C+D)。如果是两个加法减法的运算符,例如以上这个例子运算符的个数本身超过了题目要求的四个运算符的要求,所以不在我们的考虑范围之内。
第二个问题是如果按照初期思路,我们随机的永远是符号后一位的数字,在除法上会遇到无法出A ÷(B+C)这一类题目。为了解决这个问题我们在除法方法中再次加入了一次随机来决定除号的位置是在我们目前算出的答案之前还是之后。
第三个问题。我们之前的一次讨论,在讨论类似(AXB)这一类加括号合不合理,在我坚定的误导下,我们认为不合理。在最后几天的群里的激烈的中,我们详细的看了下老师给出的例题,发现括号的位置真的是随机的(ノ´д`)。但是我仍然认为这个问题并不影响我们的出题。例如思路②可以出(AXB)+C,但在我们的查重系统下,这道题目是和AXB+C无异的。
目前暂时无法解决的问题是在第一点钟提到的同级运算符,我们在后期才发现的AXB+CXD这一类问题。目前的解决思路是在最初随机两个非括号的运算符,如果不同按照常规进行运算,如果相同是加减则重新抽取,如果相同是乘除开始抽取参数并且在两个乘法式子中随机加减法完成出题。这样也解决了随机混合运算的问题。当然这个部分还没写(ノ´д`)。
2. 关键代码
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
public class Hello {
public static boolean examine(int strs[],int answer,Collection<String> list) {
String str1=""+answer;
Arrays.sort(strs);
for(int str : strs) {
str1=str1+str;
}
if(list.contains(str1)) {
return false;
}
list.add(str1);
return true;
}
public static void main(String[] args) throws IOException {
int[] str1 = new int[]{1,2,9,4,4,6};
int answer=10;
Collection<String> list=new HashSet<String>();
if(!examine(str1,answer,list)) {
System.out.println("存入的数组已存在");
}else {
System.out.println("存入的数组不存在");
}
int[] str2 = new int[]{1,2,9,4,4,6};
if(!examine(str2,answer,list)) {
System.out.println("存入的数组已存在");
}else {
System.out.println("存入的数组不存在");
}
int[] str3 = new int[]{3,2,9,4,4,6};
if(!examine(str3,answer,list)) {
System.out.println("存入的数组已存在");
}else {
System.out.println("存入的数组不存在");
}
}
}
这是一段在测试阶段测试是否能够正确运行的查重代码,为了方便异常什么的都是直接抛的并非原作业中的代码,可以直接复制运行,比较好理解。
查重思路是在主函数中创建Collection集合,将四则运算中所随机的每一个参数存入int类型的数组中,调用我们查重的examine()方法。方法中将其排序,方便之后的校对。定义String类型的str1初始保存这道题目的答案,遍历存放参数的数组添加进str1中。利用list.contains()判断存入list中是否存在,如果存在返回false,否则存入list并返回true。完成查重。
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 |
六、总结
擅用搜索软件,发现一篇博客更多重点的关键字,以及富含他人所需求的方法会吸引更多的人去看你的博客。于是我选择了查重作为这一次作业的关键代码。因为几乎找不到适合能理解可以学习使用的 ,哭了出来。
经过这一次作业我最大的感触应该就是结对合作。因为合作,我们在思路上总是有各种各样创新的想法。但同样是因为合作,我们差点打了起来。我们会因为有不同的思考方式,会各执己见,很难认同对方,低头。但也因此事半功倍去学习,去讨论,去解决目前面临的困境,尽管写到现在23点54分仍然很狼狈的在想如何写这篇博客,如何完善我们的代码。最值得反思的也是作为突发奇想提出最初的思路③的人,我带领我们两个人走向了一个不是特别好的方向。现在想想在后续开发上的确利用逆波兰函数来做这道题会容易的多,更一定程度上弥补了我们所缺少的这部分题目的缺陷。
最后戴青蛙头套写代码使我快乐