本次作業從布置的第一天我就着手做了,不過基礎較差,並沒有想過怎樣實現相關功能,反正都是一步一步做的。我不知道我花了多少時間在這次作業上,但是在空閑的時候我就在琢磨。(今天大概從下午3點開始沒課,我坐到在電腦前到8點半,先是修改代碼,然后寫完博客的大致內容,沒去跑步,飯也忘了吃,食堂也關了門,第一次感受到了完成作業的喜悅,也感受到了程序員的艱苦╮(╯▽╰)╭真的是到了廢寢忘食的地步)然后回到宿舍接着修改博客修改到宿舍斷電。
博客版式也是參考了同學的,感覺他們的排版與思路都很清晰,我也覺得可以借鑒借鑒o(* ̄︶ ̄*)o
項目簡介:
Coding.net源碼倉庫地址:
https://git.coding.net/francesca/Arithmetics.git
SSH:git@git.coding.net:francesca/Arithmetics.git
測試步驟:
1.進入src文件夾
2.在命令行輸入javac -encoding utf-8 *.java
3.回車再輸入java Main 1000
4.回車,將會在src同級目錄下產生result.txt
一、需求分析
1.程序可從命令行接收一個輸入參數n,然后隨機產生n道加減乘除習題。
2.每個數字在 0 和 100 之間,運算符3到5個。
3.每道習題要包含3-5種運算符。
4.所出的練習題在運算過程中不得出現負數與非整數。
5.將學號與生成的n道練習題及其對應的正確答案輸出到文件“result.txt”中。
6.支持有括號的運算式,包括出題與求解正確答案。算式中存在的括號必須大於2個,且不得超過運算符的個數。
二、功能設計
1.基本功能:
能夠根據用戶輸入的參數n隨機產生n道符合要求的練習題,自動算出答案,並將式子與答案以文檔的形式呈現。
2.擴展功能:
支持有括號的運算式,包括出題與求解正確答案。
支持真分數運算,包括出題與解題正確答案。
(注意,算式中存在的括號必須大於2個,且不得超過運算符的個數。)
三、設計實現
BinaryTree:生成二叉樹,獲取最終表達式,計算並驗證表達式
File:負責產生result.txt文件,並將學號和產生的習題寫入文件
Main:主類,負責接收命令行的參數並啟動程序
Ran:獲取隨機運算符及隨機數
MakeF:生成真分數並計算結果
TreeNode:生成節點,並獲取每個節點的運算結果,並檢驗除法和減法每個運算式添加括號,然后根據去括號法則,去掉多余的子式的括號
四、代碼展示
1.不出現負數與非整數
public String getResult(){ if(hasChild()){ switch(str){ case "+": return String.valueOf(Integer.parseInt(getLchild().getResult()) + Integer.parseInt(getRchild().getResult())); case "-"://change if(Integer.parseInt(getLchild().getResult()) - Integer.parseInt(getRchild().getResult()) < 0){ while(str.equals("-")){ str = String.valueOf(Ran.getOperator()); } return this.getResult(); } else return String.valueOf(Integer.parseInt(getLchild().getResult()) - Integer.parseInt(getRchild().getResult())); case "*": return String.valueOf(Integer.parseInt(getLchild().getResult()) * Integer.parseInt(getRchild().getResult())); case "÷": if(getRchild().getResult().equals("0")){ while(str.equals("÷")){ str = String.valueOf(Ran.getOperator()); } return this.getResult(); } /** * 整除運算 */ else if(Integer.parseInt(getLchild().getResult()) % Integer.parseInt(getRchild().getResult()) != 0){ while(str.equals("÷")){ str = String.valueOf(Ran.getOperator()); } return this.getResult(); } else return String.valueOf(Integer.parseInt(getLchild().getResult()) / Integer.parseInt(getRchild().getResult())); } } return str; }
2.帶括號的表達式
public String toString(){ String Lstr = "", Rstr = "", Str = ""; if(hasChild()){ //右子樹如果有孩子,說明右子樹是一個表達式,而不是數字節點。 if(getRchild().hasChild()){ //判斷左鄰括號的運算符是否為'/' if(str.equals("÷")){ //獲取右子樹的表達式,保留括號 Rstr = getRchild().toString(); } //判斷左鄰括號的運算符是否為'*'或'-' else if(str.equals("*") || str.equals("-")){ //判斷op是否為'+'或'-' if(getRchild().str.equals("+") || getRchild().str.equals("-")){ Rstr = getRchild().toString(); } else{ //獲取右子樹的表達式,並且去括號 Rstr = getRchild().toString().substring(1, getRchild().toString().length()-1); } } else{ //右子樹除此之外都是可以去括號的。 Rstr = getRchild().toString().substring(1, getRchild().toString().length()-1); } } else{ Rstr = getRchild().str; } //左子樹的情況同右子樹類似 if(getLchild().hasChild()){ if(str.equals("*") || str.equals("÷")){ if(getLchild().str.equals("+") || getLchild().str.equals("-")){ Lstr = getLchild().toString(); } else{ Lstr = getLchild().toString().substring(1, getLchild().toString().length()-1); } } else{ Lstr = getLchild().toString().substring(1, getLchild().toString().length()-1); } } else{ Lstr = getLchild().str; } //獲取當前的運算式,並加上括號 Str = "(" + Lstr + str + Rstr + ")"; } else{ //若沒有孩子,說明是數字節點,直接返回數字 Str = str; } return Str; }
3.真分數計算
public String MakeFraction() { String str = null; String[] op2 = { "+", "-" };// 存儲連接分數的操作符的數組 int denominator = 1; int numerator = 1; int denominator1 = (int) (Math.random() * 19) + 1;// 生成分母 int numerator1 = (int) (Math.random() * 20);// 生成分子 if (numerator1 != 0) { if (numerator1 > denominator1) {// 如果分子大於分母,也就是不是真分數時,交換分子分母,使其變成真分數 int temp = numerator1; numerator1 = denominator1; denominator1 = temp; } if (numerator1 == denominator1) {// 如果分子剛好等於分母,重新生成分子 numerator1 = (int) (Math.random() * 20); } int gcd1 = gcd(numerator1, denominator1);// 求分子分母最大公因數,保證分數形式最簡 denominator1 = denominator1 / gcd1;// 化簡 numerator1 = numerator1 / gcd1;// 化簡 } String question1 = numerator1 + "/" + denominator1;// 存儲題目 int count = (int) (Math.random() * 2) + 3;// 隨機產生運算符的數目 for (int u = 0; u < count; u++) {// 小於運算符數量時不斷產生分數,不斷計算 int denominator2 = (int) (Math.random() * 19) + 1;// 生成分母 int numerator2 = (int) (Math.random() * 20);// 生成分子 if (numerator2 != 0) { if (numerator2 > denominator2) {// 避免不是真分數 int temp = numerator2; numerator2 = denominator2; denominator2 = temp; } if (numerator2 == denominator2) {// 如果分子等於分母,重新生成分子 numerator2 = (int) (Math.random() * 20); } int gcd2 = gcd(numerator2, denominator2);// 化簡分式,使其最簡 denominator2 = denominator2 / gcd2; numerator2 = numerator2 / gcd2; } int symbol = (int) (Math.random() * 2);// 隨機產生運算符 if (op2[symbol].equals("+")) {// 如果是加號,實現分數加法 if (denominator1 == denominator2) {// 如果兩個分母相同,直接將分子相加 numerator = numerator1 + numerator2; } else {// 通分,相加 denominator = denominator1 * denominator2; numerator = numerator1 * denominator2 + numerator2 * denominator1; } if (denominator < numerator) {// 如果運算結果不是真分數 u--;// 計數的u減一,也就是重新生成重新計算 } else {// 在給定范圍內的話,通分運算結果 int gcd = gcd(numerator, denominator); denominator = denominator / gcd; numerator = numerator / gcd; question1 += op2[symbol] + numerator2 + "/" + denominator2;// 把題目進行完善 denominator1 = denominator;// 儲存運算結果到denominator1和numerator1 numerator1 = numerator; } } else {// 如果是減號,實現減法操作 if (denominator1 == denominator2) {// 分母相同直接分子相減 numerator = numerator1 - numerator2; } else {// 其他情況,先通分再相減 denominator = denominator1 * denominator2; numerator = numerator1 * denominator2 - numerator2 * denominator1; } if (numerator < 0) {// 如果導致結果小於0了,就重新生成 u--; } else {// 通分結果化簡 int gcd = gcd(numerator, denominator); denominator = denominator / gcd; numerator = numerator / gcd; question1 += op2[symbol] + numerator2 + "/" + denominator2; denominator1 = denominator;// 儲存通分結果 numerator1 = numerator; } } } str = question1 + " = " + numerator + "/" + denominator; return str; //System.out.println(question1 + " = " + numerator + "/" + denominator);// 輸出題目和答案 }
4、最大公因數
public int gcd(int x, int y) {// 求最大公因數的函數 if (x == 0) { return y; } else { return gcd(y % x, x); } }
五、測試運行

六、PSP
SP2.1 |
任務內容 |
計划共完成需要的時間(m) |
實際完成需要的時間(m) |
Planning |
計划 |
1500 |
2000 |
· Estimate |
· 估計這個任務需要多少時間,並規划大致工作步驟 |
1000 |
1400 |
Development |
開發 |
1902 |
3323 |
· Analysis |
· 需求分析 (包括學習新技術) |
90 |
123 |
· Design Spec |
· 生成設計文檔 |
0 |
0 |
· Design Review |
· 設計復審 (和同事審核設計文檔) |
0 |
0 |
· Coding Standard |
· 代碼規范 (為目前的開發制定合適的規范) |
0 |
0 |
· Design |
· 具體設計 |
300 |
480 |
· Coding |
· 具體編碼 |
1200 |
2300 |
· Code Review |
· 代碼復審 |
180 |
300 |
· Test |
· 測試(自我測試,修改代碼,提交修改) |
132 |
120 |
Reporting |
報告 |
360 |
380 |
· Test Report |
· 測試報告 |
300 |
320 |
· Size Measurement |
· 計算工作量 |
30 |
30 |
· Postmortem & Process Improvement Plan |
· 事后總結, 並提出過程改進計划 |
30 |
30 |
七、總結
1、最開始的隨機數和隨機符的產生也是琢磨了一小會兒,不過還好,通過百度加深了對java的理解,也更深一步了解了Random類和Math類下的random方法的區別。
2、是對非整數結果的構思,一開始采用的是比較除數和被除數的大小,然后交換數據,這個更改了好多遍,后來發現可以再次隨機產生除了除法以外的運算符,這個花費了好長時間,不過在這個基礎下,我接着沒幾分鍾就完成了非負數的完成。
3、是附加功能中的括號查了很多資料,發現要用中綴轉后綴表達式,用堆棧,這個過程比較容易懂,但是代碼還是挺難明白的,在請教學長得前提下,終於解決了,收獲也很大。
4、關於真分數的部分是在參考同學的代碼中學習到的,也寫了另外的類去測試,完成的比較好。
5、項目大部分功能是在本周三完成的,當時很興奮。不過后來才去看了題目要求,發現要在命令行測試數據,並寫入text文件,看了java教程后發現是File的相關內容,代碼完成的比較好,就是命令行是第一次用,從周四晚上開始就在琢磨為什么我電腦的命令行輸入會出現亂碼,查看了很多博客,沒找到一個成功的,到了周五下午,在學長得幫助下才發現之前的環境變量配置錯了。
6、接着是傳項目,我自己電腦配置好了git和相關的一些公鑰,傳項目也很熟練,不過換了台式電腦后,周五下午又花了一個小時弄公鑰和傳項目,成功傳項目有很多方式,最后發現自己寫的通過git傳到GitHub的博客中的方式最簡單。
總之,通過這次作業,我深刻意識到對java的掌握還不夠,相關算法也是查閱很多博客和書籍才實現的。每晚睡覺都還想着怎么實現非負數和非整數,說真的作業很難,不過收獲很大,這兩周很充實也很疲憊。總之努力就會有收獲,很慶幸自己沒有放棄,我也會繼續努力的ヾ(◍°∇°◍)ノ゙。
最后非常感謝您的閱讀,您辛苦啦!