2016011990小學四則運算練習軟件項目報告
小學四則運算練習軟件項目報告
項目克隆地址:https://git.coding.net/chenf640/workhome2_2.git
目錄:
一、需求分析
二、功能設計
三、設計實現
四、算法詳解
五、測試運行
六、代碼展示
七、psp
八、總結
——————————————————————————————————————————————————
一、需求分析
(一)功能需求
基本功能:
- 程序可接收一個輸入參數n,然後隨機產生n道加減乘除(分別使用符號+-*÷來表示)練習題。
- 每個數字在 0 和 100 之間,運算符在3個到5個之間;
- 每個練習題至少要包含2種運算符;
- 所出的練習題在運算過程中不得出現負數與非整數,比如3÷5+2=2.6,2-5+10=7等是不合法的;
- 練習題生成好後,將你的學號與生成的n道練習題及其對應的正確答案輸出到文件“result.txt”中,不要輸出額外信息,文件目錄與程序目錄一致;
- 當程序接收的參數為4時,以下為一個輸出文件示例。
2018010203
13+17-1=29
11*15-5=160
3+10+4-16=1
15÷5+3-2=4
擴展功能:
- 支持有括號的運算式,包括出題與求解正確答案。
- 擴展程序功能支持真分數的出題與運算(只需要涵蓋加減法即可),例如:1/6 + 1/8 + 2/3= 23/24。註意在實現本功能時,需支持運算時分數的自動化簡,比如 1/2+1/6=2/3,而非4/6,且計算過程中與結果都須為真分數。
(二)程序需求
1、只能使用Java語言。
2、生成文件時請使用相對路徑,生成的txt 文件需在項目的根目錄下,可直接查看演示示例。
3、使用的JDK版本為 jdk8u161,使用的JRE版本為jre8u161。
4、不得使用除限定版本jdk與jre外的額外依賴包。
二、功能設計
(一)基本功能
1、保證輸入的是數字或者特定字符,如果不符合要求,會有提示,並且可以重新輸入。
2、當輸入一個參數n,隨機產生n道加減乘除算術題。保證生成的每個練習題至少要包含2種運算符。且保證在算數過程中,不出現負數,和小數。
3、將生成的n道練習題及其對應的正確答案輸出到文件“result.txt”中,不要輸出額外信息,文件目錄與程序目錄一致;
4、為保證隨機產生的四則運算更滿足用戶的需求,當輸入參數n決定產生四則運算的個數時,另外輸入一個參數m,決定產生的n道四則運算包含運算符的個數(3、4、5)
(二)擴展功能
1、保證生成的分數運算,在運算過程中不產生假分數。並且實現自動化簡。
2、產生帶有括號的運算式時保證不產生沒有意義的括號。
三、設計實現
該程序我的設計思想是一個類實現一個功能,提高代碼的重用性,減少冗雜度。讓代碼更清晰。
(一)Main類
功能:是主函數,包含 public static void main(String[] args) 方法。並且包含判斷 輸入的參數是否為整數的 函數,如果不是函數,可提示用戶重新輸入。
目的:確保用戶輸入滿足要求的整數的參數(產生四則運算數量1-1000,每個四則運算包含運算符3-5)
(二)Algorithm類
功能:產生符合要求的一個四則運算,不產生括號。該類中包含一個 帶參的返回類型為字符串的函數。該函數參數為產生四則運算中包含的符號個數,可以產生包含任意多符號數量的四則運算,在該函數中,為滿足用戶需求,運算符數量可以為三、四、五,即該函數參數可以為3、4、5。
目的:產生滿足用戶需求的四則運算。(包含的參數個數、在運算過程中不能產生負數、小數)
關系:公有類,可直接調用。
(三)BracketsAlgo類
功能:產生符合要求的帶有括號的四則運算,該類中包含一個 帶參的返回類型為字符串的函數。該函數參數為產生四則運算中包含的符號個數,可以產生包含任意多符號數量的四則運算,在該函數中,為滿足用戶需求,運算符數量可以為三、四、五,即該函數參數可以為3、4、5。
目的:產生括號,增大運算難度。
(四)Fraction類
功能:生成分數運算,保證在運算過程中不產生假分數。並且實現自動化簡。該函數參數為產生四則運算中包含的符號個數,可以產生包含任意多符號數量的四則運算,在該函數中,為滿足用戶需求,運算符數量可以為三、四、五,即該函數參數可以為3、4、5。
目的:練習分數運算。
(五)CreatFile類
功能:生成規定數量的四則運算,並將運算式與結果輸出到result.txt文件中。該類中包含一個帶參無返回的函數,該函數參數為要打印四則運算的數量。
目的:將符合要求的四則運算輸出到txt文件中,方便保留與查看。
關系:公有類,可直接調用。
(六)Symbol類
功能:將產生的代表運算符的數字轉換為對應的運算符。(0代表“+”,1代表“-”,2代表“*”,3代表“÷”)
目的:輸出符合規範的運算符
(七)Test測試類
功能:測試方法。
四、算法詳解(具體代碼分析)
(一)重要算法
1.Algorithm類中的Algorithm算法
算法功能:產生,符合要求的四則運算(運算過程中不產生負數、小數,每個四則運算至少包括兩種運算符)
算法思想:
1.1.判斷產生的運算符不完全相等(至少包括兩種)。
- 用while循環,如果隨機產生的數字完全相等,則一直循環,直到不完全一致。
- 用數字0-3分別代表運算符的加減乘除,將隨機生成的數字進行比較,如果四個數字完全相等,則重新生成,最終將滿足要求的代表運算符的數字存到數組中。
1.2.運算符之間優先級比較。
- 將運算符從前到後兩兩比較,先計算優先級高的運算符,先乘除後加減,同級運算符從左向右依次計算。
- 通過運算符兩兩比較,每循環一次就將生成的結果代替原來的數字,循環結束後,最終的結果會放到數組的最後一位。在計算過程中判斷是否產生負數、是否產生小數,是否除數為0等,如果產生不滿足要求的運算式,則舍去,重新開始。
以一次循環為例:這是不完整的流程圖,只包含其中一部分情況。
1.3 .產生包含任意多運算符的四則運算。
- 只需改變循環的次數
1.4.將生成的正確的四則運算和結果放到一個字符串中,返回該字符串給 CreatFilel類中的寫入文件方法。
- 為了避免在比較優先級的循環中,改變存放數組與運算符的數組,因此,在進行循環判斷前,將運算數與運算符交錯放到數組中,如果通過驗證證明該四則運算符合要求,則返回給 CreatFilel類中的寫入文件方法,寫入result.txt文件。
- 在輸出時,用for進行判斷,如果是運算數子部分則直接輸出,如果是運算符部分,則將數字轉化為對應的運算符號進行輸出。
如圖所示:
2.CreatFile中的creatFile將生成結果寫入指定文件
- 將返回的字符串寫入指定路徑下的result.txt文件中,並在該文件第一行輸出學號。
- 該算法比較簡單,需要導入java.io這個jar包。
- 通過main方法調用,傳入兩個參數,生成算法的數量和每個算法包含的運算符數量。
3.Fraction類中的fractionArithmetic算法
算法功能:生成符合要求的分數運算式。
算法實現:
- 隨機產生隨機數字(0、1)來代表運算符(+、-)
- 隨機產生兩個數字,一個做分母、一個做分子(不能為0)。在運算過程中,不斷判斷產生的分數是否大於等於一、並不斷將分數進行化簡。
(二)其他算法
1.Symbol方法中的symbol算法
算法思想:輸出運算符時,將數字與字符一一對應。用switch()語句即可完成。
2.驗證輸入的為符合要求的整數(包括生成四則運算的數量、生成的四則運算中包含運算符的數量)
算法思想:將輸入的內容進行驗證,先驗證是否為整數,如果是整數,再驗證該整數是否符合要求。若不符合要求,則對用戶進行提示,要求重新輸入。
在命令行結果如下:
3.BracketsAlgo類中的bracketsAlgo算法
算法功能:產生帶有括號的符合要求的四則運算
算法實現:
- 將隨機生成的運算符從左向右兩兩比較。如果前面的優先級高,則進行運算,保存結果;如果前面運算符的優先級低,則加上括號。
- 該算法一定是從左向右依次運算。
缺點:該算法可以生成帶有括號的四則運算,但產生括號的數量不能確定。
五、測試運行
為了更好地實現用戶的要求,在運行代碼時需要輸入你要打印的題目的具體數量、包含運算符的具體數量、以及是否有括號、是否是分數運算等參數。
1.進入src文件夾;
2.在命令行輸入javac -encoding utf-8 Main.java;
3.回車再輸入java Main 20;
4回車.輸入你要打印的題目數量;
5回車.輸入你要打印的題目所帶符號的數量;
6.回車.輸入你要打印的題目的類型(y-分數運算;n- 整數運算);
7.如果輸入的是y,回車.result.txt文件生成,分數運算式寫入result.txt文件(如圖一,結果對應圖三)。
如果輸入的是n,回車選擇產生的算式是否包括括號(y--包括;n--不包括,回車.result.txt文件生成,對應的運算式寫入result.txt文件(如圖二,結果對應圖四)。
圖一 圖二
運算結果:
圖三 圖四
六、代碼展示:
Algorithm類中的Algorithm算法:
/* 將運算符從前到後兩兩比較,先計算優先級高的運算符,先乘除後加減,同級運算符從左向右依次計算 在計算過程中判斷是否產生負數、是否產生小數,是否除數為0等,如果產生不滿足要求的運算式, 則舍去,如果符合要求,則打印出來。 */ for (int i = 0; i < m.length; i++) { if ((m1[i] < m1[i + 1]) && (m1[i + 1] == 2)) { /* 包含情況為 0<2;1<2 計算優先級高的(乘法) 區域運算,並將結果放到 存放數字的數組中,運算過的運算符也被取代*/ num[i + 2] = num[i + 1] * num[i + 2]; num[i + 1] = num[i]; m1[i + 1] = m1[i]; number++; } else if ((m1[i] < m1[i + 1]) && m1[i + 1] == 3 && m1[i] != 2) { /* 包含情況為 0<3;1<3計算優先級高的(除法) 區域運算,並將結果放到 存放數字的數組中,運算過的運算符也被取代*/ if (num[i + 2] != 0) { //首先除數不能為0 int a = num[i + 1] % num[i + 2]; //是否整除 if (a == 0) { //如果整除,進行除法運算 num[i + 2] = num[i + 1] / num[i + 2]; num[i + 1] = num[i]; m1[i + 1] = m1[i]; number++; } else { //如果不能整除,重新進行四則運算的產生 number = 0; for (int k = 0; k < m.length; k++) { m[k] = 0; } break; } } else { //除數為0,重新進行四則運算的產生 number = 0; for (int k = 0; k < m.length; k++) { m[k] = 0; } break; } } else if (m1[i] == 2 && m1[i + 1] == 3) { /* 包含情況為 2<3計算從左向右進行,並將結果放到 存放數字的數組中*/ m[i + 1] = m[i] * m[i + 1]; number++; } else if ((m1[i] < m1[i + 1]) && (m1[i + 1] == 1)) { /* 包含情況為 0<1 計算從左向右進行,並將結果放到 存放數字的數組中*/ m[i + 1] = m[i] + m[i + 1]; number++; } else if ((m1[i] > m1[i + 1]) && m1[i] == 3) { /* 包含情況為 3>2;3>1;3>0 計算從左向右進行,並將結果放到 存放數字的數組中*/ if (num[i + 1] != 0) { //被除數不能為0 int a = num[i] % num[i + 1]; if (a == 0) { //能夠整除 num[i + 1] = num[i] / num[i + 1]; number++; } else { number = 0; for (int k = 0; k < m.length; k++) { m[k] = 0; } break; } } else { //被除數為0 number = 0; for (int k = 0; k < m.length; k++) { m[k] = 0; } break; } } else if ((m1[i] > m1[i + 1]) && m1[i] == 2) { /* 包含情況為 2>1;1>0 計算從左向右進行,並將結果放到 存放數字的數組中*/ num[i + 1] = num[i] * num[i + 1]; number++; } else if ((m1[i] > m1[i + 1]) && m1[i] == 1) { /* 包含情況為 1>0 計算從左向右進行,並將結果放到 存放數字的數組中*/ if (num[i] > num[i + 1]) { //是否產生負數 num[i + 1] = num[i] - num[i + 1]; number++; } else { number = 0; for (int k = 0; k < m.length; k++) { m[k] = 0; } break; } } else { /* 包括情況為0==0;1==1;2==2 3==3*/ if (m1[i] == m1[i + 1]) { if (m1[i] == 0) { //加法 num[i + 1] = num[i] + num[i + 1]; number++; } else if (m1[i] == 1) { //減法,判斷是否產生負數 if (num[i] > num[i + 1]) { num[i + 1] = num[i] - num[i + 1]; } else { number = 0; for (int k = 0; k < m.length; k++) { m[k] = 0; } break; } } else if (m1[i] == 2) { //乘法 num[i + 1] = num[i] * num[i + 1]; number++; } else { //除法運算,判斷除數不能為0,不能產生小數 if (num[i + 1] != 0) { int a = num[i] % num[i + 1]; if (a == 0) { num[i + 1] = num[i] / num[i + 1]; number++; } else { for (int k = 0; k < m.length; k++) { m[k] = 0; } number = 0; break; } } else { for (int k = 0; k < m.length; k++) { m[k] = 0; } number = 0; break; } } } } }
2.判斷你要打印的題目數量的個數是否為整數
以打印題目數量為例:
/** * 判斷 輸入你要打印的題目數量的個數是否為整數 * @return */ public static int numberTest() { Scanner input = new Scanner(System.in); int number =0; while(number<1||number>1000){ System.out.print("請輸入你要打印的題目數量 (1-1000):"); String s = input.next(); while (!(s != null && s.matches("^[0.0-9.0]+$"))) {// [0-9]沒辦法識別小數,[0.0-9.0]可以識別小數和整數 System.out.print("請輸入正確的題目數量,類型為整數 (1-1000):"); s = input.next(); } number = Integer.parseInt((s)); } return number; }
七、psp:
本次編碼並沒有嚴格計算時間,剛開始也沒有預測所有用到的時間,下表所填的時間是估計的,可能不是很準確,下次再做作業時會更加準確的記錄時間。
PSP2.1 |
任務內容 |
計劃共完成需要的時間(min) |
實際完成需要的時間(min) |
Planning |
計劃 |
20 |
|
· Estimate |
· 估計這個任務需要多少時間,並規劃大致工作步驟 |
20 |
|
Development |
開發 |
15*60 |
27*60 |
· Analysis |
· 需求分析 (包括學習新技術) |
60 |
|
· Coding Standard |
· 代碼規範 (為目前的開發制定合適的規範) |
30 |
|
· Design |
· 具體設計 |
60 |
|
· Coding |
· 具體編碼 |
14.5*60 |
|
· Code Review |
· 代碼復審 |
60 |
|
· Test |
· 測試(自我測試,修改代碼,提交修改) |
9*60 |
|
Reporting |
報告 |
6.5*60 |
|
· Size Measurement |
· 計算工作量 |
10 |
|
· Postmortem & Process Improvement Plan |
· 事後總結, 並提出過程改進計劃 |
20 |
|
寫博客 |
6*60 |
八、總結:
(一)實現軟件的‘模塊化‘原則:
- 對功能進行分析,並分解成若幹子任務,每個函數只實現一種功能,並將一類功能放到一個類中。通過函數之間的調用來實現所有功能。
- 避免代碼冗雜,提高代碼的重用性跟規範性。
(二)缺點:
雖然實現了作業的基本要求和附加要求,但關於附加功能的第一條完成得不是很好,雖然不會產生無意義的括號,如 (3*4)*5,但是因為符號的產生是隨機的 ,所以很難保證括號的數量。而且,自己的代碼還是過於冗雜,還有待提高。
(三)優點:
- 除了附加一的一些問題,本作業的其他要求都以實現。
- 代碼書寫規範
- 有詳細地註釋
- 雖是一個基本功能,但是我用了三種方法去寫(從剛開始最復雜的多重循環(Arithmetic3類中的Arithmetic_test()方法到邏輯更加嚴謹的算法(Algorithm類中的algorithm()方法再到最後更加簡潔的(BracketAlgo類中的bracketsAlgo()算法)。雖然看上去浪費了很多時間,但是這也讓我認識到了算法的強大,逐漸進步。
(四)個人感受
首先,先談談我寫這個作業的過程。
對於這次作業,剛開始我是低估了難度的。我認為只要兩天左右的課余時間就可以完成。結果,這次實際花的時間遠遠超過我預想的。並且,由於剛開始我的不恰當的想法,導致我前面浪費了許多時間。後面又用了一種更加簡便的方法去做,成功的實現了基本功能中的所有要求。於是我開始去做附加要求,對於附加要求,剛開始並沒有什麽想法,於是,我上網查了些資料,看了同學們的一些博客,漸漸的發現附加功能並沒有自己想的那麽難。可能剛開始寫的時候思路比較順暢,這次附加功能並沒有占用我很長時間(敲代碼時間的三分之一左右)。最後,就是,寫博客啦。寫博客對我而言還是挺費時間,大概花了六個小時。終於,個人項目完成啦!
這次項目,於我而言還挺能難的,也挺費時間的,主要還是自己算法不是很好,不能找到最優解,導致自己浪費了一些時間。通過這次項目,我收獲最大的就是互相學習、交流的重要性。如果沒有看同學們、看網上的博客,沒有跟同學們討論,我可能還會限制在自己的思路中。還有就是在敲代碼前一定要有明確的思路,要不然敲了半天可能會是無用功。這做這個項目過程中,debug也花費了很多時間。
雖然這周很忙,最後幾天忙到晚上兩點多,電腦沒電了才睡覺。但是我很喜歡這種專註於一件事的感覺,以前沒事的時候會看看劇,聊聊天,但是最近這幾天,有時間就是敲代碼,有時走在路上也會想怎麽更好的實現功能,之間的邏輯是什麽。在項目最終完成時,很激動也很有滿足感。我想,這就是軟件工程的魅力吧。
最後,依舊一句話結尾。也依舊是 越努力、越幸運!
2016011990小學四則運算練習軟件項目報告