zoukankan      html  css  js  c++  java
  • 2016012034+小学四则运算练习软件项目报告

    代码仓库地址:https://git.coding.net/Siamese_miao/fourArithmetic.git

    测试效果见result.txt文件


     

    一、 需求分析与功能设计

    1.  使用JAVA编程语言。

    2.  接收一个输入参数n,随机产生n道四则运算练习题,符号用+-*÷来表示。

    3.  每个数字在 0 和 100 之间。

    4.  运算符在3个到5个之间并且每道练习题至少要包含2种运算符。

    5.  运算过程中不得出现负数与非整数。

    6.  将学号和生成的练习题及对应答案输出到文件“result.txt”中,不输出额外信息。

    附加需求

    1.  产生带括号的四则运算并求解,算式中存在的括号必须大于2个,且不得超过运算符的个数。

    2.  产生真分数的加减法运算,运算过程中都为最简真分数。

    基本功能

        能够根据用户输入的参数n随机产生n道符合要求的四则混合运算练习题,自动算出答案,并将式子与答案以文档的形式呈现。

    扩展功能    

        支持有括号的运算、支持最简真分数的加减运算


    二、 设计实现

        首先,我从简单四则混合运算开始,接着在这的基础上完成了分数运算,最后再思考带括号的运算。我先按这个顺序分别创建了项目,单独编写,最后才分类规整到一个项目中,如图我分为5个类。

    • Main类:主类,负责接收命令行的参数并启动程序。
    • fileCreate类:创建文件类,负责每次出题类型并产生result.text文件,将学号与练习题写入文件。
    • formula类:式子类,负责根据调用产生同种类型的式子,含有arithmetic(简单四则运算)、bracket(带括号的四则运算)、score(真分数加减运算)三种函数。
    • calculate类:计算类,负责各种计算,含有结果运算、有条件产生减数、有条件产生除数、有条件产生分子、有条件产生分母、判断数的大小、求取最大公因数、求取最小公倍数、分数相加、分数相减等10个方法。

       各类与各函数之间的关系如下图。


    三、 算法详解

        我的算法大多数比较简单,多采用递归的方式。

    • 简单四则运算

        由于符号由+-*÷表示,而计算机计算使用+-*/,所以我先定义了两个符号数组与定义两个字符串,一个作为显示式子用,一个用于计算,数组下标一一对应。再由范围为3~5的随机数确定式子的符号数,再由两个整型数组存储算数与符号对应的下标值,值皆由随机数产生,算数范围为0~100,下标值范围为0~3。式子使用for循环,每次将一个算数与一个符号加入字符串中,执行完循环后,最后再加上最后一个算数。计算后,连同式子一并输出。在计算时,我使用java调用js中的eval(String)函数求解。因为式子必须包含两种运算符以上,在输出前需要判断是否所有符号相同,不相同则不输出并重新运行程序。

     

     1 // 计算结果
     2     public static Object result(String temp) {
     3         ScriptEngineManager sem = new ScriptEngineManager();
     4         ScriptEngine se = sem.getEngineByName("js");
     5         Object last = 0;
     6         try {
     7             last = se.eval(temp);
     8         } catch (ScriptException e) {
     9             e.printStackTrace();
    10         }
    11         return last;
    12     }
    计算结果

       

        考虑到运算过程中不能出现小数与负数,所以在除号与减号部分需要做处理,当减数大于被减数时,重新生成减数直至不大于被减数,当除数无法整除被除数或为0时,重新生成除数。后来在测试时发现两两数字符合规则,然而整条式子却仍会出现负数与小数。经过思考,一般出现于连减、连除、减号后乘除因优先级不同的式子中,所以我设了个判断,减号后一位只能为加号,除号后一位只能为加减号,由此解决了运算过程中出现负数或小数的问题。

     

     1 public static String arithmetic() {
     2         int m, j;
     3         char[] p = new char[] { '*', '+', '÷', '-' };
     4         char[] q = new char[] { '*', '+', '/', '-' };
     5         String temp1 = "";
     6         String temp2 = "";
     7         m = (int) (Math.random() * 3 + 3); // 符号数
     8         int[] num = new int[m + 1]; // 数字
     9         int[] key = new int[m]; // 符号所在的下标
    10         for (j = 0; j <= m; j++) {
    11             num[j] = (int) (Math.random() * 101);
    12         }
    13         for (j = 0; j < m; j++) {
    14             if (j > 0 && key[j - 1] == 3) { // 减号后仅允许加号,防止负数出现
    15                 key[j] = 1;
    16             } else if (j > 0 && key[j - 1] == 2) {
    17                 key[j] = (int) (Math.random() * 2); // 除号后仅允许乘号与加号,防止负数
    18             } else {
    19                 key[j] = (int) (Math.random() * 4); // 随机符号
    20             }
    21             temp1 += String.valueOf(num[j]) + String.valueOf(p[key[j]]);
    22             temp2 += String.valueOf(num[j]) + String.valueOf(q[key[j]]);
    23             if (key[j] == 3) {
    24                 num[j + 1] = calculate.decide1(num[j], num[j + 1]); // 选定小于被减数的减数
    25             } else if (key[j] == 2) {
    26                 num[j + 1] = calculate.decide2(num[j], num[j + 1]); // 确保能够整除
    27             }
    28         }
    29         j = 0;
    30         while (j < (m - 1) && key[j] == key[j + 1])
    31             j++; // 与第一个符号相同数
    32         if (j == (m - 1))
    33             return arithmetic(); // 若所有符号相同,该式子不算,保证有两种运算符
    34         else {
    35             temp1 += String.valueOf(num[m]);
    36             temp2 += String.valueOf(num[m]);
    37             return temp1 + "=" + calculate.result(temp2);
    38         }
    39     }
    简单四则运算
    • 分数的加减

        在基本混合运算的基础上修改出分数加减就显得简单许多。最主要的就是分数的通分约分,解决了这个问题,程序就基本完成了。最大公因数我之前使用了for循环挨个计算,今天(3月28日)我想起了辗转相除法,修改之后,运算速度明显提高。

       

     1     public static int gcd(int x, int x2) {
     2         int s = 1;
     3         x = Math.abs(x);
     4         x2 = Math.abs(x2);
     5         while (x2 != 0) {
     6             s = x % x2;
     7             x = x2;
     8             x2 = s;
     9         }
    10         return x;
    11     }
    求最大公因数(辗转相除法)

     

    • 含括号的运算

        这部分由于优先级的改变以及时间关系,经过改善,目前已经基本确保运算结果不含小数与负数。

     

    1 // 判断是否为小数
    2     public static boolean judgeIsDecimal(String num) {
    3         boolean isdecimal = false;
    4         if (num.contains(".")) {
    5             isdecimal = true;
    6         }
    7         return isdecimal;
    8     }
    判断小数

     

        我思考了很久如何随机生成括号,最后受到一篇博客(当时忘记保存网址,现在已经找不到了,抱歉)使用概率的启发,我以一定概率的方式生成左括号,记录生成左括号的个数以及未匹配的左括号的个数,以一定概率生成右括号,最后补齐。

     

     1 if (((brack * 2) <= (n - 1)) && (((int) (Math.random() * 2)) == 0)) // 以一定概率生成左括号,概率为1/2
     2             {
     3                 temp1 += "(";
     4                 temp2 += "(";
     5                 brack++;
     6                 brack_left++;
     7                 temp1 += num[++i]; // 生成左括号后必须生成一个数字和运算符,不然可能出现(15)这样的错误
     8                 temp2 += num[i];
     9                 op = (int) (Math.random() * 4);
    10                 temp1 += Op[op];
    11                 temp2 += p[op];
    12                 if (op == 3)
    13                     div = 1;
    14                 else if (op == 1)
    15                     div = 2;
    16             }
    概率生成左括号

     


    四、 测试运行

    进入src文件下,输入javac -encoding utf-8 Main.java 编译出相应的class文件,再输入java Main 20进行测试,我们可以先测试java Main abc或java Main 1500或java Main 0,在这里我使用的jdk版本为jdk1.8.0_25。

    测试结果如下图。

    除此之外,我还学习了使用myeclipse做单元测试,测试结果如图所示,我从测试结果发现,当文件存在时,删除重建会耗时约2秒(以5道算式为例)。

     


     

    五、代码片段

        分数的加减计算因为没有优先级的关系,可以一边补充式子一边计算从而修改算数,无需限定符号。这也是我最满意的代码。

     

     1 // 真分数分式
     2     public static String score() {
     3         char[] p = new char[] { '+', '-' };
     4         int j;
     5         String temp1 = "";
     6         int m = (int) (Math.random() * 3 + 3);
     7         int[] key = new int[m]; // 运算符
     8         int[] x = new int[m + 1]; // 分子
     9         int[] y = new int[m + 1]; // 分母
    10         int[] sum = new int[2];// 中途运算结果
    11         for (j = 0; j <= m; j++) {
    12             x[j] = (int) (Math.random() * 20 + 1);
    13             y[j] = calculate.decide3(x[j]);
    14         }
    15         sum[0] = x[0];
    16         sum[1] = y[0];
    17         for (j = 0; j < m; j++) {
    18             key[j] = (int) (Math.random() * 2);
    19             if (key[j] == 0) { // 结果小于1
    20                 int[] num = new int[2];
    21                 num = calculate.fracAdd(sum[0], sum[1], x[j + 1], y[j + 1]);
    22                 if (num[0] >= num[1]) {
    23                     key[j] = 1;
    24                 } else {
    25                     sum = num;
    26                 }
    27             }
    28             if (key[j] == 1) { // 结果不为负数
    29                 int[] num = new int[2];
    30                 num = calculate.fracSub(sum[0], sum[1], x[j + 1], y[j + 1]);
    31                 if (num[0] < 0) {
    32                     x[j + 1] = calculate.decide4(sum[0], sum[1]);
    33                     y[j + 1] = sum[1];
    34                     num = calculate.fracSub(sum[0], sum[1], x[j + 1], y[j + 1]);
    35                 }
    36                 sum = num;
    37             }
    38             temp1 += String.valueOf(x[j]) + "/" + String.valueOf(y[j]) + String.valueOf(p[key[j]]);
    39         }
    40         j = 0;
    41         while (j < (m - 1) && key[j] == key[j + 1])
    42             j++; // 与第一个符号相同数
    43         if (j == (m - 1))
    44             return score(); // 若所有符号相同,该式子不算,保证有两种运算符
    45         else {
    46             temp1 += String.valueOf(x[m]) + "/" + String.valueOf(y[m]);
    47             return temp1 + "=" + sum[0] + "/" + sum[1];
    48         }
    49     }
    真分数运算

     


     

    六、 总结

        一开始由于我不清楚我能否完成附加功能,所以我用一个Main类的主函数完整的写一个简单的四则运算。但是调试时花费了许多时间,并且十分不灵活,所以我把函数中重复的计算部分抽离出来,写成静态方法,再进行测试,调试起来方便许多,想要修改哪一个部分的代码只需要在相应的函数中修改即可。同理,分数运算与括号运算我也这么做,由此我创建了三个项目。当三个项目都差不多完成时,我意识到其中有许多重复之处,所以我将它们分类并合在一个项目中。

        除了主类包含了一个主函数外,我把我的程序分成3个类,各自分工,每个类中我尽量把各个功能细分成各种小方法,尤其在calculate类中每个方法不超过10行。具体可看第三点的关系图。方法间的逐级调用给调试和测试都带来了很多便利,尤其在测试优化方面,同时也增加了代码的可移植性和可读性。


     

    七、 PSP

    PSP2.1

    任务内容

    计划共完成需要的时间(min)

    实际完成需要的时间(min)

    Planning

    计划

    30

    20

    ·        Estimate

    ·   估计这个任务需要多少时间,并规划大致工作步骤

    30

    20

    Development

    开发

    1515

    2470

    ·        Analysis

    ·         需求分析 (包括学习新技术)

    180

    150

    ·        Design Spec

    ·         生成设计文档

    50

    30

    ·        Design Review

    ·         设计复审 (和同事审核设计文档)

    10

    15

    ·        Coding Standard

    ·         代码规范 (为目前的开发制定合适的规范)

    5

    5

    ·        Design

    ·         具体设计

    10

    15

    ·        Coding

    ·         具体编码

    1200

    1800

    ·        Code Review

    ·         代码复审

    30

    15

    ·        Test

    ·         测试(自我测试,修改代码,提交修改)

    30

    440

    Reporting

    报告

    75

    170

    ·         Test Report

    ·         测试报告

    10

    15

    ·         Size Measurement

    ·         计算工作量

    5

    5

    ·         Postmortem & Process Improvement Plan

    ·         事后总结, 并提出过程改进计划

    60

    150

        这次的代码我思考了很久,在敲代码与测试方面远远的超过了计划的时间,源于我低估了代码的复杂度。虽然有思路,但把思路实现却花费了特别多的时间,思虑的不充分总是让程序报错,再加上我对java掌握的不够,所以我在编程花费了许多许多时间,有很多时候就是不知如何实现需求。再者,打这篇博客报告也花费了很多时间,但也再次整体的梳理了我的思路。

        在这次打代码的过程中,我学到了四个方法。

        一个是使用java调用js的eval()函数,这个方法可以输入字符串型的算式然后直接算出答案。

        另外一个是在用命令运行符使用Java Main 20作为命令时,应使用

     

    1 n=Integer.parseInt(args[0]);

     

    来获取输入的参数,而我在myeclipse使用的一直是

     

    1 Scanner in = new Scanner(System.in);
    2 n=in.nextInt();

     

    语句执行。

       还有一个是以概率的方式随机生成括号。

       最后是使用Debug修改错误代码,使用单元测试优化程序提高性能。


     

       这次的学习让我意识到自己的不足,我以后会继续努力。

  • 相关阅读:
    原生ajax书写
    java 中的instanceof
    Leetcode-Python3
    快速读入挂
    HDU 6044 Limited Permutation(2017多校)【计数 快速读入挂 线性逆元】
    HDU 6015 Colorful Tree(2017多校)
    HDU 6034 【贪心】
    POJ 3415 Common Substrings 【长度不小于 K 的公共子串的个数】
    POJ Football Game 【NIMK博弈 && Bash 博弈】
    2018
  • 原文地址:https://www.cnblogs.com/maomao-miao/p/8641912.html
Copyright © 2011-2022 走看看