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的掌握还不够,相关算法也是查阅很多博客和书籍才实现的。每晚睡觉都还想着怎么实现非负数和非整数,说真的作业很难,不过收获很大,这两周很充实也很疲惫。总之努力就会有收获,很庆幸自己没有放弃,我也会继续努力的ヾ(◍°∇°◍)ノ゙。

  最后非常感谢您的阅读,您辛苦啦!

原文地址:https://www.cnblogs.com/tingjuanli/p/8632652.html