四則運算程序(java基於控制臺)
一、題目描述:
1. 使用 -n 參數控制生成題目的個數,例如
Myapp.exe -n 10 -o Exercise.txt
將生成10個題目。
2. 使用 -r 參數控制題目中數值(自然數、真分數和真分數分母)的範圍,例如
Myapp.exe -r 10
將生成10以內(不包括10)的四則運算題目。該參數可以設置為1或其他自然數。該參數必須給定,否則程序報錯並給出幫助信息。
3. 生成的題目中如果存在形如e1 ÷ e2的子表達式,那麽其結果應是真分數。
4. 每道題目中出現的運算符個數不超過3個。
5. 程序一次運行生成的題目不能重復,即任何兩道題目不能通過有限次交換+和×左右的算術表達式變換為同一道題目。例如,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. 四則運算題目1
2. 四則運算題目2
……
其中真分數在輸入輸出時采用如下格式,真分數五分之三表示為3/5,真分數二又八分之三表示為2’3/8。 6. 在生成題目的同時,計算出所有題目的答案,並存入執行程序的當前目錄下的Answers.txt文件,格式如下: 1. 答案1 2. 答案2 特別的,真分數的運算如下例所示:1/6 + 1/8 = 7/24。 7. 程序應能支持一萬道題目的生成。 8. 程序支持對給定的題目文件和答案文件,判定答案中的對錯並進行數量統計,並會輸出所有題目中重復的題目,輸入參數如下: Myapp.exe -e <exercisefile>.txt -a <answerfile>.txt -o Grade.txt 統計結果輸出到文件Grade.txt,格式如下: Correct: 5 (1, 3, 5, 7, 9) Wrong: 5 (2, 4, 6, 8, 10) Repeat:2 RepeatDetail: (1) 2,45+32 Repeat 3,32+45 (2) 5,3+(2+1) Repeat 7,1+2+3
二、分析設計
1.生成隨機表達式
需要生成隨機數(整數,分數,帶分數)的函數,隨機運算符的函數,隨機添加括號函數,采用String拼接生成隨機表達式。
2.表達式處理計算
將中綴表達式轉換為後綴表達式,對後綴表達式進行分割處理,通過棧操作進行運算,由於存在分數和帶分數,需通過自定義四則運算法則進行計算,具體為同化成分數進行運算,完成後需約分。
3.表達式查重
先通過讀取答案文檔,掃描相同答案的表達式進行判斷,可提高效率,接著再將相同答案的中綴表達式轉換成後綴表達式,判斷所有元素是否相等。此方法效率較高,但存在局限性。由於本人並不是通過二叉樹處理表達式,再使用二叉樹顯得 很繁瑣,且效率不高。
4.輸出至文檔
需要輸出的文檔有表達式Exercises.txt,答案Answers.txt,成績及查重結果Grade.txt。
三、功能實現
1.主程序Main.java
主要代碼:
System.out.println("---------------四則運算程序---------------");
System.out.println("-n:生成題目個數");
System.out.println("-r:參數數值範圍");
System.out.println("-g:查看測試結果");
System.out.println("Do:執行程序");
System.out.println("請輸入指令:");
Scanner in =new Scanner(System.in);
while(in.hasNext()){
switch(in.next()){
case "-n" :
System.out.println("請輸入要生成的題目個數:");
n=in.nextInt();
break;
case "-r":
System.out.println("請輸入運算數的數值範圍:");
m=in.nextInt();
break;
case "-g":
fo.FileC(file2, file3, file4); //答案和做題文檔對比,結果寫入Grade文檔
break;
case "Do":
for(int i=0;i<n;i++){
String s=ex.CreatExp(n,m),fstr; //生成隨機表達式並求解
String rus=its.suffixToArithmetic(its.infixToSuffix(s));
fstr=i+1+":"+s+"\r\n";
fo.FileW(file1, fstr); //表達式寫入文檔
fstr=i+1+":"+rus+"\r\n";
fo.FileW(file2, fstr); //答案寫入文檔
}
break;
default:
System.out.println("無效指令!");
break;
}
System.out.println("請輸入指令:");
2.隨機表達式生成
主要代碼:
/*隨機生成表達式*/
public String CreatExp(int n ,int m){
String exp=CreatNum(m); //隨機操作數
Random rd=new Random();
int t=rd.nextInt(2);
boolean flag=false; //是否生成括號
if(t>0)
flag=Creatkh();
for(int i=0;i<=t;i++){ //生成String類型中綴表達式
if(flag==true){
if(i==0){
exp=exp+CreatChar()+"("+CreatNum(m);
}else
{
exp=exp+CreatChar()+CreatNum(m)+")";
}
}else{
exp=exp+CreatChar()+CreatNum(m);
}
}
return exp;
}
/*隨機生成操作數*/
public String CreatNum(int m){
String s="";
Random rd=new Random();
switch(rd.nextInt(2)){ //隨機類型:整數,分數
case 0:
s=Integer.toString(rd.nextInt(m-1)+1); //整數
break;
case 1: //分數
int a,b;
a=rd.nextInt(m-1)+1;
b=rd.nextInt(m-2)+2;
s=Dating(a,b); //分數約分處理
break;
}
return s;
}
/*隨機生成運算符*/
public String CreatChar(){
String s="";
Random rd=new Random();
switch(rd.nextInt(4)){
case 0:s="+";break;
case 1:s="-";break;
case 2:s="*";break;
case 3:s="÷";break;
}
return s;
}
/*分數進行約分*/
public String Dating(int a,int b){
String s="";
int gongyinshu=1,c;
c=a/b;
a=a%b;
if(c<0){ //若帶分數已為負數,這分數不用帶負號
a=a*-1;
}
for (int i = 1; i <= a; i++) { //求最小公約數
if (a % i == 0 && b % i == 0) {
gongyinshu = i;
}
}
a=a/gongyinshu; //生成最簡分數
b=b/gongyinshu;
if(a==0){
s=Integer.toString(c);
}else if(c==0){
s=Integer.toString(a)+"/"+Integer.toString(b);
}else{
s=Integer.toString(c)+"‘"+Integer.toString(a)+"/"+Integer.toString(b);
}
return s;
}
/*隨機是否生成括號*/
public boolean Creatkh(){
boolean flag=false;
Random rd=new Random();
if(rd.nextInt(3)<1) //生成擴號的概率為1/3
flag=true;
return flag;
}
3.表達式處理
主要代碼:
/*中綴表達式轉後綴表達式*/
public String infixToSuffix(String exp) {
Stack<Character> s = new Stack<Character>(); // 創建操作符堆棧
String suffix = ""; // 要輸出的後綴表達式字符串
int length = exp.length(); // 輸入的中綴表達式的長度
for (int i = 0; i < length; i++) {
char temp;
char ch = exp.charAt(i); // 獲取該中綴表達式的每一個字符並進行判斷
switch (ch) {
case ‘(‘:
s.push(ch);
break;
case ‘+‘: // 碰到‘+‘ ‘-‘,將棧中的所有運算符全部彈出去,直至碰到左括號為止,輸出到隊列中去
case ‘-‘:
suffix += " ";
while (s.size() != 0) {
temp = s.pop();
if (temp == ‘(‘) {
s.push(‘(‘);
break;
}
suffix += temp;
suffix += " ";
}
s.push(ch);
break;
case ‘*‘: // 如果是乘號或者除號,則彈出所有序列,直到碰到加好、減號、左括號為止,最後將該操作符壓入堆棧
case ‘÷‘:
suffix += " ";
while (s.size() != 0) {
temp = s.pop();
if (temp == ‘+‘ || temp == ‘-‘ || temp == ‘(‘) {
s.push(temp);
break;
} else {
suffix += temp;
suffix += " ";
}
}
s.push(ch);
break;
case ‘)‘:
while (!s.isEmpty()) {
temp = s.pop();
if (temp == ‘(‘) {
break;
} else {
suffix += " ";
suffix += temp;
}
}
break;
default:
suffix += ch;
break;
}
}
while (s.size() != 0) { // 如果堆棧不為空,則把剩余運算符一次彈出,送至輸出序列
suffix += " ";
suffix += s.pop();
}
return suffix;
}
/*計算後綴表達式*/
public String suffixToArithmetic(String exp) {
String[] strings = exp.split(" "); //按空格分解字符串
Stack<String> stack = new Stack<String>(); //操作數棧
for (int i = 0; i < strings.length; i++) {
if(strings[i].equals("+")||strings[i].equals("-")||strings[i].equals("*")||strings[i].equals("÷")){
String y=stack.pop(); //讀取到運算符,提取棧頂的兩個操作數,先出的操作數為運算符後的數
String x=stack.pop();
String rus=calculate(x, y, strings[i]); //調用自定義的四則運算法則
stack.push(rus);
if(rus.equals("無解")) //除數為0返回無解
return rus;
}else{
stack.push(strings[i]);
}
}
return stack.pop();
}
/*自定義四則運算法則*/
public String calculate(String x, String y, String ch) {
}
註:四則運算過程代碼較多,不展示。
4.文件操作及表達式查重代碼不展示
四、結果展示
命令選擇:
表達式文檔:
答案文檔:
答題文檔:
成績文檔:
一萬道題測試:
五、實驗小結
此次編程要點在於表達式的處理,重點是對分數,帶分數的處理,具體解決方法是將其每個部分的整數提取出來,存於幾個參數中,通過參數間的轉化運算達到分數的計算,從而實現表達式的計算。過程中遇到挺多小問題,例如除數為0,6÷(3-3) ,解決方法為計算除法時,進行判斷,如果除數為0直接返回結果“無解”。
六 、PSP表
PSP2.1 Personal Software Process Stages Time Senior Student Time
Planning 計劃 2 2
Estimate 估計這個任務需要多少時間 48 36
Development 開發 40 36
Analysis 需求分析 (包括學習新技術) 2 1
Design Spec 生成設計文檔 1 1
Design Review 設計復審 2 1
Coding Standard 代碼規範 0 0
Design 具體設計 3 4
Coding 具體編碼 27 22
Code Review 代碼復審 2 1
Test 測試(自我測試,修改代碼,提交修改 2 4
Reporting 報告 1 2
七、源代碼
碼雲項目地址:https://gitee.com/liangs96_master/FourOperations
四則運算程序(java基於控制臺)