java實現小學生四則運算


GitHub地址https://github.com/TaoTaoLv1/arithmetic

結對伙伴:葉文濤

項目要求:

實現一個自動生成小學四則運算題目的命令行程序.

  • 使用 -n 參數控制生成題目的個數(完成)
  • 使用 -r 參數控制題目中數值的范圍, 。該參數可以設置為1或其他自然數。(完成)
  • 生成的題目中計算過程不能產生負數(完成)
  • 生成的題目中如果存在形如e1 ÷ e2的子表達式,那么其結果應是真分數。(完成)
  • 程序一次運行生成的題目不能重復,生成的題目存入執行程序的當前目錄下的Exercises.txt文件(完成)
  • 每道題目中出現的運算符個數不超過3個(完成)
  • 在生成題目的同時,計算出所有題目的答案,並存入執行程序的當前目錄下的Answers.txt文件(完成)
  • 程序應能支持一萬道題目的生成。(完成)
  • 程序支持對給定的題目文件和答案文件,判定答案中的對錯並進行數量統計。(完成)

 

設計:

分為兩個部分,第一是生成題目,生成表達式及答案后寫入當前目錄下的文件,第二是判斷答案是否正確,讀取文件后判斷,再將結果寫入當前目錄下的文件。

 

 

 代碼

 主函數

 public static void main(String[] args){

            System.out.println("請選擇功能:");
            System.out.println("    1. 四則運算生成器");
            System.out.println("    2. 答案對比");
            System.out.print("請輸入你的選擇[1/2]:");
            int choose = new Scanner(System.in).nextInt();

            switch (choose){
                case 1:
                    ProducerController producerController = new ProducerController();
                    producerController.ConstructProblem();break;
                case 2:
                    JudgeAnswerController judgeAnswerController = new JudgeAnswerController();
                    judgeAnswerController.start();break;
                default:
                    System.out.println("輸入不正確,請輸入1或2");main(args);break;
            }
    }

 

整數生成器與真分數生成器函數

 public String[] createProblem(int range){
            Random random = new Random();
            int operatorCount = 1 + random.nextInt(3); //隨機操作符的個數(1-3個)
            int operand[] = new int[operatorCount + 1]; //操作數個數
            int[] operatorIndex = index(operatorCount, 4, random);

            for(int i = 0; i < operatorCount + 1; i++){
                operand[i] = random.nextInt(range);
            }

            String formula = stitchingFormula(operatorCount, operand, operatorIndex);

            //計算結果
            Calculator calculator = new Calculator();
            int res = calculator.algorithm(formula);
            String formulaRes[] = new String[2];

            if (res > 0){
                formulaRes[0] = formula;
                formulaRes[1] = String.valueOf(res);
            }else {
                return createProblem(range);
            }
            return formulaRes;
        }
 public String[] createProblem(int range){
            Random random = new Random();
            int operatorCount = 1 + random.nextInt(3); //操作符的個數1-3

            CreateInteger create = new CreateInteger();
            int[] operatorIndex = create.index(operatorCount,2, random); //操作符的下標

            //生成第一個操作數
            int[] coprimeNumber1 = createCoprimeNumbers(range, random);
            int x = coprimeNumber1[0];
            int y = coprimeNumber1[1];

            String s = shamToProperFraction(x, y);

            for(int i=0; i < operatorCount; i++){
                //生成剩下的操作數
                int[] coprimeNumber = createCoprimeNumbers(range, random);
                int numx = coprimeNumber[0];
                int numy = coprimeNumber[1];

                String currentOpreator = OPERATOR[operatorIndex[i]];

                if(currentOpreator.equals("+")){  //加法
                    x = x * numy + y * numx;
                    y = y * numy;
                }else {   //減法
                    int count = 0;
                    while(x * numy - y * numx < 0){ //差為負數
                        coprimeNumber = createCoprimeNumbers(range, random);
                        numx = coprimeNumber[0];
                        numy = coprimeNumber[1];
                        count++;
                        if (count >= 5){
                            numx = x - 1;
                            numy = y;
                        }
                    }
                    x = x * numy - y * numx;
                    y = y * numy;
                }

                String num = shamToProperFraction(numx, numy);
                s += currentOpreator + num;
            }

            int greatFactor = greatFactor(x,y);
            x /= greatFactor; //最終結果化簡
            y /= greatFactor;

            String res = shamToProperFraction(x, y);
            s += "=";

            String formulaRes[] = {s, res};
            return formulaRes;
        }

 /**
         * 假分數轉化為真分數
         * @param x 分子
         * @param y 分母
         * @return
         */
        public String shamToProperFraction(int x, int y){
            if (x > y){
                int n = x / y;
                x = (x - n * y);
                if (x == 0){
                    return String.valueOf(n);
                }
                return n + "'" + x + "/" + y;
            }else if (x == y){
                return "1";
            }else if (y == 1){
                return String.valueOf(x);
            }else if (x == 0){
                return "0";
            }
            return x + "/" + y;
    }
}

 

計算結果函數

    public int algorithm(String s) {
        Stack<Integer> numStack = new Stack<>(); //放數字
        Stack<String> operatorStack = new Stack<>(); //放操作符
        HashMap<String, Integer> hashMap = new HashMap<>(); //存放運算符優先級
        hashMap.put("(", 0);
        hashMap.put("+", 1);
        hashMap.put("-", 1);
        hashMap.put("*", 2);
        hashMap.put("÷", 2);

        String formula = s.replaceAll(" ", "");

        for (int i = 0; i < formula.length();) {
            StringBuilder digit = new StringBuilder();  //StringBuilder類中的方法主要偏重於對於字符串的變化,例如追加、插入和刪除等,這個也是StringBuffer和String類的主要區別。
            char c = formula.charAt(i); //將式子字符串切割為c字符
            while (Character.isDigit(c)) { //判斷字符是否為10進制數字,將一個數加入digit
                digit.append(c);
                i++;
                if (i < formula.length()){
                    c = formula.charAt(i);
                }else {
                    break;
                }
            }
            if (digit.length() == 0){ //當前digit里面已經無數字,即當前處理符號
                switch (c) {
                    case '(': {
                        operatorStack.push(String.valueOf(c));//如果是(   轉化為字符串壓入字符棧
                        break;
                    }
                    case ')': { //遇到右括號了計算,因為(的優先級最高
                        String stmp = operatorStack.pop(); //如果是),將符號棧棧頂元素取到
                        while (!operatorStack.isEmpty() && !stmp.equals("(")) { //當前符號棧里面還有+ - * /
                            int a = numStack.pop();  //取操作數a,b
                            int b = numStack.pop();
                            int result = calculate(b, a, stmp); //計算
                            if(result < 0)
                                return  -1;
                            numStack.push(result); //將結果壓入棧
                            stmp = operatorStack.pop(); //符號指向下一個計算符號
                        }
                        break;
                    }
                    case '=': { //遇到等號了計算
                        String stmp;
                        while (!operatorStack.isEmpty()) { //當前符號棧里面還有+ - * /,即還沒有算完
                            stmp = operatorStack.pop();
                            int a = numStack.pop();
                            int b = numStack.pop();
                            int result = calculate(b, a, stmp);
                            if(result < 0)
                                return  -1;
                            numStack.push(result);
                        }
                        break;
                    }
                    default: {  //不滿足之前的任何情況
                        String stmp;
                        while (!operatorStack.isEmpty()) { //如果符號棧有符號
                            stmp = operatorStack.pop(); //當前符號棧,棧頂元素
                            if (hashMap.get(stmp) >= hashMap.get(String.valueOf(c))) { //比較優先級
                                int a = numStack.pop();
                                int b = numStack.pop();
                                int result =calculate (b, a, stmp);
                                if(result < 0)
                                    return  -1;
                                numStack.push(result);
                            }
                            else {
                                operatorStack.push(stmp);
                                break;
                            }

                        }
                        operatorStack.push(String.valueOf(c));  //將符號壓入符號棧
                        break;
                    }
                }
            }
            else { //處理數字,直接壓棧
                numStack.push(Integer.valueOf(digit.toString()));  //Integer.valueof()返回的是Integer對象,而Integer.parseInt()返回的是int型
                continue; //結束本次循環,回到for語句進行下一次循環,即不執行i++(因為此時i已經指向符號了)
            }
            i++;
        }
        return numStack.peek();  //返回棧底數字即等式的答案。
    }

 

判斷結果函數

 public void start(){
        System.out.print("請輸入待驗證答案路徑:");
        Scanner scanner = new Scanner(System.in);
        String exerciseFilePath = scanner.next();
        System.out.print("請輸入程序生成答案文件路徑:");
        String answerFilePath = scanner.next();

        try {
            List<String> exerciseAnswers = exerciseFileReader(exerciseFilePath);
            List<String> answers = answerReader(answerFilePath);

            List<String> correct = new ArrayList<>();
            List<String> wrong = new ArrayList<>();

            int max = Math.max(exerciseAnswers.size(), answers.size());
            int num = 1;
            for (int i = 0; i < max; i++){
                if (exerciseAnswers.get(i).equals(answers.get(i))){
                    correct.add(String.valueOf(num++));
                }else {
                    wrong.add(String.valueOf(num++));
                }
            }

            File grade = new File("Grade.txt");
            if (grade.exists()){
                grade.delete();
            }
            if (grade.createNewFile()){
                FileOutputStream gradeOutput = new FileOutputStream(grade);
                PrintStream gradePrintStream = new PrintStream(gradeOutput);
                String corrects = String.join(",", correct);
                gradePrintStream.println("Correct:" + correct.size() +
                        " (" + corrects + ")");
                String wrongs = String.join(",", wrong);
                gradePrintStream.println("Wrong:" + wrong.size() +
                        " (" + wrongs + ")");
            }

            System.out.println("判定完成");

        } catch (FileNotFoundException e) {
            System.out.println("文件不存在");
        } catch (IOException e) {
            System.out.println("文件讀入異常");
        }
    }

    public List<String> exerciseFileReader(String path) throws IOException {
        BufferedReader exerciseReader = new BufferedReader(new FileReader(path));
        String exerciseAnswer = "";
        List<String> exerciseAnswers = new ArrayList<>();
        while ((exerciseAnswer = exerciseReader.readLine()) != null){
            String[] split = exerciseAnswer.split("=");
            if (split[1] != null){
                exerciseAnswers.add(split[1]);
            }else {
                exerciseAnswers.add(" ");
            }
        }
        return exerciseAnswers;
    }

 

測試

1.隨機生成10道10以內的四則運算

 

 2.判斷題目的正確與否

3.支持生成一萬道題目

由於題目過多,因此直接在連接中打開

測試題目文件

測試答案文件

以上就是所有功能的測試

PSP表格

PSP2.1

Personal Software Process Stages

預估耗時(分鍾)

實際耗時(分鍾)

Planning

計划

10

5

· Estimate

· 估計這個任務需要多少時間

800

1200

Development

開發

480

630

· Analysis

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

60

30

· Design Spec

· 生成設計文檔

60

80

· Design Review

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

30

45

· Coding Standard

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

30

30

· Design

· 具體設計

30

60

· Coding

· 具體編碼

120

360

· Code Review

· 代碼復審

30

45

· Test

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

120

120

Reporting

報告

120

120

· Test Report

· 測試報告

60

30

· Size Measurement

· 計算工作量

30

30

· Postmortem & Process Improvement Plan

· 事后總結, 並提出過程改進計划

30

30

合計

 

1200

1610

 

總結

在這次編程作業中,我和葉文濤同學一起討論,他主要負責編寫代碼,后期的測試和報告則由我負責,在這一次的作業中,我也從伙伴身上學習到了很多,認識到自己與他人之間的差距,他在編程過程中也耐心的解答我的疑惑,在這個過程中不斷的完善代碼。兩個人一起處理問題,互相汲取對方好的想法,有些細節沒有考慮到的,另一個人可以幫忙補充,這樣使得效率也大大提高。例如在我們進行最后測試的過程中,我發現當計算答案時如果答案為空則會出錯,在這個情況下我們進行討論之后也克服了這個問題。


免責聲明!

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



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