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


  本次作業從布置的第一天我就着手做了,不過基礎較差,並沒有想過怎樣實現相關功能,反正都是一步一步做的。我不知道我花了多少時間在這次作業上,但是在空閑的時候我就在琢磨。(今天大概從下午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的掌握還不夠,相關算法也是查閱很多博客和書籍才實現的。每晚睡覺都還想着怎么實現非負數和非整數,說真的作業很難,不過收獲很大,這兩周很充實也很疲憊。總之努力就會有收獲,很慶幸自己沒有放棄,我也會繼續努力的ヾ(◍°∇°◍)ノ゙。

  最后非常感謝您的閱讀,您辛苦啦!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM