小学四则运算生成Java实现 (彭迪彬 李尤)

Posted 2youyou

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小学四则运算生成Java实现 (彭迪彬 李尤)相关的知识,希望对你有一定的参考价值。

Java实现小学四则运算

by 彭迪彬 李尤

GitHub项目地址:https://github.com/2youyou/JDBC

项目要求

题目:实现一个自动生成小学四则运算题目的命令行程序

  • 功能列表
  1. [完成] 使用 -n 参数控制生成题目的个数。
  2. [完成] 使用 -r 参数控制题目中数值的范围。
  3. [完成] 生成的题目中计算过程不能产生负数。
  4. [完成] 生成的题目中如果存在形如e1 ÷ e2的子表达式,那么其结果应是真分数
  5. [完成] 程序一次运行生成的题目不能重复,生成的题目存入执行程序的当前目录下的Exercises.txt文件。
  6. [完成] 每道题目中出现的运算符个数不超过3个。
  7. [完成] 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件。
  8. [完成] 程序应能支持一万道题目的生成。
  9. [完成] 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计。

设计实现

1.表达式生成:根据输入的两个参数决定表达式的数量及数值范围,随机生成数值范围内的自然数及运算符,随机插入左括号并在相应的位置插入右括号。

2.分数:专门写了一个类Fraction来生成分数,并且有约分的功能。

3.负数:负数只会在减法运算时产生,每当遇到减法运算时,若运算结果出现负数,就重新生成新的表达式。

4.计算:将整数也当成分数进行计算,先将上面生成的表达式(以字符串存储)分别入数字栈和符号栈,再根据符号优先级计算最终结果。

技术分享图片

代码

分数类

public class Fraction {
    int x;              //分子
    int y;              //分母
    private Fraction temp;

    public Fraction(int a, int b) {
        x = a;
        y = b;
    }

    Fraction add(Fraction r) {
        temp = new Fraction(0, 0);
        temp.x = x * r.y + y * r.x;
        temp.y = y * r.y;
        return temp;
    }

    Fraction minus(Fraction r) {
        temp = new Fraction(0, 0);
        temp.x = x * r.y - y * r.x;
        temp.y = y * r.y;
        return temp;
    }

    Fraction multiply(Fraction r) {
        temp = new Fraction(0, 0);
        temp.x = x * r.x;
        temp.y = y * r.y;
        return temp;
    }

    Fraction divide(Fraction r) {
        temp = new Fraction(0, 0);
        temp.x = x * r.y;
        temp.y = y * r.x;
        return temp;
    }

    String print() {
        /**
         * 计算结果化简
         */
        if (x == 0) {
            return "0";
        } else {
            int n;
            if (x > y)
                n = x;
            else
                n = y;
            int maxn = 0;
            for (int i = 1; i <= n; ++i) {              //约分
                if (x % i == 0 && y % i == 0)
                    maxn = i;
            }
            int a = x / maxn;
            int b = y / maxn;
            if (a == b)
                return "1";
            else if(b==1)
                return a+"";
            else
                return a + "/" + b;
        }
    }
}

表达式生成

public class CreateExercise {
    String[] sign = {"+", "-", "x", "÷", "/"};
    int range_num;
    Random random = new Random();

    
    public void setRange_num(int range_num) {
        this.range_num =range_num;
    }

    
    public String create() {
        String str = "";
        int local = random.nextInt(3);
        
        for (int j = 0; j < 3; j++) {
            if (local == 0 && j == 0) {
                str += "(";
            } else if (local == 2 && j == 1) {
                str += "(";
            }
            
            str += random.nextInt(range_num) % range_num + 1;   //产生指定范围随机数
            if (local == 0 && j == 1) {
                str += ")";
            }
            if (local == 2 && j == 2) {
                str += ")";
            }
            
            String signElement = sign[random.nextInt(5)];//产生随机运算符号
            str += signElement;
            if (signElement == "/") {
                str += random.nextInt(range_num) % range_num + 1;
                signElement = sign[random.nextInt(5)];
                while (true) {
                    if (signElement != "/") {
                        str += signElement;
                        break;
                    }
                    signElement = sign[random.nextInt(5)];
                }
            }

        }
        str = str.substring(0, str.length() - 1);
        return str;
    }

    public void belongFraction(String strfraction) {
        /**
         * 处理分数计算
         */
        String[] fractionlist = null;
        if (strfraction.contains("+")) {
            fractionlist = strfraction.split("\\+");
            CalculateFraction(fractionlist, 0);
        } else if (strfraction.contains("-")) {
            fractionlist = strfraction.split("-");
            CalculateFraction(fractionlist, 1);
        } else if (strfraction.contains("x")) {
            fractionlist = strfraction.split("\\x");
            CalculateFraction(fractionlist, 2);
        } else if (strfraction.contains("÷")) {
            fractionlist = strfraction.split("÷");
            CalculateFraction(fractionlist, 3);
        }

    }

    public void CalculateFraction(String[] strlist, int flag) {
        /**
         * 分数的四种基本运算
         */
        String[] fraction1 = new String[2];
        String[] fraction2 = new String[2];
        
        if (strlist[0].contains("/"))
            fraction1 = strlist[0].split("/");
        else {
            fraction1[0] = strlist[0];
            fraction1[1] = "1";
        }
        if (strlist[1].contains("/"))
            fraction2 = strlist[1].split("/");
        else {
            fraction2[0] = strlist[1];
            fraction2[1] = "1";
        }

        Fraction fr1 = new Fraction(Integer.parseInt(fraction1[0]), Integer.parseInt(fraction1[1]));
        Fraction fr2 = new Fraction(Integer.parseInt(fraction2[0]), Integer.parseInt(fraction2[1]));
        fr1.print();
        
        switch (flag) {
            case 0:
                fr1.add(fr2).print();
                break;
            case 1:
                fr1.minus(fr2).print();
                break;
            case 2:
                fr1.multiply(fr2).print();
                break;
            case 3:
                fr1.divide(fr2).print();
                break;
        }
    }
}

计算

public class Calculator {

    /**
     * 数字栈:存储表达式中的数字
     */
    private Stack<String> numStack = null;
    /**
     * 符号栈:存储表达式中的运算符和括号
     */
    private Stack<Character> charStack = null;

    /**
     * 计算四则运算表达式,返回计算结果
     *
     */
    public String calculate(String numStr) {
        numStr = removeStrSpace(numStr);
        
        if (numStr.length() > 1 && !"=".equals(numStr.charAt(numStr.length() - 1) + "")) {
            numStr += "=";
        }
        if (!isStandard(numStr)) {
            return "0";
        }

        numStack = new Stack<String>();
        charStack = new Stack<Character>();
        StringBuffer temp = new StringBuffer();
        
        for (int i = 0; i < numStr.length(); i++) {
            char ch = numStr.charAt(i);
            if (isNumber(ch) || ch == ‘/‘) { 
                temp.append(ch);
            } else {
                String tempStr = temp.toString();
                if (!tempStr.isEmpty()) {
                    numStack.push(tempStr);
                    temp = new StringBuffer();
                }
                while (!comparePri(ch) && !charStack.empty()) {
                    String a = numStack.pop(); 
                    String b = numStack.pop();
                    Fraction f1 = null;
                    Fraction f2 = null;
                    if (a.contains("/")) {
                        String[] alist = a.split("/");
                        f1 = new Fraction(Integer.parseInt(alist[0]), Integer.parseInt(alist[1]));
                    } else {
                        f1 = new Fraction(Integer.parseInt(a), 1);
                    }
                    if (b.contains("/")) {
                        String[] blist = b.split("/");
                        f2 = new Fraction(Integer.parseInt(blist[0]), Integer.parseInt(blist[1]));
                    } else {
                        f2 = new Fraction(Integer.parseInt(b), 1);
                    }
                    switch (charStack.pop()) {
                        case ‘+‘:
                            numStack.push(f2.add(f1).print());
                            break;
                        case ‘-‘:
                            if ((f1.x/f1.y) >= (f2.x/f2.y)) {
                                return null;
                            
                        }
                            numStack.push(f2.minus(f1).print());
                            break;
                        case ‘x‘:
                            numStack.push(f2.multiply(f1).print());
                            break;
                        case ‘÷‘:
                            if (f1.x==0){
                                return null;
                            }
                            numStack.push(f2.divide(f1).print());
                            break;
                        default:
                            break;
                    }
                } 
                if (ch != ‘=‘) {
                    charStack.push(new Character(ch)); 
                    if (ch == ‘)‘) { 
                        charStack.pop();
                        charStack.pop();
                    }
                }
            }
        } 

        return numStack.pop(); 
    }


    private String removeStrSpace(String str) {
        return str != null ? str.replaceAll(" ", "") : "";
    }

 
    private boolean isStandard(String numStr) {
        if (numStr == null || numStr.isEmpty()) 
            return false;
        Stack<Character> stack = new Stack<Character>(); 
        boolean b = false; 
        for (int i = 0; i < numStr.length(); i++) {
            char n = numStr.charAt(i);
            if (!(isNumber(n) || "(".equals(n + "") || ")".equals(n + "")
                    || "+".equals(n + "") || "-".equals(n + "")
                    || "x".equals(n + "") || "÷".equals(n + "") || "/".equals(n + "")
                    || "=".equals(n + ""))) {
                return false;
            }
            
            if ("(".equals(n + "")) {
                stack.push(n);
            }
            if (")".equals(n + "")) { 
                if (stack.isEmpty() || !"(".equals((char) stack.pop() + "")) 
                    return false;
            }
            
            if ("=".equals(n + "")) {
                if (b)
                    return false;
                b = true;
            }
        }
        if (!stack.isEmpty())
            return false;
        if (!("=".equals(numStr.charAt(numStr.length() - 1) + "")))
            return false;
        return true;
    }


    private boolean isNumber(char num) {
        if (num >= ‘0‘ && num <= ‘9‘)
            return true;
        return false;
    }

    /**
     * 比较优先级:如果当前运算符比栈顶元素运算符优先级高则返回true,否则返回false
     */
    private boolean comparePri(char symbol) {
        if (charStack.empty()) { 
            return true;
        }


        char top = charStack.peek();
        if (top == ‘(‘) {
            return true;
        }
        switch (symbol) {
            case ‘(‘: 
                return true;
            case ‘x‘: {
                if (top == ‘+‘ || top == ‘-‘) 
                    return true;
                else
                    return false;
            }
            case ‘÷‘: {
                if (top == ‘+‘ || top == ‘-‘) 
                    return true;
                else
                    return false;
            }
            case ‘+‘:
                return false;
            case ‘-‘:
                return false;
            case ‘)‘: 
                return false;
            case ‘=‘:
                return false;
            default:
                break;
        }
        return true;
    }

    String getFinalResult(String str) {
        if (!str.contains("/"))
            return str;
        String[] part = str.split("/");
        int a = Integer.parseInt(part[0]);
        int b = Integer.parseInt(part[1]);
        
        if (a == b)
            return "1";
        else if (a > b && a % b != 0) {
            return a / b + "’" + a % b + "/" + b;
        } else if (a < b && -a > b && (-a) % b != 0) {
            return "-" + (-a) / b + "’" + (-a) % b + "/" + b;
        } else if (b == 1)
            return a + "";
        else
            return a + "/" + b;
    }

}

生成txt文本

public class IO {
    File ExerciseFile = null;
    File AnswerFile = null;
    String filename = "";
    BufferedWriter ExerciseOut = null;
    BufferedWriter AnswerOut = null;

    public IO() {
        if (this.CreateFile()) {
            this.setOutBufferedWriter();
        } else
            System.out.println("创建文件失败!");
    }

    public void setOutBufferedWriter() {
        try {
            this.ExerciseOut = new BufferedWriter(new FileWriter(ExerciseFile));
            this.AnswerOut=new BufferedWriter(new FileWriter(AnswerFile));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public boolean CreateFile() {
        String relativelyPath=System.getProperty("user.dir");
        ExerciseFile = new File(relativelyPath+"\\Exercise" + ".txt");
        AnswerFile = new File(relativelyPath+"\\Answer" + ".txt");
        if (ExerciseFile.exists()) {
            ExerciseFile.delete();
        }
        if (AnswerFile.exists()) {
            AnswerFile.delete();
        }
        try {
            ExerciseFile.createNewFile();
            AnswerFile.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return true;
    }

    public boolean WriteToFile(String content, int flag) {
        try {
            switch (flag) {
                case 0:
                    ExerciseOut.write(content);
                    ExerciseOut.write("
");
                    ExerciseOut.flush();
                    return true;
                case 1:
                    AnswerOut.write(content);
                    AnswerOut.write("
");
                    AnswerOut.flush();
                    return true;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    public boolean CloseOutBufferedWriter() {
        try {
            ExerciseOut.close(); 
            AnswerOut.close();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }
}

主函数启动类

public class MyApp {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        System.out.print("请输入希望生成的题目数量:");
        int problems_num = scan.nextInt();
        System.out.print("请输入题目的数值范围n(范围为0到n):");
        int range_num = scan.nextInt();
        System.out.print("是否开始答题(yes/no):");
            String is_doNow=scan.next();

        LinkedHashMap<Integer, String> rightAnswerMap = new LinkedHashMap<Integer, String>();
        LinkedHashMap<Integer,String> exerciseMap = new LinkedHashMap<Integer, String>();
        ArrayList<Integer> rightRecord = new ArrayList<Integer>();
        ArrayList<Integer> wrongRecord = new ArrayList<Integer>();
        CreateExercise ce = new CreateExercise();
        IO save = new IO();
        ce.setRange_num(range_num);
        
        for (int i = 1; i <= problems_num; i++) {
            String problem = ce.create();
            exerciseMap.put(i,problem);
            String ns = problem;
            int rightbrackets;
            int leftbrackets;
            
            if (problem.contains(")")) {
                rightbrackets = problem.indexOf(")");
                leftbrackets = problem.indexOf("(");
                if (rightbrackets != problem.length() - 1 && problem.charAt(rightbrackets + 1) == ‘/‘) {
                    StringBuilder sb = new StringBuilder(problem);
                    if (leftbrackets - 1 > 0 && problem.charAt(leftbrackets - 1) == ‘÷‘)
                        sb.replace(rightbrackets + 1, rightbrackets + 2, "x");
                    else
                        sb.replace(rightbrackets + 1, rightbrackets + 2, "÷");
                    ns = sb.toString();
                }
            }


            Calculator cal = new Calculator();
            String result = cal.calculate(ns);
            
            if (result != null) {
                result = cal.getFinalResult(result);
                System.out.println(i + ":  " + problem + "=" );
                rightAnswerMap.put(i, result);
                if (!save.WriteToFile(i + ":  " + problem + "=", 0)) {
                    System.out.println("生成Exercise文件失败!");
                    System.exit(0);
                }
                if (!save.WriteToFile(+ i + ":  " + problem + "=" + result, 1)) {
                    System.out.println("生成Answer文件失败!");
                    System.exit(0);
                }
            } else {
                i--;
            }

            

        }

        if(is_doNow.equals("yes")){
            System.out.println("请输入答案(带分数用“ ’ ”分隔,如1’1/2):");
            
            for (int i = 1; i <= problems_num; i++) {
                System.out.print( + i + ":  " + exerciseMap.get(i) + "=");
                String input = scan.next();
                if (rightAnswerMap.get(i).equals(input))
                    rightRecord.add(i);
                else
                    wrongRecord.add(i);
            }
            System.out.println("结果为:");

            if (rightRecord.size()!=0){
                System.out.print("正确题目:"+rightRecord.size()+" (");
                for (int i=0;i<rightRecord.size()-1;i++)
                    System.out.print(rightRecord.get(i)+",");
                System.out.println(rightRecord.get(rightRecord.size()-1)+")");
            }
            else
                System.out.println("正确题目:"+rightRecord.size());

            if (wrongRecord.size()!=0){
                System.out.print("错误题目:"+wrongRecord.size()+" (");
                for (int i=0;i<wrongRecord.size()-1;i++)
                    System.out.print(wrongRecord.get(i)+",");
                System.out.println(wrongRecord.get(wrongRecord.size()-1)+")");
            }
            else
                System.out.println("错误题目:"+wrongRecord.size());
        }
        if (save.CloseOutBufferedWriter())
            System.out.println("题目和答案文本创建成功");
        else
            System.out.println("题目和答案文本创建失败");
    }
}

测试运行

生成10000道题目

技术分享图片

部分题目及答案

技术分享图片

点击查看完整题目

点击查看完整答案

答案统计

技术分享图片

故意答错 1,3题
技术分享图片

代码覆盖率
技术分享图片

PSP

PSP2.1

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 60 120
· Estimate · 估计这个任务需要多少时间 30 60
Development 开发 1000 2200
· Analysis · 需求分析 (包括学习新技术) 100 180
· Design Spec · 生成设计文档 60 100
· Design Review · 设计复审 (和同事审核设计文档) 30 30
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30 30
· Design · 具体设计 120 150
· Coding · 具体编码 600 1500
· Code Review · 代码复审 50 60
· Test · 测试(自我测试,修改代码,提交修改) 150 60
Reporting 报告 80 80
· Test Report · 测试报告 30 40
· Size Measurement · 计算工作量 20 10
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 40 20
合计 1150 2400

总结

此次的结对编程,我主要负责表达式的生成、计算,李尤同学主要负责文件生成的IO,我们都相互审阅了对方的代码。刚拿到题目的时候,我们的思路并不是很清晰,对于表达式的计算及负数的避免想要直接实现,尝试过后发现过于麻烦,后来与李尤同学讨论后发现使用数据结构的栈来实现会简单很多,可以说这次的结对编程,通过两个人的讨论,使得项目的整体思路变得很清晰,实现起来也更加容易。此次编程较为遗憾的没有实现题目的查重功能,我们以后会继续努力完善这个功能。李尤同学的思维很活跃,经常有一些天马行空的想法,在编写程序的过程中给了我很多启发。这次的结对编程,让我体会到了合作的力量,同时也认识到了自己数据结构方面知识的不足,今后会多加学习这方面的知识,完善自己的不足。



以上是关于小学四则运算生成Java实现 (彭迪彬 李尤)的主要内容,如果未能解决你的问题,请参考以下文章

自动生成小学四则运算(简易)

实现生成小学四则运算练习题

小学生四则运算JAVA

自动生成小学四则运算题目(Python实现)

结对编程实现小学四则运算

实现一个自动生成小学四则运算题目的命令行程序