结对项目——自动生成小学四则运算题目

作业所属课程 https://edu.cnblogs.com/campus/gdgy/Networkengineering1834
作业要求 https://edu.cnblogs.com/campus/gdgy/Networkengineering1834/homework/11148
作业目标 实现一个自动生成小学四则运算题目的命令行程序,结对共同完成一个项目

一.项目成员

  • 姓名:陈诒祺 学号:3118005318
  • 姓名:王烁俊 学号:3118005336

二.GitHub地址

GitHub仓库:https://github.com/EricChanXO/Pair-Item-arithmetic

三.PSP表格

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

四.性能分析

  • 各数据类型占用内存

  • CPU占用频率

五.设计实现过程

(一) 文件工程结构

(二)设计流程

  • 使用Random类进行随机取值,对每一道题目的运算符个数,括号位置,运算数值进行生成。
  • 使用的是Java语言,使用到几个固定的运算符和括号,通过Java随机类的api进行随机获取运算符和数值,在进行计算。

(三)关键代码说明

  • 数字

    package com.cyqwsj.arithmetic.service.impl;
    
    import com.cyqwsj.arithmetic.service.AbstractNumber;
    
    /**
     * 数字
     *
     * @author cyq
     * @date 2020/10/9 22:27
     */
    public class Number extends AbstractNumber {
    
        protected Number() {
            super();
        }
    
        public Number(int numerator) {
            super(numerator);
        }
    
        public Number(int numerator, int denominator) {
            super(numerator, denominator);
        }
    
        @Override
        public AbstractNumber convertToTrueFraction(int numerator, int denominator) {
            Number number = new Number();
            if (denominator == 0 || numerator == 0) {
                number.setNumerator(numerator);
                number.setDenominator(denominator);
                number.setNaN(true);
                return number;
            }
            //获取最大公因数
            int maxCommonFactor = getMaxCommonFactor(Math.abs(numerator), Math.abs(denominator));
            numerator = numerator / maxCommonFactor;
            denominator = denominator / maxCommonFactor;
            if (numerator < 0 && denominator < 0) {
                numerator = -numerator;
                denominator = -denominator;
            }
            number.setNumerator(numerator);
            number.setDenominator(denominator);
            if (denominator == 1) {
                number.setFraction(false);
            } else {
                number.setFraction(true);
            }
            return number;
        }
    
        @Override
        public AbstractNumber plus(AbstractNumber other) {
            int thisMolecule = this.getNumerator();
            int thisDenominator = this.getDenominator();
    
            int otherMolecule = other.getNumerator();
    
            //通分
            thisMolecule = thisMolecule * other.getDenominator();
            thisDenominator = thisDenominator * other.getDenominator();
            otherMolecule = otherMolecule * this.getDenominator();
    
    
            int numerator = thisMolecule + otherMolecule;
            return new Number(numerator, thisDenominator);
        }
    
        @Override
        public AbstractNumber subtract(AbstractNumber other) {
            int thisMolecule = this.getNumerator();
            int thisDenominator = this.getDenominator();
    
            int otherMolecule = other.getNumerator();
    
            //通分
            thisMolecule = thisMolecule * other.getDenominator();
            thisDenominator = thisDenominator * other.getDenominator();
            otherMolecule = otherMolecule * this.getDenominator();
    
    
            int numerator = thisMolecule - otherMolecule;
            return new Number(numerator, thisDenominator);
        }
    
        @Override
        public AbstractNumber multiply(AbstractNumber other) {
            int numerator = this.getNumerator() * other.getNumerator();
            int denominator = this.getDenominator() * other.getDenominator();
            return new Number(numerator, denominator);
        }
    
        @Override
        public AbstractNumber divide(AbstractNumber other) {
            int numerator = this.getNumerator() * other.getDenominator();
            int denominator = this.getDenominator() * other.getNumerator();
            return new Number(numerator, denominator);
        }
    
        /**
         * 获取最大公因数
         *
         * @param a 一个数
         * @param b 另一个数
         * @return 最大公因数
         */
        private int getMaxCommonFactor(int a, int b) {
            if (a < b) {
                int c = a;
                a = b;
                b = c;
            }
            int r = a % b;
            while (r != 0) {
                a = b;
                b = r;
                r = a % b;
            }
            return b;
        }
    }
    
  • 表达式(题目)

    package com.cyqwsj.arithmetic.service.impl;
    
    import com.cyqwsj.arithmetic.po.BracePosition;
    import com.cyqwsj.arithmetic.service.AbstractExpression;
    
    import java.util.ArrayList;
    import java.util.LinkedList;
    import java.util.List;
    
    import static com.cyqwsj.arithmetic.constant.OperationalConstant.*;
    
    /**
     * 表达式(题目)
     *
     * @author cyq
     * @date 2020/10/9 22:28
     */
    public class Expression extends AbstractExpression {
    
        @Override
        public void calculate() {
            Expression temp = copy(this);
            while (temp.getOperatorList().size() > 0) {
                int index = getPrioritizedOperation(temp);
                if (index == -1) {
                    throw new RuntimeException("error");
                }
                Number number1 = temp.getNumberList().get(index);
                Number number2 = temp.getNumberList().get(index + 1);
                String opt = temp.getOperatorList().get(index);
                Number result = operate(number1, number2, opt);
    
                temp.getNumberList().set(index, result);
                temp.getNumberList().remove(index + 1);
                temp.getOperatorList().remove(index);
    
            }
            this.setAnswer(temp.getNumberList().get(0));
        }
    
        /**
         * 复制一个表达式
         *
         * @param expression 要复制的表达式
         * @return 复制好的表达式
         */
        private Expression copy(Expression expression) {
            Expression temp = new Expression();
            List<Number> numberList = new LinkedList<>(expression.getNumberList());
            temp.setNumberList(numberList);
            List<String> optList = new LinkedList<>(expression.getOperatorList());
            temp.setOperatorList(optList);
            List<BracePosition> positionList = new LinkedList<>();
            for (BracePosition p : expression.getBracePositions()) {
                BracePosition position = new BracePosition(p.getBegin(), p.getEnd());
                positionList.add(position);
            }
            temp.setBracePositions(positionList);
            return temp;
        }
    
        /**
         * 做运算
         *
         * @param number1 操作数
         * @param number2 操作数
         * @param opt     运算符
         * @return 运算结果
         */
        private Number operate(Number number1, Number number2, String opt) {
            if (PLUS.equals(opt)) {
                return (Number) number1.plus(number2);
            } else if (SUBTRACT.equals(opt)) {
                Number result = (Number) number1.subtract(number2);
                if (result.getNumerator() < 0 || result.getDenominator() <= 0) {
                    this.setExistNegative(true);
                }
                return result;
            } else if (MULTIPLY.equals(opt)) {
                return (Number) number1.multiply(number2);
            } else if (DIVIDE.equals(opt)) {
    
                if (number2.getNumerator() == 0) {
                    this.setExistDivideZero(true);
                    return new Number(0);
                }
                return (Number) number1.divide(number2);
            }
            return null;
        }
    
        /**
         * 获取当前最高优先级的操作符
         *
         * @param expression 表达式
         * @return 最高优先级的操作符的下标
         */
        private int getPrioritizedOperation(Expression expression) {
            if (expression.getBracePositions().size() > 0) {
                //两个括号
                if (expression.getBracePositions().size() > 1) {
                    BracePosition pos1 = expression.getBracePositions().get(0);
                    BracePosition pos2 = expression.getBracePositions().get(1);
                    if (pos1.range() == pos2.range()) {
                        pos2.setBegin(pos2.getBegin() - 1);
                        pos2.setEnd(pos2.getEnd() - 1);
                        expression.getBracePositions().set(1, pos2);
                        expression.getBracePositions().remove(0);
                        return pos1.getBegin();
                    } else if (pos1.range() < pos2.range()) {
                        pos2.setEnd(pos2.getEnd() - 1);
                        expression.getBracePositions().set(1, pos2);
                        expression.getBracePositions().remove(0);
                        return pos1.getBegin();
                    } else if (pos1.range() > pos2.range()) {
                        pos1.setEnd(pos1.getEnd() - 1);
                        expression.getBracePositions().set(0, pos1);
                        expression.getBracePositions().remove(1);
                        return pos2.getBegin();
                    }
    
                    //一个括号
                } else if (expression.getBracePositions().size() == 1) {
                    BracePosition pos = expression.getBracePositions().get(0);
                    //括号包括一个操作符
                    if (pos.range() == 1) {
                        expression.getBracePositions().remove(0);
                        return pos.getBegin();
                        //括号包括2个操作符
                    } else {
                        String opt1 = expression.getOperatorList().get(pos.getBegin());
                        String opt2 = expression.getOperatorList().get(pos.getBegin() + 1);
                        //取第一个操作符
                        if (isPrioritizedOperation(opt1, opt2)) {
                            pos.setEnd(pos.getEnd() - 1);
                            expression.getBracePositions().set(0, pos);
                            return pos.getBegin();
                        //取第二个操作符
                        } else {
                            pos.setEnd(pos.getEnd() - 1);
                            expression.getBracePositions().set(0, pos);
                            return pos.getBegin() + 1;
                        }
                    }
                }
            } else {
                if (expression.getOperatorList().size() == 1) {
                    return 0;
                }
                int index = 0;
                String prioritizedOpt = expression.getOperatorList().get(0);
                String opt;
                for (int i = 1; i < expression.getOperatorList().size(); i++) {
                    opt = expression.getOperatorList().get(i);
                    if (!isPrioritizedOperation(prioritizedOpt, opt)) {
                        prioritizedOpt = opt;
                        index = i;
                    }
                }
                return index;
            }
            return -1;
        }
    
        /**
         * 比较两个运算符的优先级
         *
         * @param opt1 左边的运算符
         * @param opt2 右边的运算符
         * @return 左边优先级高返回true
         */
        private boolean isPrioritizedOperation(String opt1, String opt2) {
            if (opt1.equals(MULTIPLY) || opt1.equals(DIVIDE)) {
                return true;
            } else {
                if (opt2.equals(PLUS) || opt2.equals(SUBTRACT)) {
                    return true;
                }
            }
            return false;
        }
    
        @Override
        public boolean isRepeat(AbstractExpression expression) {
            if (this.getTotalOperator() == expression.getTotalOperator()) {
                List<Number> numberList = this.getNumberList();
                List<Number> otherNumberList = expression.getNumberList();
                if (numberList.containsAll(otherNumberList)) {
                    List<String> operatorList = this.getOperatorList();
                    List<String> otherOperatorList = expression.getOperatorList();
                    if (operatorList.containsAll(otherOperatorList)) {
                        return compareExpression(this, (Expression) expression);
                    }
                }
            }
            return false;
        }
    
        /**
         * 比较表达式是否重复
         *
         * @param expression      表达式
         * @param otherExpression 另一个表达式
         * @return true为重复
         */
        private boolean compareExpression(Expression expression, Expression otherExpression) {
            Expression temp1 = expression.copy(expression);
            Expression temp2 = expression.copy(otherExpression);
            while (temp1.getOperatorList().size() > 0) {
                int index1 = getPrioritizedOperation(temp1);
                int index2 = getPrioritizedOperation(temp2);
    
                Number number1 = temp1.getNumberList().get(index1);
                Number number2 = temp1.getNumberList().get(index1 + 1);
                String opt1 = temp1.getOperatorList().get(index1);
                Number result1 = operate(number1, number2, opt1);
    
                Number number3 = temp2.getNumberList().get(index2);
                Number number4 = temp2.getNumberList().get(index2 + 1);
                String opt2 = temp2.getOperatorList().get(index2);
                Number result2 = operate(number3, number4, opt2);
    
                if (PLUS.equals(opt1) && PLUS.equals(opt2) || MULTIPLY.equals(opt1) || MULTIPLY.equals(opt2)) {
                    if (!((number1.equals(number3) || number1.equals(number4))
                            && (number2.equals(number3) || number2.equals(number4)))) {
                        return false;
                    }
                }
                temp1.getNumberList().set(index1, result1);
                temp1.getNumberList().remove(index1 + 1);
                temp1.getOperatorList().remove(index1);
    
                temp2.getNumberList().set(index2, result2);
                temp2.getNumberList().remove(index2 + 1);
                temp2.getOperatorList().remove(index2);
    
            }
            return true;
        }
    
        @Override
        public boolean checkNegative() {
            return this.isExistNegative();
        }
    
        @Override
        public boolean checkDivideZero() {
            return this.isExistDivideZero();
        }
    
        @Override
        public boolean checkLegitimacy() {
            this.calculate();
            return checkNegative() || checkDivideZero();
        }
    
        @Override
        public String printQuestion() {
            StringBuilder stringBuilder = new StringBuilder();
            List<String> list = makeupString();
            for (String s : list) {
                stringBuilder.append(s);
                stringBuilder.append(" ");
            }
            stringBuilder.append("= ");
            return stringBuilder.toString();
        }
    
        /**
         * 构建输出字符串
         *
         * @return 字符串列表
         */
        private List<String> makeupString() {
            List<String> list = new ArrayList<>(7 + this.getBracePositions().size() * 2);
            List<Number> numberList = this.getNumberList();
            List<String> operatorList = this.getOperatorList();
            list.add(numberList.get(0).toString());
            for (int i = 0; i < operatorList.size(); i++) {
                list.add(operatorList.get(i));
                list.add(numberList.get(i + 1).toString());
            }
            insertBrackets(list);
            return list;
    
        }
    
        /**
         * 插入括号
         *
         * @param list 有操作数和运算符的字符串列表
         * @return 完整的运算表达式字符串
         */
        private List<String> insertBrackets(List<String> list) {
            List<BracePosition> bracePositions = this.getBracePositions();
            if (bracePositions == null || bracePositions.size() == 0) {
                return list;
            }
            BracePosition pos = bracePositions.get(0);
            //只有一个括号
            if (bracePositions.size() == 1) {
                //括号括着两个运算符
                if (pos.range() == 2) {
                    if (pos.getBegin() == 0) {
                        list.add(0, LEFT_BRACKET);
                        list.add(6, RIGHT_BRACKET);
                    } else {
                        list.add(2, LEFT_BRACKET);
                        list.add(8, RIGHT_BRACKET);
                    }
                } else {
                    int leftBegin = pos.getBegin() * 2;
                    list.add(leftBegin, LEFT_BRACKET);
                    list.add(leftBegin + 4, RIGHT_BRACKET);
                }
            } else {
                BracePosition pos2 = bracePositions.get(1);
                if (pos.range() == pos2.range()) {
                    int begin = 0;
                    list.add(begin, LEFT_BRACKET);
                    list.add(begin + 4, RIGHT_BRACKET);
                    list.add(begin + 6, LEFT_BRACKET);
                    list.add(10, RIGHT_BRACKET);
                } else {
                    if (pos.range() < pos2.range()) {
                        int leftBegin = pos.getBegin() * 2;
                        list.add(leftBegin, LEFT_BRACKET);
                        list.add(leftBegin + 4, RIGHT_BRACKET);
    
                        leftBegin = pos2.getBegin() * 2;
                        list.add(leftBegin, LEFT_BRACKET);
                        list.add(leftBegin + 6, RIGHT_BRACKET);
                    } else if (pos.range() > pos2.range()) {
                        int leftBegin = pos2.getBegin() * 2;
                        list.add(leftBegin, LEFT_BRACKET);
                        list.add(leftBegin + 4, RIGHT_BRACKET);
    
                        leftBegin = pos.getBegin() * 2;
                        list.add(leftBegin, LEFT_BRACKET);
                        list.add(leftBegin + 6, RIGHT_BRACKET);
                    }
                }
            }
            return list;
        }
    }
    
  • 题目生成器

    package com.cyqwsj.arithmetic.service;
    import com.cyqwsj.arithmetic.po.BracePosition;
    import com.cyqwsj.arithmetic.service.impl.Expression;
    import com.cyqwsj.arithmetic.service.impl.Number;
    import org.springframework.stereotype.Component;
    
    import java.util.ArrayList;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Random;
    
    import static com.cyqwsj.arithmetic.constant.OperationalConstant.*;
    
    /**
     * 题目生成器
     *
     * @author wsj
     * @date 2020/10/9 22:47
     */
    @Component
    public class TopicGenerator {
    
        private Random random = new Random();
    
        /**
         * 生成问题列表
         *
         * @param totalNum 问题个数
         * @param range    数字范围
         * @return 问题列表
         */
        public List<Expression> generate(int totalNum, int range) {
            List<Expression> expressionList = new LinkedList<>();
            int i = 0;
            while (i < totalNum) {
                Expression expression = new Expression();
    
                int totalOperator = randInt(1, 3);
                List<Number> numberList = generateNumList(totalOperator + 1, range);
                List<String> optList = generateOptList(totalOperator);
                List<BracePosition> positionList = generateBracketsPos(totalOperator);
    
                expression.setTotalOperator(totalOperator);
                expression.setNumberList(numberList);
                expression.setOperatorList(optList);
                expression.setBracePositions(positionList);
    
                //产生的表达式有问题
                if (expression.checkLegitimacy()) {
                    continue;
                }
                if (isRepeat(expressionList, expression)) {
                    continue;
                }
                expressionList.add(expression);
                i++;
    
            }
            return expressionList;
        }
    
        /**
         * 判断是否重复出题
         *
         * @param expressionList 题目列表
         * @param expression     要判断的列表
         * @return 是否重复
         */
        private boolean isRepeat(List<Expression> expressionList, Expression expression) {
            for (Expression s : expressionList) {
                if (expression.isRepeat(s)) {
                    return true;
                }
            }
            return false;
        }
    
        /**
         * 随机生成一个分数
         *
         * @param range      范围
         * @param isFraction 要分数还是自然数
         * @return 生成的数
         */
        private Number generateNum(int range, boolean isFraction) {
            if (isFraction) {
                return new Number(randInt(1, range), randInt(1, range));
            } else {
                return new Number(random.nextInt(range + 1));
            }
        }
    
        /**
         * 随机生成运算符
         *
         * @return 运算符
         */
        private String generateOpt() {
            List<String> list = new ArrayList<>(4);
            list.add(PLUS);
            list.add(SUBTRACT);
            list.add(MULTIPLY);
            list.add(DIVIDE);
            return choice(list);
        }
    
        /**
         * 随机生成括号位置列表
         *
         * @param optNum 运算符个数
         * @return 括号位置列表
         */
        private List<BracePosition> generateBracketsPos(int optNum) {
            List<BracePosition> positionList = new LinkedList<>();
            if (optNum < 2) {
                return positionList;
            } else if (optNum == 2) {
                BracePosition pos1 = new BracePosition(0, 1);
                BracePosition pos2 = new BracePosition(1, 2);
                List<BracePosition> list = new LinkedList<>();
                list.add(pos1);
                list.add(pos2);
                positionList.add(choice(list));
                return positionList;
            } else if (optNum == 3) {
                int begin;
                int end;
                //一个括号
                if (random.nextBoolean()) {
                    do {
                        begin = random.nextInt(3);
                        end = randInt(begin + 1, 3);
                    } while (end - begin >= 3);
                    positionList.add(new BracePosition(begin, end));
                    return positionList;
                    //两个括号
                } else {
                    //两个没有嵌套的括号
                    if (random.nextBoolean()) {
                        positionList.add(new BracePosition(0, 1));
                        positionList.add(new BracePosition(2, 3));
                        return positionList;
                    } else {
                        BracePosition pos = random.nextBoolean() ? new BracePosition(0, 2) : new BracePosition(1, 3);
                        begin = randInt(pos.getBegin(), pos.getEnd() - 1);
                        end = randInt(begin, pos.getEnd());
                        BracePosition pos1 = new BracePosition(begin, end);
                        positionList.add(pos);
                        positionList.add(pos1);
                        return positionList;
                    }
                }
            }
            return null;
        }
    
        /**
         * 随机生成数字列表
         *
         * @param totalNum 要生成的数量
         * @param range    范围
         * @return 数字列表
         */
        private List<Number> generateNumList(int totalNum, int range) {
            List<Number> list = new LinkedList<>();
            for (int i = 0; i < totalNum; i++) {
                list.add(generateNum(range, random.nextBoolean()));
            }
            return list;
        }
    
        /**
         * 随机生成运算符列表
         *
         * @param optNum 运算符数量
         * @return 运算符列表
         */
        private List<String> generateOptList(int optNum) {
            List<String> list = new LinkedList<>();
            for (int i = 0; i < optNum; i++) {
                list.add(generateOpt());
            }
            return list;
        }      
    
        /**
         * 随机选择列表中的一项
         *
         * @param list 列表
         * @param <T>  泛型
         * @return 选择的结果
         */
        private <T> T choice(List<T> list) {
            int i = random.nextInt(list.size());
            return list.get(i);
    
        }
    
        /**
         * 返回范围内的随机数
         *
         * @param min 最小值
         * @param max 最大值
         * @return 随机数
         */
        private int randInt(int min, int max) {
            return random.nextInt(max) % (max - min + 1) + min;
        }
    }      
    

六.运行测试

  • 测试 -n -r 参数

  • 运行结果
    Exercise.txt

Answers.txt

  • 测试 -e -a 参数
    测试用例:Myapp -e D:\eda\Pair-Item-arithmetic\Exercises.txt -a D:\eda\Pair-Item-arithmetic\Answers.txt

预期结果:正确 7 错误 3

  • 运行结果

  • 生成10000道题目

七.总结

经过这次的结对项目,我们不再局限于是个人编程,而是作为一个小团队来进行开发,可以充分发挥出各自的长处。首先在做结对编程项目的时候,先要一起讨论题目以及整个项目的需求,再综合两个人的想法,得到最后的执行方案。
编码过程中,各自完成属于自己的部分并且进行测试,这大大提高了我们的效率。并且在开发过程中应及时沟通双方工作进程,当遇到推进困难问题可以双方一起解决,这样就不会落下进度。本次的项目虽然做的并没有那么完美,还有地方值得优化,但是本次的结对项目体验到了小团队开发的经历,学会相互合作,共同进步。

posted @ 2020-10-12 22:25  EmmaJun  阅读(166)  评论(0编辑  收藏  举报