2016011986+小學四則運算練習軟件項目報告
我的項目源碼地址:https://coding.net/u/Dearpangpang/SiZeYunSuan.git/git.html
項目需求:
軟件基本功能要求如下:
- 程序可接收一個輸入參數n,然後隨機產生n道加減乘除(分別使用符號+-*÷來表示)練習題,每個數字在0和100之間,運算符在3個到5個之間。 為了讓小學生得到充分鍛煉,每個練習題至少要包含2種運算符。同時,由於小徐生沒有分數和負數的概念,你所出的練習題在運算過程中不得出現負數與非整數,比如不能出(3÷5+2=2.6,2-5+10=7)等算式。
- 練習題生成好後,將你的學號與生成的n道練習題及對應的正確答案輸出到文件“result.txt”中,不要輸出額外信息,文件目錄與程序目錄一致。
- 當程序接收的參數為4時,以下為 我的輸出文件
功能設計:
程序能接收一個輸入參數n,然後根據題目要求隨機產生n道四則運算以及其對應的正確答案並輸入到文件“result.txt”中。
代碼分析:
首先,我先創建了CreatProblems()方法,用來出題,如下圖:
public static String CreatProblems() { Random random = new Random(); String s =new String(); int CaozuoCount = random.nextInt(3)+3;//保證操作符在3~5個 int CaozuoIndex[]=index(CaozuoCount,4); int Number[] =new int[CaozuoCount+1]; String[] FuHao = {"+","-","*","÷"}; for(int i=0;i<CaozuoCount+1;i++) { Number[i]=random.nextInt(100); } switch(CaozuoCount) {case 3: s= Number[0]+FuHao[CaozuoIndex[0]]+Number[1]+FuHao[CaozuoIndex[1]]+Number[2]+FuHao[CaozuoIndex[2]]+Number[3]; break; case 4: s= Number[0]+FuHao[CaozuoIndex[0]]+Number[1]+FuHao[CaozuoIndex[1]]+Number[2]+FuHao[CaozuoIndex[2]]+Number[3]+FuHao[CaozuoIndex[3]]+Number[4]; break; case 5: s= Number[0]+FuHao[CaozuoIndex[0]]+Number[1]+FuHao[CaozuoIndex[1]]+Number[2]+FuHao[CaozuoIndex[2]]+Number[3]+FuHao[CaozuoIndex[3]]+Number[4]+FuHao[CaozuoIndex[4]]+Number[5]; break; } return s; }
可能是我的方法有些問題,我並不能很好地解決每一部分的整數都可以除盡,我能想到的方法就是在我的符號前面加判斷,如果是除號,那麽除號的前一個數字和後一個數字需要能夠整除,如果不能做到,那麽就重新調用該方法重新得到一個問題,但是一些小細節始終處理的不好,我會繼續修正,請教同學,看看其他同學是怎樣實現該部分功能的。
除此之外,我還創建了一個index()方法,用於避免一道題中所有的符號一樣,也就是至少有兩種運算符。
public static int[] index(int n,int m){ //產生操作符的下標數組 Random random = new Random(); int b=0; int[] a = new int[n]; for(int j=0;j<n;j++){ a[j] = random.nextInt(m); } for(int j=1;j<n;j++){ if(a[0]==a[j]) b++; } if(b==n-1) return index(n,m); //保證一個式子裏至少有2個不同的操作符,若所有操作符下標都一樣,則重新產生操作符下標 else { return a; } }
然後我才用的是棧的方法實現字符串求值,該方法原理如下:
創建兩個棧,一個棧用來存放操作符,叫做opter,另一個棧存放操作數,叫做opval;讓字符串一個字符一個字符讀取,逐一進棧,先將“#”字符存入opter棧中,再使得題目字符串結尾處也加上字符“#”。
賦值字符c為當前字符串將要讀取的字符,top為opter棧頂元素。
若top的優先級小於c,即top<c,則彈出opter的棧頂元素,並讀入下一字符賦值給c
若top的優先級等於c,即top=c,則彈出opter的棧頂元素,並讀入下一字符c,該操作便於進行括號操作,便於後期完善
若top的優先級高於c,即top>c,則表明可以計算,此時彈出opval的兩棧頂元素,並且彈出opter棧頂的運算符,且放入opval棧中,直至opter的棧頂元素和當前讀入的字符均為“#”。
如下,是我的代碼實現:
首先創建棧還有getIndex()方法,使得對應的符號有對應的索引值
如圖所示:
static Stack<Character> opter = new Stack<Character>(); //運算符棧 static Stack<Integer> opval = new Stack<Integer>();//操作數棧
public static int getIndex(char theta)//獲取theta所對應的索引 { int index = 0; switch(theta) { case ‘+‘: index=0; break; case ‘-‘: index=1; break; case ‘*‘: index=2; break; case ‘÷‘: index=3; break; case ‘(‘: index=4; break; case ‘)‘: index=5; break; case ‘#‘: index=6; break; default:break; } return index; }
創建getPriority()方法,獲取到兩符號之間的優先級,如果top的優先級高於c,則返回‘>’,如果top優先級小於c,則返回‘<’。
public static char getPriority(char theta1,char theta2)//獲取theta1和theta2之間的優先級 { char priority[][]= { {‘>‘,‘>‘,‘<‘,‘<‘,‘<‘,‘>‘,‘>‘}, {‘>‘,‘>‘,‘<‘,‘<‘,‘<‘,‘>‘,‘>‘}, {‘>‘,‘>‘,‘>‘,‘>‘,‘<‘,‘>‘,‘>‘}, {‘>‘,‘>‘,‘>‘,‘>‘,‘<‘,‘>‘,‘>‘}, {‘<‘,‘<‘,‘<‘,‘<‘,‘<‘,‘=‘,‘0‘}, {‘>‘,‘>‘,‘>‘,‘>‘,‘0‘,‘>‘,‘>‘}, {‘<‘,‘<‘,‘<‘,‘<‘,‘<‘,‘0‘,‘=‘}, }; int index1 =getIndex(theta1); int index2 =getIndex(theta2); return priority[index1][index2]; }
如圖所示:
然後創建基本的計算功能,這一段代碼比較簡單
如圖所示:
public static int calculate(int b,char theta,int a)//計算 { int result=0; switch(theta) { case ‘+‘: result= a+b; break; case ‘-‘: result=b-a; break; case ‘*‘: result=b*a; break; case ‘÷‘: if(b%a==0) { result=b/a; } else theta=‘+‘; break; default:break; } return result; }
創建getAnsewer()方法,就是算法的實現部分,表達式求值
代碼如下:
public static int getAnsewer(String problem)//表達式求值 { opter.push(‘#‘);//首先將#號入棧opter int counter =0;//添加變量counter便是有多少個數字相繼入棧,實現多位數的四則運算 char c=0; problem+="#"; while(c!=‘#‘||opter.peek()!=‘#‘)//終止條件 { for(int i=0;i<problem.length();i++){ c=problem.charAt(i); //System.out.println("e333"); if(Character.isDigit(c))//如果c在0~9之間 { if(counter==1)//counter==1表示上一字符也是數字,所以要合並 { int t =opval.peek(); opval.pop(); opval.push(t*10+(c-‘0‘)); counter=1; } else { opval.push(c-‘0‘); counter++; } } else { counter=0;//counter至零 switch(getPriority(opter.peek(),c)){//獲取運算符棧opter棧頂元素與c之間的優先級 case ‘<‘://則將c入棧opter opter.push(c); break; case ‘=‘://將opter棧頂元素彈出,用於括號的處理 opter.pop(); break; case ‘>‘://可以進行計算 char theta =opter.peek(); opter.pop(); int a =opval.peek(); opval.pop(); int b =opval.peek(); opval.pop(); opval.push(calculate(b,theta,a)); } } } } return opval.peek(); }
然後,基於不可以有負數這一要求,我創建了FinalltResult(),如果答案為負數的話,則返回CreateProblems()方法重新生成題目,重新求解。
最後創建文件代碼,如下圖所示:
public static void creatFile(int n) { try{ File file=new File("../result.txt"); if(file.exists()) { file.delete(); } if(file.createNewFile()) { FileOutputStream txtfile = new FileOutputStream(file); PrintStream p =new PrintStream(txtfile); p.println("2016011986"); Random random =new Random(); for(int i=0;i<n;i++) { p.println(CreatProblems()+"="+FinalltResult(CreatProblems())); } txtfile.close(); p.close(); System.out.println("文件創建成功!"); } }catch(IOException ioe) { ioe.printStackTrace(); } }
測試運行:
PSP:
PSP2.1 |
任務內容 |
計劃共完成需要的時間(h) |
實際完成需要的時間(h) |
Planning |
計劃 |
1 |
1 |
· Estimate |
· 估計這個任務需要多少時間,並規劃大致工作步驟 |
45 |
60 |
Development |
開發 |
45 |
60 |
· Analysis |
· 需求分析 (包括學習新技術) |
2 |
2 |
· Design Spec |
· 生成設計文檔 |
0 |
0 |
· Design Review |
· 設計復審 (和同事審核設計文檔) |
0 |
0 |
· Coding Standard |
· 代碼規範 (為目前的開發制定合適的規範) |
8 |
10 |
· Design |
· 具體設計 |
1 |
2 |
· Coding |
· 具體編碼 |
30 |
38 |
· Code Review |
· 代碼復審 |
1 |
2 |
· Test |
· 測試(自我測試,修改代碼,提交修改) |
1 |
3 |
Reporting |
報告 |
0 |
0 |
· Test Report |
· 測試報告 |
0 |
0 |
· Size Measurement |
· 計算工作量 |
0 |
0 |
· Postmortem & Process Improvement Plan |
· 事後總結, 並提出過程改進計劃 |
3 |
3 |
我的感想:
剛看到這個作業的時候,我還以為就是上次小程序的完整版,而實際上,上次的只是很基本的操作,這一次的作業就有很多邏輯上的問題,比如我遇到的很多問題,像是如何生成很多道符號種類不同的算術題,如何將一串字符串進行數值運算,如何判斷能否整除是否為負,還有怎樣加括號還有怎樣實現分數,分數運算還有整式運算怎樣隨機產生,如何將一串字符串進行數值運算這個是整道題中最難也是最有意思的部分,正好運用到我們上個學期學習的數據結構,我當時上數據結構的時候還覺得那些算法太復雜感覺變成代碼根本做不到,但是通過百度各種博客發現,其實這種看上去邏輯很強的算法,其實都是一步一步慢慢得到的,通過不同的調用方法一點一點實現,這期間,我也出了很多錯,也請教了同學很多,也學到了一些簡單的找錯的方法,一點一點執行代碼一步一步找錯,總之學到了很多,還有之前一直不是很懂的文件輸出,這一次也鍛煉到了,雖然我打的很慢慢,也遇到了很多問題,但是確實學到了很多很多,也體會到了實現代碼興奮激動的感覺,雖然熬了好幾天的夜,但是真的完成這一任務時,真的是很開心的!
2016011986+小學四則運算練習軟件項目報告