GitHub项目地址:https://github.com/LixinXie/WorkTogetherProject
结对成员:18软3谢立新(3118005071)、18软3高山(3118005048)
正文
一、项目需求
- 使用 -n 参数控制生成题目的个数,例如
Myapp.exe -n 10 将生成10个题目。
- 使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,例如
Myapp.exe -r 10
将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定,否则程序报错并给出帮助信息。
- 生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1− e2的子表达式,那么e1≥ e2。
- 生成的题目中如果存在形如e1÷ e2的子表达式,那么其结果应是真分数。
- 每道题目中出现的运算符个数不超过3个。
- 程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。例如,23 + 45 = 和45 + 23 = 是重复的题目,6 × 8 = 和8 × 6 = 也是重复的题目。3+(2+1)和1+2+3这两个题目是重复的,由于+是左结合的,1+2+3等价于(1+2)+3,也就是3+(1+2),也就是3+(2+1)。但是1+2+3和3+2+1是不重复的两道题,因为1+2+3等价于(1+2)+3,而3+2+1等价于(3+2)+1,它们之间不能通过有限次交换变成同一个题目。
生成的题目存入执行程序的当前目录下的Exercises.txt文件,格式如下:
-
- 四则运算题目1
- 四则运算题目2
- ……
其中真分数在输入输出时采用如下格式,真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8。
- 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件,格式如下:
-
- 答案1
- 答案2
特别的,真分数的运算如下例所示:1/6 + 1/8 = 7/24。
- 程序应能支持一万道题目的生成。
- 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,输入参数如下:
Myapp.exe -e <exercisefile>.txt -a <answerfile>.txt
统计结果输出到文件Grade.txt,格式如下:
Correct: 5 (1, 3, 5, 7, 9)
Wrong: 5 (2, 4, 6, 8, 10)
其中“:”后面的数字5表示对/错的题目的数量,括号内的是对/错题目的编号。为简单起见,假设输入的题目都是按照顺序编号的符合规范的题目。
二、设计实现过程
最初分析设计
后来边做边改进的设计
项目结构
三、代码说明
主函数,进行菜单选择。嵌入了一些记录运行时间的语句(暂时还不会AOP所以使用的传统方法)
1 public static void main(String[] args) throws Exception { 2 int n;//题目个数 3 int r;//数值取值范围 4 System.out.println("欢迎使用四则运算"); 5 System.out.println("输入 -q 生成题目和答案"); 6 System.out.println("输入 -c 验证你的答案"); 7 System.out.println("-------------------"); 8 System.out.println("请选择你的操作:"); 9 Scanner scanner = new Scanner(System.in); 10 String order = scanner.next(); 11 switch(order){ 12 case "-q": 13 System.out.println("题目个数-n (请输入一个正整数):"); 14 n=scanner.nextInt(); 15 System.out.println("数值取值范围-r (请输入一个正整数):"); 16 r=scanner.nextInt(); 17 if(n<0||r<0){ 18 System.out.println("请的输入有误!"); 19 break; 20 } 21 long startTime = System.currentTimeMillis(); // 获取开始时间 22 List<String> questionList = GenerateQuestion.generateQuestion(n,r); 23 long endTime = System.currentTimeMillis(); // 获取结束时间 24 25 26 long startTime2 = System.currentTimeMillis(); // 获取开始时间 27 List<String> answerList = Calculator.calculate(questionList); 28 long endTime2 = System.currentTimeMillis(); // 获取结束时间 29 30 31 long startTime3 = System.currentTimeMillis(); // 获取开始时间 32 OutputFile.printQuestion(questionList); 33 long endTime3 = System.currentTimeMillis(); // 获取结束时间 34 35 36 long startTime4 = System.currentTimeMillis(); // 获取开始时间 37 OutputFile.printAnswer(answerList); 38 long endTime4 = System.currentTimeMillis(); // 获取结束时间 39 40 System.out.println("generateQuestion方法运行时间: " + (endTime - startTime) + "ms"); 41 System.out.println("calculate方法运行时间: " + (endTime2 - startTime2) + "ms"); 42 System.out.println("printQuestion方法运行时间: " + (endTime3 - startTime3) + "ms"); 43 System.out.println("printAnswer方法运行时间: " + (endTime4 - startTime4) + "ms"); 44 break; 45 case "-c": 46 System.out.println("请输入题目文件:"); 47 Scanner scanner1 = new Scanner(System.in); 48 String questionfilepath = scanner1.nextLine(); 49 50 System.out.println("请输入答案文件:"); 51 Scanner scanner2 = new Scanner(System.in); 52 String answerfilepath = scanner2.nextLine(); 53 54 List<List> result = CompareAnswer.compareAnswer(questionfilepath,answerfilepath); 55 OutputFile.printCompareResult(result); 56 break; 57 default: 58 System.out.println("你的输入有误!"); 59 break; 60 } 61 }
生成题目的类,其中包括生成随机符号和随机正整数的方法,各种题目类型的生成方法。生成题目过程中使用了最笨的方法来保证题目计算过程非负
1 public class GenerateQuestion { 2 3 //生成题目,可被其他类调用 4 public static List<String> generateQuestion(int n, int r){ 5 List<String> questionList = new ArrayList<>(); 6 Random random = new Random(); 7 for(int i=0;i<n;i++){ 8 int j = random.nextInt(3); 9 switch (j){ 10 case 0: questionList.add(oneSymbol(r)); 11 break; 12 case 1: questionList.add(twoSymbol(r)); 13 break; 14 case 2: questionList.add(threeSymbol(r)); 15 break; 16 } 17 } 18 return questionList; 19 } 20 //生成一个随机符号 21 public static char randomSymbol(){ 22 Character[] s = new Character[4]; 23 Random r= new Random(); 24 int i = r.nextInt(4); 25 int b; 26 s[0] = '+'; 27 s[1] = '-'; 28 s[2] = '*'; 29 s[3] = '/'; 30 31 return s[i]; 32 } 33 34 //生成一个随机正整数 35 public static int randomInt(int r){ 36 Random ra = new Random(); 37 int i; 38 while(true) { 39 i = ra.nextInt(r); 40 if(i!=0)break; 41 } 42 return i; 43 } 44 45 //生成一个含一个符号的题目 46 public static String oneSymbol(int r){ 47 String[] s = new String[2]; 48 int a,b; 49 char c; 50 c = randomSymbol(); 51 a = randomInt(r); 52 b = randomInt(r); 53 while(c=='-'){//保证计算过程非负 54 if(a-b>=0){ 55 break; 56 }else 57 c = randomSymbol(); 58 } 59 s[1] = a + "" + c + b; 60 return s[1]; 61 } 62 //生成一个含两个符号的题目 63 public static String twoSymbol(int r){ 64 String[] s = new String[2]; 65 int a,b,c; 66 char c1,c2; 67 a = randomInt(r); 68 b = randomInt(r); 69 c = randomInt(r); 70 c1 = randomSymbol(); 71 c2 = randomSymbol(); 72 while(c1=='-'||c2=='-'){//保证计算过程非负;当符号为-时判断过程是否产生负数,产生负数则重新生成符号 73 if(c1=='-'&&c2=='-'){// - - 74 if(a-b-c>=0) 75 break; 76 }else if(c1=='-'){// - 其他 77 if(c2=='*' && a-(b*c)>=0){// - * 78 break; 79 }if(c2=='/'&& a*c-b>=0){// - / 80 break; 81 } 82 if(c2=='+'&& a-b>=0)// - + 83 break; 84 }else {// 其他 - 85 if(c1=='+' && b-c>=0){// + - 86 break; 87 }if(c1=='*' && (a*b)-c>=0){// * - 88 break; 89 }if(c1== '/' && a-b*c>=0){// / - 90 break; 91 } 92 } 93 c1 = randomSymbol(); 94 c2 = randomSymbol(); 95 } 96 s[1] =""+ a + c1 + b + c2 + c; 97 return s[1]; 98 } 99 //生成一个含三个符号的题目 100 public static String threeSymbol(int r){ 101 int a=0,b=0,c=0,d=0; 102 String s; 103 String s1,s2; 104 char c1 = randomSymbol(); 105 char c2 = randomSymbol(); 106 char c3 = randomSymbol(); 107 a = randomInt(r); 108 b = randomInt(r); 109 c = randomInt(r); 110 d = randomInt(r); 111 if (c1 == '-' || c2 == '-' || c3 == '-') { 112 while (true) { 113 if (c1 == '-' && c2 == '-' && c3 == '-') { 114 if (a - b - c - d >= 0) break; 115 } 116 else if (c1=='-'&&c2=='-'){ 117 if(c3=='+' && a-b-c>=0) break; 118 else if(c3=='*' && a-b-(c*d)>=0)break; 119 else if(c3=='/' && a*d-b*d-c>=0)break; 120 } 121 else if(c1=='-'&&c3=='-') { 122 if (c2 == '+' && a-b>=0 &&c-d>=0)break; 123 else if (c2 == '*' && a-(b*c)-d>=0)break; 124 else if (c2 == '/' && a*c-b-c*d>=0)break; 125 } 126 else if(c2=='-'&&c3=='-') { 127 if (c1 == '+' && b-c-d>=0)break; 128 else if (c1 == '*' && (a*b)-c-d>=0)break; 129 else if (c1 == '/' && a-b*c-b*d>=0)break; 130 } 131 else if(c1=='-'){ 132 if(c2=='+') { 133 if(c3=='+' && a-b>=0)break; 134 else if(c3=='*' && a-b>=0)break; 135 else if(c3=='/' && a-b>=0)break; 136 } 137 else if(c2=='*'){ 138 if(c3=='+' && a-(b*c)>=0)break; 139 else if(c3=='*' && a-(b*c*d)>=0)break; 140 else if(c3=='/' && a*d-b*c>=0)break; 141 } 142 else if(c2=='/'){ 143 if(c3=='+' && a*c-b>=0)break; 144 else if(c3=='*' && a*c-b*d>=0)break; 145 else if(c3=='/' && a*c*d-b>=0)break; 146 } 147 } 148 else if(c2=='-') 149 { 150 if(c1=='+') { 151 if(c3=='+' && b-c>=0)break; 152 else if(c3=='*' && (b-(c*d))>=0)break; 153 else if(c3=='/' && b*d-c>=0)break; 154 } 155 else if(c1=='*'){ 156 if(c3=='+' && (a*b)-c>=0)break; 157 else if(c3=='*' && (a*b)-(c*d)>=0)break; 158 else if(c3=='/' && a*b*d-c>=0)break; 159 } 160 else if(c1=='/'){ 161 if(c3=='+' && a-b*c>=0)break; 162 else if(c3=='*' && a-b*c*d>=0)break; 163 else if((c3=='/') &&a*d-b*c>=0)break; 164 } 165 } 166 else if(c3=='-') 167 { 168 if(c1=='+') { 169 if(c2=='+' && c-d>=0)break; 170 else if(c2=='*' && ((b*c)-d)>=0)break; 171 else if(c2=='/' && b-c*d>=0)break; 172 } 173 else if(c1=='*'){ 174 if(c2=='+' && c-d>=0)break; 175 else if(c2=='*' && (a*b*c-d)>=0)break; 176 else if(c2=='/' && a*b-c*d>=0)break; 177 } 178 else if(c1=='/'){ 179 if(c2=='+' && c-d>=0)break; 180 else if(c2=='*' && a*c-b*d>=0)break; 181 else if(c2=='/' && a-b*c*d>=0)break; 182 } 183 else continue; 184 } 185 c1 = randomSymbol(); 186 c2 = randomSymbol(); 187 c3 = randomSymbol(); 188 } 189 } 190 s =""+ a + c1 + b + c2 + c + c3 + d ; 191 return s; 192 } 193 }
计算类,其中有四则运算方法,各种类型题目计算方法。没有使用中缀表达式转后缀表达式进行计算(挺遗憾),因为对后缀表达式不太熟,所以还是用了最笨的方法进行计算
1 public class Calculator { 2 public static List<String> calculate(List<String> questionList){//计算题目表达式,题目中没有=和题号 3 List<String> answerList = new ArrayList<>(); 4 for(int i=0;i<questionList.size();i++){ 5 String str = questionList.get(i);//从list中获取题目 6 char[] strarray = str.toCharArray();//将当前题目字符串转化为字符数组 7 int[] sysmbolindex = new int[3];//记录当前题目表达式中按顺序出现的计算符号的索引 8 char[] sysmbol = new char[3];//记录当前题目表达式中按顺序出现的各个计算符号 9 int x=0;//x标记当前sysmbolindex数组存放到第几个(索引) 10 int y=0;//y标记当前sysmbol数组存放到第几个(索引) 11 int sumsysmbol=0;//记录当前题目运算符个数 12 for(int j=0;j<strarray.length;j++){//遍历字符数组记录运算符和运算符索引 13 if(strarray[j]=='+'||strarray[j]=='-'||strarray[j]=='*'||strarray[j]=='/'){ 14 sysmbolindex[x]=j;//记录当前运算符的索引 15 sysmbol[y]=strarray[j];//记录当前运算符 16 if(x<sysmbolindex.length-1) 17 x++;//将x向后移动一位 18 if(y<sysmbol.length-1) 19 y++;//将y向后移动一位 20 sumsysmbol++;//运算符个数加一 21 } 22 } 23 if(sumsysmbol==1){//一运算符题目 24 String result = null; 25 int num1 = Integer.parseInt(str.substring(0,sysmbolindex[0]));//运算符左边的数 26 int num2 = Integer.parseInt(str.substring(sysmbolindex[0]+1));//运算符右边的数 27 result = twoNumCalculate(num1,num2,sysmbol[0]); 28 answerList.add(result); 29 }else if(sumsysmbol==2){//二运算符题目 30 String result = null; 31 int num1 = Integer.parseInt(str.substring(0,sysmbolindex[0])); 32 int num2 = Integer.parseInt(str.substring(sysmbolindex[0]+1,sysmbolindex[1])); 33 int num3 = Integer.parseInt(str.substring(sysmbolindex[1]+1)); 34 result = threeNumCalculate(num1,num2,num3,sysmbol[0],sysmbol[1]); 35 answerList.add(result); 36 }else if(sumsysmbol==3){//三运算符题目 37 String result = null; 38 int num1 = Integer.parseInt(str.substring(0,sysmbolindex[0])); 39 int num2 = Integer.parseInt(str.substring(sysmbolindex[0]+1,sysmbolindex[1])); 40 int num3 = Integer.parseInt(str.substring(sysmbolindex[1]+1,sysmbolindex[2])); 41 int num4 = Integer.parseInt(str.substring(sysmbolindex[2]+1)); 42 result = fourNumCalculate(num1,num2,num3,num4,sysmbol[0],sysmbol[1],sysmbol[2]); 43 answerList.add(result); 44 } 45 } 46 return answerList; 47 } 48 //加法 49 public static int add(int l, int r){ 50 return l+r; 51 } 52 //减法 53 public static int sub(int l, int r){ 54 return l-r; 55 } 56 //乘法 57 public static int mul(int l, int r){ 58 return l*r; 59 } 60 //除法 61 public static String div(int l, int r){ 62 String result = null; 63 if(l==0){//被除数等于0 64 result = "" + 0; 65 }else if(r==0){//除数等于0 66 result = "NaN"; 67 }else if(l==r){//等于1的情况 68 result = "1"; 69 }else if(l<r){//真分数情况 70 if(maxCommonDivisor(l,r)==1){//已不可化简 71 result = l+"/"+r; 72 }else{//可化简 73 int mcd = maxCommonDivisor(l,r); 74 int rl = l/mcd; 75 int rr = r/mcd; 76 result = rl + "/" + rr; 77 } 78 }else{//被除数大于除数 79 int rl = l; 80 int rr = r; 81 if(rl%rr==0)//可整除 82 result = "" + rl/rr; 83 else{//不可整除,假分数处理,转化为带分数形式 84 if(maxCommonDivisor(l,r)!=1){//有公约数,先化简 85 int mcd = maxCommonDivisor(l,r); 86 rl = l/mcd; 87 rr = r/mcd; 88 } 89 int c = rl/rr;//除数 90 int y = rl%rr;//余数 91 result = c + "'"+ y +"/" +rr; 92 } 93 } 94 return result; 95 } 96 //辗转相除法求两数最大公约数 97 public static int maxCommonDivisor(int a, int b){ 98 if(b == 0){ 99 return a; 100 } 101 int r = a%b; 102 return maxCommonDivisor(b,r); 103 } 104 //二元运算 105 public static String twoNumCalculate(int num1, int num2 , char s){ 106 String result = ""; 107 if(s=='+'){ 108 result = "" + add(num1,num2); 109 }else if(s=='-'){ 110 result = "" + sub(num1,num2); 111 }else if(s=='*'){ 112 result = "" + mul(num1,num2); 113 }else if(s=='/'){ 114 result = div(num1,num2); 115 } 116 return result; 117 } 118 //三元运算 119 public static String threeNumCalculate(int num1, int num2, int num3, char c1, char c2){ 120 String result = ""; 121 if(c1=='+'){ 122 switch (c2){ 123 case '+':result = "" + add(add(num1,num2),num3);break; 124 case '-':result = "" + sub(add(num1,num2),num3);break; 125 case '*':result = "" + add(num1,mul(num2,num3));break; 126 case '/':result = div(num1*num3+num2,num3);break; 127 } 128 }else if(c1=='-'){ 129 switch (c2){ 130 case '+':result = "" + add(sub(num1,num2),num3);break; 131 case '-':result = "" + sub(sub(num1,num2),num3);break; 132 case '*':result = "" + sub(num1,mul(num2,num3));break; 133 case '/':result = div(num1*num3-num2,num3);break; 134 } 135 }else if(c1=='*'){ 136 switch (c2){ 137 case '+':result = "" + add(mul(num1,num2),num3);break; 138 case '-':result = "" + sub(mul(num1,num2),num3);break; 139 case '*':result = "" + mul(mul(num1,num2),num3);break; 140 case '/':result = div(num1*num2,num3);break; 141 } 142 }else if(c1=='/'){ 143 switch (c2){ 144 case '+':result = div(num1+num2*num3,num2);break; 145 case '-':result = div(num1-num2*num3,num2);break; 146 case '*':result = div(num1*num3,num2);break; 147 case '/':result = div(num1,num2*num3);break; 148 } 149 } 150 return result; 151 } 152 //四元运算 153 public static String fourNumCalculate(int num1, int num2, int num3, int num4, char c1, char c2, char c3){ 154 String result = ""; 155 if(c1=='+'){//第一是+ 156 if (c2=='+'){ 157 switch (c3){ 158 case '+':result = "" + add(add(add(num1,num2),num3),num4);break; 159 case '-':result = "" + sub(add(add(num1,num2),num3),num4);break; 160 case '*':result = "" + add(add(num1,num2),mul(num3,num4));break; 161 case '/':result = div(add(num1,num2)*num4+num3,num4);break; 162 } 163 }else if(c2=='-'){ 164 switch (c3){ 165 case '+':result = "" + add(sub(add(num1,num2),num3),num4);break; 166 case '-':result = "" + sub(sub(add(num1,num2),num3),num4);break; 167 case '*':result = "" + sub(add(num1,num2),mul(num3,num4));break; 168 case '/':result = div(add(num1,num2)*num4-num3,num4);break; 169 } 170 }else if(c2=='*'){ 171 switch (c3){ 172 case '+':result = "" + add(add(num1,mul(num2,num3)),num4);break; 173 case '-':result = "" + sub(add(num1,mul(num2,num3)),num4);break; 174 case '*':result = "" + add(num1,mul(mul(num2,num3),num4));break; 175 case '/':result = div(num1*num4+mul(num2,num3),num4);break; 176 } 177 }else if(c2=='/'){ 178 switch(c3){ 179 case '+':result = div(add(num1,num4)*num3+num2,num3);break; 180 case '-':result = div(sub(num1,num4)*num3+num2,num3);break; 181 case '*':result = div(num1*num3+mul(num2,num4),num3);break; 182 case '/':result = div(num1*num3*num4+num2,num3*num4);break; 183 } 184 } 185 }else if(c1=='-'){//第一是- 186 if (c2=='+'){ 187 switch (c3){ 188 case '+':result = "" + (num1-num2+num3+num4);break; 189 case '-':result = "" + (num1-num2+num3-num4);break; 190 case '*':result = "" + (num1-num2+num3*num4);break; 191 case '/':result = div((num1-num2)*num4+num3,num4);break; 192 } 193 }else if(c2=='-'){ 194 switch (c3){ 195 case '+':result = "" + (num1-num2-num3+num4);break; 196 case '-':result = "" + (num1-num2-num3-num4);break; 197 case '*':result = "" + (num1-num2-num3*num4);break; 198 case '/':result = div((num1-num2)*num4-num3,num4);break; 199 } 200 }else if(c2=='*'){ 201 switch (c3){ 202 case '+':result = "" + (num1-num2*num3+num4);break; 203 case '-':result = "" + (num1-num2*num3-num4);break; 204 case '*':result = "" + (num1-num2*num3*num4);break; 205 case '/':result = div(num1*num4-num2*num3,num4);break; 206 } 207 }else if(c2=='/'){ 208 switch(c3){ 209 case '+':result = div((num1+num4)*num3-num2,num3);break; 210 case '-':result = div((num1-num4)*num3-num2,num3);break; 211 case '*':result = div(num1*num3-num2*num4,num3);break; 212 case '/':result = div(num1*num3*num4-num2,num3*num4);break; 213 } 214 } 215 }else if(c1=='*'){//第一是* 216 if (c2=='+'){ 217 switch (c3){ 218 case '+':result = "" + (num1*num2+num3+num4);break; 219 case '-':result = "" + (num1*num2+num3-num4);break; 220 case '*':result = "" + (num1*num2+num3*num4);break; 221 case '/':result = div(num1*num2*num4+num3,num4);break; 222 } 223 }else if(c2=='-'){ 224 switch (c3){ 225 case '+':result = "" + (num1*num2-num3+num4);break; 226 case '-':result = "" + (num1*num2-num3-num4);break; 227 case '*':result = "" + (num1*num2-num3*num4);break; 228 case '/':result = div(num1*num2*num4-num3,num4);break; 229 } 230 }else if(c2=='*'){ 231 switch (c3){ 232 case '+':result = "" + (num1*num2*num3+num4);break; 233 case '-':result = "" + (num1*num2*num3-num4);break; 234 case '*':result = "" + (num1*num2*num3*num4);break; 235 case '/':result = div(num1*num2*num3,num4);break; 236 } 237 }else if(c2=='/'){ 238 switch(c3){ 239 case '+':result = div(num1*num2+num3*num4,num3);break; 240 case '-':result = div(num1*num2-num3*num4,num3);break; 241 case '*':result = div(num1*num2*num4,num3);break; 242 case '/':result = div(num1*num2,num3*num4);break; 243 } 244 } 245 }else if(c1=='/'){//第一是/ 246 if (c2=='+'){ 247 switch (c3){ 248 case '+':result = div(num1+num2*num3+num2*num4,num2);break; 249 case '-':result = div(num1+num2*num3-num2*num4,num2);break; 250 case '*':result = div(num1+num2*num3*num4,num2);break; 251 case '/':result = div(num1*num4+num2*num3,num2*num4);break; 252 } 253 }else if(c2=='-'){ 254 switch (c3){ 255 case '+':result = div(num1-num2*num3+num2*num4,num2);break; 256 case '-':result = div(num1-num2*num3-num2*num4,num2);break; 257 case '*':result = div(num1-num2*num3*num4,num2);break; 258 case '/':result = div(num1*num4-num2*num3,num2*num4);break; 259 } 260 }else if(c2=='*'){ 261 switch (c3){ 262 case '+':result = div(num1*num3+num2*num4,num2);break; 263 case '-':result = div(num1*num3-num2*num4,num2);break; 264 case '*':result = div(num1*num3*num4,num2);break; 265 case '/':result = div(num1*num3,num2*num4);break; 266 } 267 }else if(c2=='/'){ 268 switch(c3){ 269 case '+':result = div(num1+num2*num3*num4,num2*num3);break; 270 case '-':result = div(num1-num2*num3*num4,num2*num3);break; 271 case '*':result = div(num1*num4,num2*num3);break; 272 case '/':result = div(num1,num2*num3*num4);break; 273 } 274 } 275 } 276 return result; 277 } 278 }
比较答案的类,其中有读取题目和答案到List的方法,一个比较的方法比较题目计算得到的答案与读取到的答案
1 public class CompareAnswer { 2 //验证答案正误,list里面装两个list 3 public static List<List> compareAnswer(String questionFilepath, String yourAnswerFilepath) throws Exception { 4 List<List> compareResult = new ArrayList<>(); 5 List<String> correctlist = new ArrayList(); 6 List<String> wronglist = new ArrayList(); 7 List<String> questionlist = questionfile2list(questionFilepath); 8 List<String> answerlist = Calculator.calculate(questionlist); 9 List<String> youranswerlist = answerfile2list(yourAnswerFilepath); 10 for(int i=0;i<questionlist.size() && i<answerlist.size();i++){ 11 if(answerlist.get(i).equals(youranswerlist.get(i))){ 12 String str =i+1+"";//索引+1变题号 13 correctlist.add(str); 14 }else{ 15 String str =i+1+""; 16 wronglist.add(str); 17 } 18 } 19 compareResult.add(correctlist); 20 compareResult.add(wronglist); 21 return compareResult; 22 } 23 public static List<String> questionfile2list(String filepath) throws Exception { 24 List<String> list = new ArrayList<>(); 25 FileReader fileReader = new FileReader(filepath); 26 BufferedReader bufferedReader = new BufferedReader(fileReader); 27 String str = null; 28 while ((str=bufferedReader.readLine())!=null){ 29 int begin = str.indexOf("、")+1; 30 int end = str.indexOf(" ="); 31 str = str.substring(begin,end); 32 str.replaceAll("\\s*",""); 33 list.add(str); 34 } 35 return list; 36 } 37 public static List<String> answerfile2list(String filepath) throws Exception { 38 List<String> list = new ArrayList<>(); 39 FileReader fileReader = new FileReader(filepath); 40 BufferedReader bufferedReader = new BufferedReader(fileReader); 41 String str = null; 42 while ((str=bufferedReader.readLine())!=null){ 43 int begin = str.indexOf("、")+1; 44 str = str.substring(begin); 45 str.replaceAll("\\s*",""); 46 list.add(str); 47 } 48 return list; 49 } 50 }
写文件的类,提供三种写文件的方法,它们写入的格式各不相同
public class OutputFile { //输出题目文件 public static void printQuestion(List<String> queList){ File file = new File("./Exercises.txt"); questionlist2file(file,queList); System.out.println("生成题目成功!"); } //输出答案文件 public static void printAnswer(List<String> ansList){ File file = new File("./Answers.txt"); answerlist2file(file,ansList); System.out.println("生成答案成功!"); } //输出答案的比较结果 public static void printCompareResult(List<List> resultList){ File file = new File("./Grade.txt"); compareresultlist2file(file,resultList); System.out.println("答案验证完成!"); } //输出题目文件 public static void questionlist2file(File file, List<String> list){ int i=1;//行号从1开始 BufferedWriter writer = null; try { writer = new BufferedWriter(new FileWriter(file)); for (String s : list) { writer.write(i+"、"+s+" ="+"\n"); i++; } } catch (IOException e) { e.printStackTrace(); }finally { if(writer!=null){ try { writer.close(); } catch (IOException e) { e.printStackTrace(); } } } } //输出答案文件 public static void answerlist2file(File file, List<String> list){ int i=1;//行号从1开始 BufferedWriter writer = null; try { writer = new BufferedWriter(new FileWriter(file)); for (String s : list) { writer.write(i+"、"+s+"\n"); i++; } } catch (IOException e) { e.printStackTrace(); }finally { if(writer!=null){ try { writer.close(); } catch (IOException e) { e.printStackTrace(); } } } } //输出比较结果文件 public static void compareresultlist2file(File file, List<List> list){ BufferedWriter writer = null; int sumcorrect=0; int sumwrong=0; try { writer = new BufferedWriter(new FileWriter(file)); List correct = list.get(0); List wrong = list.get(1); sumcorrect=correct.size(); sumwrong=wrong.size(); writer.write("Correct: "+sumcorrect); writer.write(" ("); for(int i=0;i<correct.size();i++){ writer.write((String) correct.get(i)); if(i<correct.size()-1){ writer.write(", "); } } writer.write(")"); writer.write("\n"); writer.write("Wrong: "+sumwrong); writer.write(" ("); for(int j=0;j<wrong.size();j++){ writer.write((String) wrong.get(j)); if(j<wrong.size()-1){ writer.write(", "); } } writer.write(")"); } catch (IOException e) { e.printStackTrace(); }finally { if(writer!=null){ try { writer.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
四、测试运行
第一种测试:(生成几个题目)
修改几个答案后的比较结果:
第二种测试:(生成1万道题目)
第三种测试:(生成100万道题目)
第四种测试:(生成一亿道题目的测试)
经过测试,在生成一亿道题目过程中:
CPU几乎占100%,JVM的GC在一小段时间后几乎占100%,内存几乎满;
在测试进行到6分多钟的时候,出现了GC overhead limit exceeded,没有足够的内存而触发了GC的保护机制
(JVM花费了98%的时间进行垃圾回收,而只得到2%可用的内存)
运行生成题目和答案代码覆盖率
运行验证答案的代码覆盖率
五、效能分析
生成题目较少时:
各方法执行时间相差不大,时间有波动
生成一万道题目时:
经过几次测试,生成题目的方法时间较长
生成一百万道题目时:
经过几次测试,这时输出答案用时较长
资源消耗情况
十道题目,很普通
一万道题目,也没消耗多少资源
一百万道题目,过程中看到进行了一次GC,资源消耗较大了
一亿道题目(最终没有成功)。CPU爆满,GC爆满,内存爆满。由于安全机制自动停止了。
六、遇到的困难及解决方法
比较答案的时候读入题目文件去掉题号和=号时不能去掉英文点
解决办法:将题号的英文点换成中文顿号
在写计算类的时候,发现一开始的设计不能不能扩展更多计算符和带括号的题目
解决办法:没有考虑带括号的题目了,也没有考虑兼容更多符号的题目了;但能够支持数值是两位数甚至更多位的情况
遇到生成题目保证非负的时候,生成100万道还是有0.63%的错误率
解决办法:经检查,是因为忽略了/运算符在编程语言里的处理是整除,即11/10的结果是1;经过改正,只要保证有/运算的题目通分后的分子非负即可。
七、PSP
PSP |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
||
· Estimate |
· 估计这个任务需要多少时间 |
30 |
26 |
Development |
开发 |
||
· Analysis |
· 需求分析 (包括学习新技术) |
120 |
148 |
· Design Spec |
· 生成设计文档 |
120 |
122 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
60 |
30 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
10 |
10 |
· Design |
· 具体设计 |
60 |
55 |
· Coding |
· 具体编码 |
1000 |
923 |
· Code Review |
· 代码复审 |
120 |
220 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
120 |
190 |
Reporting |
报告 |
||
· Test Report |
· 测试报告 |
30 |
45 |
· Size Measurement |
· 计算工作量 |
5 |
5 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
120 |
111 |
合计 |
1795 |
1885 |
八、项目小结
本次结对项目,一开始就花了挺长时间进行需求分析,这让我们后面的进展就比较容易。遗憾的是在算法方面我们的程序不够先进,兼容性也不够;也遗憾没有进行查重操作。在查因除法操作产生bug花了比较多时间,万万没想到的是没有熟悉除法/在编程里面的真正含义。
立新项目感想:
本次组队项目我更一步了解GitHub的一些操作,更多的了解java编程的用法,印象深刻的记住了编程语言与数学语言的不同;懂得了与合作伙伴沟通的重要性。
高山项目感想:
本次组队项目让我近一步学习了GitHub的使用,同时也是我初次尝试java项目,让我对于面向对象编程有了更深入的认识。同时,组队项目也让我深刻的意识到在一个团队中听取和采纳别人的意见,并且从不同想法中择优是软件工程项目中至关重要的。