1. 程式人生 > >2016012034+小學四則運算練習軟件項目報告

2016012034+小學四則運算練習軟件項目報告

進入 大於 sco mce 計劃 命令 最大 nag sca

代碼倉庫地址:https://coding.net/u/Siamese_miao/p/fourArithmetic/git?public=true

測試效果見src下生成的result.txt文件


一、 需求分析

1. 使用JAVA編程語言。

2. 接收一個輸入參數n,隨機產生n道四則運算練習題,符號用+-*÷來表示。

3. 每個數字在 0 和 100 之間。

4. 運算符在3個到5個之間並且每道練習題至少要包含2種運算符。

5. 運算過程中不得出現負數與非整數。

6. 將學號和生成的練習題及對應答案輸出到文件“result.txt”中,不輸出額外信息。

附加需求

1. 產生帶括號的四則運算並求解,算式中存在的括號必須大於2個,且不得超過運算符的個數。

2. 產生真分數的加減法運算,運算過程中都為最簡真分數。


二、 功能設計

基本功能

能夠根據用戶輸入的參數n隨機產生n道符合要求的四則混合運算練習題,自動算出答案,並將式子與答案以文檔的形式呈現。

擴展功能

支持有括號的運算、支持最簡真分數的加減運算

技術分享圖片


三、 設計實現

首先,我從簡單四則混合運算開始,接著在這的基礎上完成了分數運算,最後再思考帶括號的運算。我先按這個順序分別創建了項目,單獨編寫,最後才分類規整到一個項目中,如圖我分為5個類。

技術分享圖片

  • Main類:主類,負責接收命令行的參數並啟動程序。
  • fileCreate類:創建文件類,負責每次出題類型並產生result.text文件,將學號與練習題寫入文件。
  • formula類:式子類,負責根據調用產生同種類型的式子,含有arithmetic(簡單四則運算)、bracket(帶括號的四則運算)、score(真分數加減運算)三種函數。
  • calculate類:計算類,負責各種計算,含有結果運算、有條件產生減數、有條件產生除數、有條件產生分子、有條件產生分母、判斷數的大小、求取最大公因數、求取最小公倍數、分數相加、分數相減等10個方法。

各類與各函數之間的關系如下圖。

技術分享圖片


四、 算法詳解

我的算法大多數比較簡單,多采用遞歸的方式。

  • 簡單四則運算

由於符號由+-*÷表示,而計算機計算使用+-*/,所以我先定義了兩個符號數組與定義兩個字符串,一個作為顯示式子用,一個用於計算,數組下標一一對應。再由範圍為3~5的隨機數確定式子的符號數,再由兩個整型數組存儲算數與符號對應的下標值,值皆由隨機數產生,算數範圍為0~100,下標值範圍為0~3。式子使用for循環,每次將一個算數與一個符號加入字符串中,執行完循環後,最後再加上最後一個算數。計算後,連同式子一並輸出。在計算時,我使用java調用js中的eval(String)函數求解。因為式子必須包含兩種運算符以上,在輸出前需要判斷是否所有符號相同,不相同則不輸出並重新運行程序。

技術分享圖片
 1 // 計算結果
 2     public static Object result(String temp) {
 3         ScriptEngineManager sem = new ScriptEngineManager();
 4         ScriptEngine se = sem.getEngineByName("js");
 5         Object last = 0;
 6         try {
 7             last = se.eval(temp);
 8         } catch (ScriptException e) {
 9             e.printStackTrace();
10         }
11         return last;
12     }
計算結果

考慮到運算過程中不能出現小數與負數,所以在除號與減號部分需要做處理,當減數大於被減數時,重新生成減數直至不大於被減數,當除數無法整除被除數或為0時,重新生成除數。後來在測試時發現兩兩數字符合規則,然而整條式子卻仍會出現負數與小數。經過思考,一般出現於連減、連除、減號後乘除因優先級不同的式子中,所以我設了個判斷,減號後一位只能為加號,除號後一位只能為加減號,由此解決了運算過程中出現負數或小數的問題。

技術分享圖片
 1 public static String arithmetic() {
 2         int m, j;
 3         char[] p = new char[] { ‘*‘, ‘+‘, ‘÷‘, ‘-‘ };
 4         char[] q = new char[] { ‘*‘, ‘+‘, ‘/‘, ‘-‘ };
 5         String temp1 = "";
 6         String temp2 = "";
 7         m = (int) (Math.random() * 3 + 3); // 符號數
 8         int[] num = new int[m + 1]; // 數字
 9         int[] key = new int[m]; // 符號所在的下標
10         for (j = 0; j <= m; j++) {
11             num[j] = (int) (Math.random() * 101);
12         }
13         for (j = 0; j < m; j++) {
14             if (j > 0 && key[j - 1] == 3) { // 減號後僅允許加號,防止負數出現
15                 key[j] = 1;
16             } else if (j > 0 && key[j - 1] == 2) {
17                 key[j] = (int) (Math.random() * 2); // 除號後僅允許乘號與加號,防止負數
18             } else {
19                 key[j] = (int) (Math.random() * 4); // 隨機符號
20             }
21             temp1 += String.valueOf(num[j]) + String.valueOf(p[key[j]]);
22             temp2 += String.valueOf(num[j]) + String.valueOf(q[key[j]]);
23             if (key[j] == 3) {
24                 num[j + 1] = calculate.decide1(num[j], num[j + 1]); // 選定小於被減數的減數
25             } else if (key[j] == 2) {
26                 num[j + 1] = calculate.decide2(num[j], num[j + 1]); // 確保能夠整除
27             }
28         }
29         j = 0;
30         while (j < (m - 1) && key[j] == key[j + 1])
31             j++; // 與第一個符號相同數
32         if (j == (m - 1))
33             return arithmetic(); // 若所有符號相同,該式子不算,保證有兩種運算符
34         else {
35             temp1 += String.valueOf(num[m]);
36             temp2 += String.valueOf(num[m]);
37             return temp1 + "=" + calculate.result(temp2);
38         }
39     }
簡單四則運算

  • 分數的加減

在基本混合運算的基礎上修改出分數加減就顯得簡單許多。最主要的就是分數的通分約分,解決了這個問題,程序就基本完成了。因為沒有優先級的關系,可以一邊補充式子一邊計算從而修改算數,無需限定符號。這也是我最滿意的代碼。

技術分享圖片
 1 // 真分數分式
 2     public static String score() {
 3         char[] p = new char[] { ‘+‘, ‘-‘ };
 4         int j;
 5         String temp1 = "";
 6         int m = (int) (Math.random() * 3 + 3);
 7         int[] key = new int[m]; // 運算符
 8         int[] x = new int[m + 1]; // 分子
 9         int[] y = new int[m + 1]; // 分母
10         int[] sum = new int[2];// 中途運算結果
11         for (j = 0; j <= m; j++) {
12             x[j] = (int) (Math.random() * 20 + 1);
13             y[j] = calculate.decide3(x[j]);
14         }
15         sum[0] = x[0];
16         sum[1] = y[0];
17         for (j = 0; j < m; j++) {
18             key[j] = (int) (Math.random() * 2);
19             if (key[j] == 0) { // 結果小於1
20                 int[] num = new int[2];
21                 num = calculate.fracAdd(sum[0], sum[1], x[j + 1], y[j + 1]);
22                 if (num[0] >= num[1]) {
23                     key[j] = 1;
24                 } else {
25                     sum = num;
26                 }
27             }
28             if (key[j] == 1) { // 結果不為負數
29                 int[] num = new int[2];
30                 num = calculate.fracSub(sum[0], sum[1], x[j + 1], y[j + 1]);
31                 if (num[0] < 0) {
32                     x[j + 1] = calculate.decide4(sum[0], sum[1]);
33                     y[j + 1] = sum[1];
34                     num = calculate.fracSub(sum[0], sum[1], x[j + 1], y[j + 1]);
35                 }
36                 sum = num;
37             }
38             temp1 += String.valueOf(x[j]) + "/" + String.valueOf(y[j]) + String.valueOf(p[key[j]]);
39         }
40         j = 0;
41         while (j < (m - 1) && key[j] == key[j + 1])
42             j++; // 與第一個符號相同數
43         if (j == (m - 1))
44             return score(); // 若所有符號相同,該式子不算,保證有兩種運算符
45         else {
46             temp1 += String.valueOf(x[m]) + "/" + String.valueOf(y[m]);
47             return temp1 + "=" + sum[0] + "/" + sum[1];
48         }
49     }
分數運算

  • 含括號的運算

這部分由於優先級的改變以及時間關系,還沒有改善完成,目前仍無法確保運算結果不含小數與負數。

我思考了很久如何隨機生成括號,最後受到一篇博客(當時忘記保存網址,現在已經找不到了,抱歉)使用概率的啟發,我以一定概率的方式生成左括號,記錄生成左括號的個數以及未匹配的左括號的個數,以一定概率生成右括號,最後補齊。

技術分享圖片
 1 if (((brack * 2) <= (n - 1)) && (((int) (Math.random() * 2)) == 0)) // 以一定概率生成左括號,概率為1/2
 2             {
 3                 temp1 += "(";
 4                 temp2 += "(";
 5                 brack++;
 6                 brack_left++;
 7                 temp1 += num[++i]; // 生成左括號後必須生成一個數字和運算符,不然可能出現(15)這樣的錯誤
 8                 temp2 += num[i];
 9                 op = (int) (Math.random() * 4);
10                 temp1 += Op[op];
11                 temp2 += p[op];
12                 if (op == 3)
13                     div = 1;
14                 else if (op == 1)
15                     div = 2;
16             }
概率生成左括號


五、 測試運行

進入src文件下,輸入javac -encoding utf-8 Main.java 編譯出相應的class文件,再輸入java Main 20進行測試,我們可以先測試java Main abc或java Main 1500或java Main 0,在這裏我使用的jdk版本為jdk1.8.0_25。

測試結果如下圖。

技術分享圖片

除此之外,我還學習了使用myeclipse做單元測試,測試結果如圖所示,我從測試結果發現,當文件存在時,刪除重建會耗時約2秒(以5道算式為例)。

技術分享圖片技術分享圖片技術分享圖片技術分享圖片


六、 總結

一開始由於我不清楚我能否完成附加功能,所以我用一個Main類的主函數完整的寫一個簡單的四則運算。但是調試時花費了許多時間,並且十分不靈活,所以我把函數中重復的計算部分抽離出來,寫成靜態方法,再進行測試,調試起來方便許多,想要修改哪一個部分的代碼只需要在相應的函數中修改即可。同理,分數運算與括號運算我也這麽做,由此我創建了三個項目。當三個項目都差不多完成時,我意識到其中有許多重復之處,所以我將它們分類並合在一個項目中。

除了主類包含了一個主函數外,我把我的程序分成3個類,各自分工,每個類中我盡量把各個功能細分成各種小方法,尤其在calculate類中每個方法不超過10行。具體可看第三點的關系圖。方法間的逐級調用給調試和測試都帶來了很多便利,尤其在測試優化方面,同時也增加了代碼的可移植性和可讀性。


七、 PSP

PSP2.1

任務內容

計劃共完成需要的時間(min)

實際完成需要的時間(min)

Planning

計劃

30

20

· Estimate

· 估計這個任務需要多少時間,並規劃大致工作步驟

30

20

Development

開發

1515

2470

· Analysis

· 需求分析 (包括學習新技術)

180

150

· Design Spec

· 生成設計文檔

50

30

· Design Review

· 設計復審 (和同事審核設計文檔)

10

15

· Coding Standard

· 代碼規範 (為目前的開發制定合適的規範)

5

5

· Design

· 具體設計

10

15

· Coding

· 具體編碼

1200

1800

· Code Review

· 代碼復審

30

15

· Test

· 測試(自我測試,修改代碼,提交修改)

30

440

Reporting

報告

75

170

· Test Report

· 測試報告

10

15

· Size Measurement

· 計算工作量

5

5

· Postmortem & Process Improvement Plan

· 事後總結, 並提出過程改進計劃

60

150

這次的代碼我思考了很久,在敲代碼與測試方面遠遠的超過了計劃的時間,源於我低估了代碼的復雜度。雖然有思路,但把思路實現卻花費了特別多的時間,思慮的不充分總是讓程序報錯,再加上我對java掌握的不夠,所以我在編程花費了許多許多時間,有很多時候就是不知如何實現需求。再者,打這篇博客報告也花費了很多時間,但也再次整體的梳理了我的思路。

在這次打代碼的過程中,我學到了四個方法。

一個是使用java調用js的eval()函數,這個方法可以輸入字符串型的算式然後直接算出答案。

另外一個是在用命令運行符使用Java Main 20作為命令時,應使用

1 n=Integer.parseInt(args[0]);

來獲取輸入的參數,而我在myeclipse使用的一直是

1 Scanner in = new Scanner(System.in);
2 n=in.nextInt();

語句執行。

還有一個是以概率的方式隨機生成括號。

最後是使用Debug修改錯誤代碼,使用單元測試優化程序提高性能。


這次的學習讓我意識到自己的不足,我以後會繼續努力。

2016012034+小學四則運算練習軟件項目報告