这次上课,老师对四则运算的程序又做了一些系统的要求。
一、要求
1.定义:
自然数:0, 1, 2, …。
真分数:1/2, 1/3, 2/3, 1/4, 1’1/2, …。
运算符:+, −, ×, ÷。
括号:(, )。
等号:=。
分隔符:空格(用于四则运算符和等号前后)。
2.算术表达式:
e := n | e1 + e2 | e1 − e2 | e1 × e2 | e1 ÷ e2 | (e),
其中e, e1和e2为表达式,n为自然数或真分数。
四则运算题目:e = ,其中e为算术表达式。
3.定义参数控制生成题目的个数。
例如,参数n=10;则将生成10个题目。
4.定义参数控制题目中数值(自然数、真分数和真分数分母)的范围。
例如参数r= 10,将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。
该参数必须给定,否则程序报错并给出帮助信息。
5.生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1 − e2的子表达式,那么e1 ≥ e2。
6.生成的题目中如果存在形如e1 ÷ e2的子表达式,那么其结果应是真分数。
7.每道题目中出现的运算符个数不超过3个。
8.程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目
例如,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,它们之间不能通过有限次交换变成同一个题目
9.生成的题目存储到数据库中,格式如下:
1. 四则运算题目1
2. 四则运算题目2
……
其中真分数在输入输出时采用如下格式,真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8。
10.在生成题目的同时,计算出所有题目的答案,并存入数据库文件。格式如下:
1. 答案1
2. 答案2
特别的,真分数的运算如下例所示:1/6 + 1/8 = 7/24。
11.程序应能支持一万道题目的生成。
12.程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,
统计结果输出到数据表文件Grade,格式如下:
Correct: 5 (1, 3, 5, 7, 9)
Wrong: 5 (2, 4, 6, 8, 10)
其中“:”后面的数字5表示对/错的题目的数量,括号内的是对/错题目的编号。为简单起见,假设输入的题目都是按照顺序编号的符合规范的题目。
二、编程思路
(1)查重
每三个元素划分为一组,来进行子表达式之间的查重,进行判断是否相同。
(2)数据库连接、释放
获取数据库连接的方法是将注册数据库驱动,获得一个数据库的连接进行封装,返回一个数据库连接对象;另外,释放资源的方法是将释放查询数据库结果集对象、数据库操作对象和数据库连接对象进行封装。
三、源代码:
package hh; import java.util.Random; import java.util.Scanner; import java.util.Stack; //分数类 class fraction { public int fenMu,fenZi; public int getFenMu() { return fenMu; } public void setFenMu(int fenMu) { this.fenMu = fenMu; } public int getFenZi() { return fenZi; } public void setFenZi(int fenZi) { this.fenZi = fenZi; } public fraction(int fenMu, int fenZi) { this.fenMu = fenMu; this.fenZi = fenZi; yueFen(); } public fraction(){} //找出两数的最大公约数 public int MaxCommon(int a,int b){ //保证第一个参数大于第二个参数 if(a<b) { int temp; temp=a; a=b; b=temp; } while(a%b!=0) //在余数不为零时循环 { int temp=a%b; a=b; b=temp; } return b; //返回最大公约数 } //找出两数的最小公倍数 public int MinCommon(int a,int b){ return a*b/MaxCommon(a,b); } //约分化简 public void yueFen(){ int y = 1; for(int i = fenZi;i > 1;i--){ if(fenZi % i == 0 && fenMu % i == 0){ y = i; break; } } fenZi /= y; fenMu /= y; } //计算(加) public fraction add(fraction b){ fraction c = null; int newFenZi = this.fenZi * b.getFenMu() + this.fenMu * b.getFenMu(); int newFenMu = this.fenMu * b.getFenMu(); c = new fraction(newFenMu,newFenZi); return c; } //计算(减) public fraction subtract(fraction b){ fraction c = null; int newFenZi = this.fenZi * b.getFenMu() - this.fenMu * b.getFenZi(); int newFenMu = this.fenMu * b.getFenMu(); c = new fraction(newFenMu,newFenZi); return c; } //计算(乘) public fraction multiply(fraction b){ fraction c = null; int newFenZi = this.fenZi * b.getFenZi(); int newFenMu = this.fenMu * b.getFenMu(); c = new fraction(newFenMu,newFenZi); return c; } //计算(除) public fraction divide(fraction b){ fraction c = null; int newFenZi = this.fenZi * b.getFenMu(); int newFenMu = this.fenMu * b.getFenZi(); c = new fraction(newFenMu,newFenZi); return c; } //输出分数形式 public String toString(){ if(fenZi != 0){ return fenZi + "/" + fenMu; } return "0"; } } public class kk { public static String[] Answer=new String[100]; //生成整数计算式添加限制条件,type为运算式类型 0代表整数式,1代表真分数式 public static String[] createYunSuanShi(int hasChengChu,int hasKuoHao,int hasFuShu,int hasYuShu,int minNum,int maxNum,int n,int type) { int i = 0; int z = 0; String yunSuanShiTemp; String[] yunSuanShiArray = new String[n]; int operatorScope = 2 + 2 * hasChengChu;//运算符范围,2或4,2代表只有加减,4代表有加减乘除 int length; String[] operatorArray = {"+","-","*","/"}; String[] operatorNum = null;//存储运算数 int num_index;//运算数下标 String[] operatorSymbol = null;//存储运算符 int symbol_index;//运算符下标 int[] brackets = null;//存储括号个数 while(i < n) { length = Integer.parseInt(getOperatorNumber(0, 0,9)) + 2;//计算式运算数长度 operatorNum = new String[length]; operatorSymbol = new String[length - 1]; num_index = 0; symbol_index = 0; operatorNum[num_index++] = getOperatorNumber(type,minNum, maxNum);//随机生成操作数 for(int j = 0;j < length - 1;j++){ operatorSymbol[symbol_index++] = operatorArray[Integer.parseInt(getOperatorNumber(0,0,operatorScope-1))];//随机生成操作符 operatorNum[num_index++] = getOperatorNumber(type, minNum,maxNum);//随机生成操作数 } if(hasKuoHao == 1){ brackets = randomAddBracket(length);//生成括号数组 } //构造运算式 yunSuanShiTemp = ""; for(int j = 0;j < length;j++){ //添加左括号 if(hasKuoHao == 1){ for(int k = 0;k < brackets[j];k++){ yunSuanShiTemp += "("; } } yunSuanShiTemp += " " + operatorNum[j] + " ";//加上运算数 //添加右括号 if(hasKuoHao == 1){ for(int k = 0;k > brackets[j];k--){ yunSuanShiTemp += ")"; } } //如果不是最后一个运算数则要加上运算符 if(j != length - 1){ yunSuanShiTemp += operatorSymbol[j]; } } //计算结果 String answer = expressCalculate(yunSuanShiTemp, hasFuShu, hasYuShu, type, length - 1); if((answer.equals("ERROR"))){ continue; } yunSuanShiTemp += "=" /*+ answer*/; Answer[z]=answer; z++; //检验重复 boolean chongFu = false; for(int j = 0;j < i;j++){ if((yunSuanShiArray[j].equals(yunSuanShiTemp))){ chongFu = true; break; } } if(chongFu == false){ yunSuanShiArray[i++] = yunSuanShiTemp; } } return yunSuanShiArray; } //表达式计算,参数为字符串类型的运算式 public static String expressCalculate(String express,int hasFuShu,int hasYuShu,int type,int symbolNum){ Stack<String> num = new Stack<String>(); Stack<String> symbolS = new Stack<String>(); symbolS.push("#"); express += "#"; char ch; int i = 0; int s = 0; ch = express.charAt(i); while(s < symbolNum){ if(ch == ' '){//读到空格,说明开始读运算数 String readNumStr = ""; while(true){ ch = express.charAt(++i); if(ch == ' '){ break; } readNumStr += ch; } if((i + 1) < express.length()){ ch = express.charAt(++i); } num.push(readNumStr); }else{//读到的是运算符 char compare = priorityCompare(symbolS.peek(),ch + ""); if(compare == '='){//如果是右括号 symbolS.pop(); ch = express.charAt(++i); }else if(compare == '>'){//ch的优先级小于栈顶的优先级 比栈顶的优先级高就不算,入栈,低就弹栈运算 //弹出两个运算数,弹出一个运算符 String bStr = num.pop(); String aStr = num.pop(); String symbolT = symbolS.pop(); String c = yunSuan(aStr,bStr,symbolT,hasFuShu,hasYuShu,type); if(c.equals("ERROR")){ return "ERROR"; }else if(c.indexOf("余") >= 0 && s != symbolNum - 1){//有余数 return "ERROR"; }else{ num.push(c); } s++; }else{ symbolS.push(ch + ""); if((i + 1) < express.length()){ ch = express.charAt(++i); } } } } return num.pop(); } public static String yunSuan(String aStr,String bStr,String symbol,int hasFuShu,int hasYuShu,int type){ if(type == 0){//整数 int a = Integer.parseInt(aStr); int b = Integer.parseInt(bStr); if(symbol.equals("+")){ return "" + (a + b); }else if(symbol.equals("-")){ if(a - b < 0 && hasFuShu == 0){ return "ERROR"; }else{ return "" + (a - b); } }else if(symbol.equals("*")){ return "" + (a * b); }else{ if(b == 0){ return "ERROR"; } if(a % b == 0){ return "" + (a / b); }else{ if(hasYuShu == 1){ return (a / b) + "余" + (a % b); }else{ return "ERROR"; } } } }else{//分数 String[] af = aStr.split("/"); String[] bf = bStr.split("/"); if(af[0].equals("0") || bf[0].equals("0")){ return "ERROR"; } fraction a = new fraction(Integer.parseInt(af[1]),Integer.parseInt(af[0])); fraction b = new fraction(Integer.parseInt(bf[1]),Integer.parseInt(bf[0])); if(symbol.equals("+")){ return a.add(b).toString(); }else if(symbol.equals("-")){ fraction c = a.subtract(b); if(hasFuShu == 1 && c.getFenZi() < 0){ return "ERROR"; } return c.toString(); }else if(symbol.equals("*")){ return a.multiply(b).toString(); }else{ return a.divide(b).toString(); } } } //判断优先级 public static char priorityCompare(String a,String b){ char[][] priority = { {'>','>','<','<','<','>','>'}, {'>','>','<','<','<','>','>'}, {'>','>','>','>','<','>','>'}, {'>','>','>','>','<','>','>'}, {'<','<','<','<','<','=','>'}, {'>','>','>','>',' ','>','>'}, {'<','<','<','<','<',' ','='} }; int a_index = index_symbol(a); int b_index = index_symbol(b); return priority[a_index][b_index]; } public static int index_symbol(String a){ String p = "+-*/()#"; return p.indexOf(a); } //随机生成括号,参数为运算式的运算数的个数 public static int[] randomAddBracket(int length){ int[] brackets = new int[length]; for(int i = 0;i < brackets.length;i++) brackets[i] = 0; Random rd = new Random(); for(int i = 2;i < length;i++){//添加的括号长度(括号包围的运算数的个数) for(int j = 0;j < length - i + 1;j++){ int t = rd.nextInt(2);//随机生成0或1,0代表不加括号,1代表加括号 if(t == 1){ if(brackets[j] >= 0 && brackets[j + i - 1] <= 0){//要加的括号的第一个运算数周围没有右括号,且 最后一个运算数周围没有左括号 int counteract = 0; for(int k = j;k < j + i;k++){//将要加的括号之间的所有运算数对应的brackets相加, //如果和为0说明这个括号之间的括号是匹配的,不会出现括号交叉现象 counteract += brackets[k]; } if(counteract == 0){ brackets[j]++; brackets[j + i - 1]--; } } } } } return brackets; } //随机生成一个运算数( type==0代表生成整数,type==1代表生成真分数,maxNum代表数值范围 0-(maxNum-1) ) public static String getOperatorNumber(int type,int minNum,int maxNum){ Random rd = new Random(); int a,b; while(true){ a = rd.nextInt(maxNum-minNum+1)+minNum; if(type == 0){//随机生成一个整数 return "" + a; }else{//随机生成一个真分数 if(a == 0){ continue; } b = rd.nextInt(maxNum-minNum+1)+minNum; System.out.println(b); while(b>=a) { b = rd.nextInt(maxNum-minNum+1)+minNum; if(a>b) { break; } } System.out.println(b); fraction c = new fraction(a,b); return c.toString(); } } } public static void main(String[] args) { // TODO Auto-generated method stub Scanner sc = new Scanner(System.in); System.out.print("请输入所要生成题目的数目:"); int n = sc.nextInt(); System.out.print("请输入: 0、整数式 ; 1、分数式:"); int type = sc.nextInt(); System.out.print("是否有乘除法?(1有,0没有):"); int hasChengChu = sc.nextInt(); System.out.print("是否有括号?(1有,0没有):"); int hasKuoHao = sc.nextInt(); System.out.print("加减有无负数?(1有,0没有):"); int hasFuShu = sc.nextInt(); System.out.print("除法有无余数?(1有,0没有):"); int hasYuShu = sc.nextInt(); System.out.print("数值范围(最小数):"); int minNum = sc.nextInt(); System.out.print("数值范围(最大数):"); int maxNum = sc.nextInt(); String[] yunSuanShiArray = createYunSuanShi(hasChengChu, hasKuoHao, hasFuShu, hasYuShu,minNum, maxNum, n, type); for(int i = 0;i < yunSuanShiArray.length;i++){ System.out.print(yunSuanShiArray[i]); String result=sc.next(); if(result.equals(Answer[i])){ System.out.print(" 回答正确!"); } else{ System.out.print(" 回答错误!"+"正确答案为:"+Answer[i]); } System.out.println(); } sc.close(); } }
四、截图
五、反思:
下星期的编程任务应该是改为web版,这一星期的编程时间并不是特别长,所以下个星期应该更加努力去完善程序,争取早日完成这个程序!