2016012062+小学四则运算练习软件项目报告

小学四则运算练习软件报告

 本篇博客仍在更新修改中 ing

前言:因为这一周各方面的事情接踵而来,所以对于这次作业,除了问题本身的算法,时间对我来说也是一个很大的难题。对于问题的难度,我可以通过和同学的交流,通过博客,去学习,去积累,所以我用了我所有的闲散时间来完成这个作业,但是时间还是太紧迫了。每天早上醒来、晚上睡觉前,只要是可以利用的时间我都会想算法,想完成这个作业的思路,然后晚上忙完工作之后回寝第一件事也是打开电脑开始敲代码,再讨论、再学习。但是因为时间对于我来说真的太需要了,所以对于实现这个项目,我基本完成了最基本的功能,对于附加功能,还在不断敲代码中,因为马上要交作业了,所以我选择先把代码提交了,然后继续调试,再来修改博客。在这个项目没有满足所有的条件前,我是不会放弃的!

https://git.coding.net/Faith_suzy/Sizeyunsuan_.git

项目实施过程

需求分析:

 基本功能:

  • 程序可接收一个输入参数n,然后随机产生n道加减乘除(分别使用符号+-*÷来表示)练习题,每个数字在 0 和 100 之间,运算符在3个到5个之间。
  • 所出练习题至少要包含2种运算符,且运算过程中不得出现负数与非整数。
  • 练习题生成好后,将你的学号与生成的n道练习题及其对应的正确答案输出到文件“result.txt”中,不要输出额外信息,文件目录与程序目录一致。
  • 当程序接收的参数为4时,右图为一个输出文件示例。

附加功能:

  • 出题与求解正确答案过程中,支持有括号的运算式。其中需要注意的是,算式中存在的括号必须大于2个,且不得超过运算符的个数。
  • 支持真分数加减的出题与求解,在此过程中,保证运算时分数自动化简,且计算过程与结果都须为真分数。

 

功能设计: 

针对基本功能:

  • 根据基本功能的需求可知,实现一个基本的四则混合运算的出题和计算

针对扩展功能:

  • 对于括号的形成是一大难点,因为优先级的关系,所以括号的形成是否有意义是我们需要考虑的;其次,在运算中,因为有了括号,所以对优先级会有一个判断,这也是需要解决的问题
  • 对于真分数的运算,主要解决最简分式的问题,需要时刻将分数化为最简(即先求其gcd,再进行化简)

      

设计实现: 

设计包括你会有哪些类,这些类分别负责什么功能,他们之间的关系怎样?你会设计哪些重要的函数,关键的函数是否需要画出流程图?函数之间的逻辑关系如何?

首先解决最基本四则混合运算的问题。再仔细研究了最早提交的代码后,我发现同学们设计生成四则运算的思路都不一样,于我来说,每一种算法都很巧妙。我认为这是我值得去学习的,所以再研究了大家已上传的代码,也经常找璐瑶针对生成的算法进行讨论之后,我现阶段选取的算法结合了璐瑶和宇欣两个人的思路。我最开始根据宇欣提供的思路,根据自己进一步的算法,进行了编程,但是到后来发现在中缀表达式转后缀表达式并计算的时候,会出现莫名其妙的debug,然后让我很焦躁,但是也是在耐心的不断调bug,不过调了两天之后,我还是有地方没有弄清,对于式子的生成总是有问题,所以我重头再来,还是选择数组的方法,因为我一般不会选择用数组的方法进行计算,所以在这个过程中,我不断在学习,在编程的时候通过输出语句,不断地在程序运行之后让其显示每一步的运行结果,以便我再调整。

设计的类:

就一个Main类,和一个Lib类,Lib类里面定义了多个方法。

 Exam方法:通过两个数组生成运算式

InfixToSuffixExpression方法:中缀表达式转后缀表达式并计算(包含优先级等方法)

gcd:求最大公因数

random:便于求满足各个范围的随机数

printFile:输出文件

 

算法详解: 

在生成题目时,我选择的是数组生成。先定义两个数组,分别存放随机数与符号,这样在两个数组数据生成完之后,进行整合,就将其整形成了一个完整的含有3~5个运算符的式子。

在进行对题目的运算时,我选择运用的还是中缀表达式转后缀表达式并进行运算,这个算法我大概看了十几个博主写的关于中缀转后缀的博客,其中有代码,也有经验。我觉得这个过程是我在这次作业中学到的最大的知识。之前学数据结构的时候对栈的应用,只是知道可以进行运算式的求解,但是从来没有真正写过,所以这一次,再借鉴了其他人的经验和代码之后,我也尝试了自己写,先把这一部分的算法单独创建一个类,先保证它的正常运行,这样再之后的融合中,我就会先排除针对这个算法的debug。

 

对于括号的添加及优先级的运算,我自己有一个大致的思路,但是对于具体的程序还没有进行编程,因为时间实在是对我来说有点紧,我会在之后继续完善这一部分,我希望通过我的不断调试可以实现此附加功能。

对于真分数的运算,我认为比添加括号运算的思路更清晰一点,现阶段已完善真分数的生成,为了满足需求,在生成和计算的过程中需要一直保持最简,所以每一步都需要用到求gcd。之后的运算我也会在提交了博客之后,进行完善。也希望老师能够督促我,同时我自己也会在这个运算软件没有完全满足所有要求之前,继续研究,继续思考,继续调节。

 

算法详解: 

  • 建立两个数组,一个存放随机数,一个存放运算符,然后再将两个数组整合成一个完整的运算式。其中,对于减法,保证前一个数大于后一个数,使其不出现负数;对于除法,使前一个数为后一个数的倍数,以满足整除的需求。
                int N = rand(1,4);  //随机产生3~5个随机运算符
                int count = 0;
                for(int j=1;j<=N;j++){
                    count++;
                    int opselect=rand(0,3);  
                    if(opselect == 0){
                        op[j] = "+";
                        num[j+1] = rand(2,100);
                    }
                    else if(opselect == 1){  //保证两数相减大于零
                        op[j] = "-";
                        num[j+1] = rand(2,100);
                        if(num[j]<num[j+1]){
                            int Temp = num[j+1];
                            num[j] = num[j+1];
                            num[j+1] = Temp;
                        }
                    }        
                    else if(opselect == 2){
                        if(op[j-1] == "×" || op[j-1] == "÷"){
                            op[j] = "+";
                            num[j+1] = rand(2,10);
                        }
                        else{
                            op[j] = "×";
                            num[j+1] = rand(2,10);
                        }                            
                    }
                    else if(opselect == 3){  //保证整除
                        if(op[j-1] == "×" || op[j-1] == "÷"){
                            op[j] = "+";
                            num[j+1] = rand(2,10);
                        }
                        else{
                            op[j] = "÷";
                            num[j+1] = rand(2,10);
                            num[j] = num[j+1]*rand(2,10);
                        }
                    }
                }        
                String question=""+num[0];   //将生成的运算符整合成一个完整的运算式
                op[1]="+";
                for(int k=1;k<count;k++){
                    question+=op[k]+num[k+1];
                }
  • 为了完成运算,先后查找了关于中缀表达式转后缀表达式的算法,也看了很多博主的博客。虽然我现阶段还没有完成括号的添加和计算,但是在转为表达式和运算的时候,我还是先给定了对括号优先级的判断。

利用堆栈完成中缀表达式转后缀表达式:

  1. 遇到操作数:直接输出(添加到后缀表达式中)
  2. 栈为空时,遇到运算符,直接入栈
  3. 遇到左括号:将其入栈
  4. 遇到右括号:执行出栈操作,并将出栈的元素输出,直到弹出栈的是左括号,左括号不输出。
  5. 遇到其他运算符:加减乘除:弹出所有优先级大于或者等于该运算符的栈顶元素,然后将该运算符入栈
  6. 最终将栈中的元素依次出栈,输出。
public static int InfixToSuffixExpression(String Str){ //中缀表达式转后缀表达式并计算
        Stack<Integer> numStack = new Stack<Integer>();
        Stack<Character> opStack = new Stack<Character>();
        Stack<Object> suffixExpr = new Stack<Object>();
        int len = Str.length();
        char c,temp;
        int number;
        for(int i=0;i<len;i++)
        {
            c = Str.charAt(i);
            if(Character.isDigit(c))
            {
                int endDigitPos = getEndPosOfDigit(Str,i);
                number = Integer.parseInt(Str.substring(i,endDigitPos));
                i = endDigitPos - 1;
                numStack.push(number);
                if((int)number == number)
                {
                    suffixExpr.push(number);
                }
                else
                {
                    suffixExpr.push(number);
                }
            }
            else if(isOperator(c))  //操作符栈非空,且栈顶不是'(',且当前操作符优先级低于栈顶操作符  
            {
                while(!opStack.isEmpty()
                      && opStack.peek()!='('
                      && priorityCompare(c,opStack.peek())<=0)
                {
                    suffixExpr.push(opStack.peek());
                    numStack.push(calc(numStack,opStack.pop()));
                }
                opStack.push(c);
            }
            else if(c == '(')
            {
                opStack.push(c);
            }
            else if(c == ')')
            {
                while((temp = opStack.pop())!='(')
                {
                    numStack.push(calc(numStack,temp));
                    suffixExpr.push(temp);
                }
            }
            else if(c == ' ')
            {
                
            }
            else
            {
                throw new IllegalArgumentException("Wrong character '" + c + "'");
            }
        }
        
        while(!opStack.isEmpty())
        {
            temp = opStack.pop();
            suffixExpr.push(temp);
            numStack.push(calc(numStack,temp));
        }
        
        return numStack.pop();
//        printStack(suffixExpr);
//        System.out.println( Str + " = " + numStack.pop());
    }
InfixToSuffixExpression(中缀转后缀)

 

测试运行: 

 

 PSP

PSP2.1

任务内容

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

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

Planning

计划

10

5

Estimate

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

10

8

Development

开发

30*60min

15*60min

Analysis

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

10

15

Design Spec

生成设计文档

0

0

Design Review

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

0

0

Coding Standard

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

5

5

Design

具体设计

30

60

Coding

具体编码

6*60

8*60min

Code Review

代码复审

 

持续中

Test

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

 

240

Reporting

报告

180

120

Test Report

测试报告

 

 

Size Measurement

计算工作量

 

 

Postmortem & Process Improvement Plan

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

 

持续中

更深一步的总结:

       完成这个作业耗用了我很多时间,让我深知了自己的不足,但是与此同时,我也在同学的博客中学习到了很多知识,每一次打翻以前的思路重写代码的时候,都是一种历练、一种学习。虽然这个过程是非常难熬的,但是真正自己完成下来了还是有一定的成就感的,正是因为知道自己还有非常多的不足,所以每一次的作业完成后我也会不断修改,比其他人用更多的时间去完善、不怕这个过程的艰辛,我相信自己如果真正能够坚持,最好的效果会非常好!希望我所有的付出都值得,不求所有都能有回报,但求自己能有长进、能够更加向前。

To be continued: 这篇博客还在继续完善中ing

       在完善这篇博客期间,我看到了很多同学用的是js中eval函数,我觉得这个比以前的复杂的算法简单的多,所以在学习、借鉴了别人的代码之后,自己也按照同学博客里面介绍的写了代码。

    static ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");   //运用js
 private static ArrayList<String> calculate(ArrayList<String> arrayList) 
    {
        ArrayList<String> question = new ArrayList<String>();
        for (String nquestion : arrayList) 
        {
            try 
            {
                nquestion = nquestion + "=" + jse.eval(nquestion);
                System.out.println(nquestion);
                question.add(nquestion);
            }
            catch (ScriptException SE) 
            {
                // TODO Auto-generated catch block
                SE.printStackTrace();
            }
        }

        try {
            File test = new File("result.txt");
            FileWriter fw = new FileWriter(test);
            PrintWriter pw = new PrintWriter(fw);
            pw.println("2016012062");
            for (String answer : question) 
            {
                pw.println(answer);
            }
            fw.close();

        } catch (IOException IOE) {
            // TODO Auto-generated catch block
            IOE.printStackTrace();
        }
        return question;
    }

所以我也改了coding.net的代码,然后也会持续更改代码,用不同的方式解决。现在还不够好。

转载于:https://www.cnblogs.com/Faith-sy/p/8647310.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值