四则运算四

(结对伙伴:毛雯雯 20143066 编程和撰写博客的过程在一个笔记本上 所以该博客是以及程序是两个人合作的结果 )

一:作业要求:

1、 生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1 − e2的子表达式,那么e1 ≥ e2。

2、生成的题目中如果存在形如e1 ÷ e2的子表达式,那么其结果应是真分数。

3、每道题目中出现的运算符个数不超过3个,括号不限。

4、程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。

5、把程序变成一个网页程序, 用户通过设定参数,就可以得到各种题目,并可实现在线答题并评判。

二、设计思想:

首先,解决括号的问题(创建一个有括号的式子);其次,将这个式子放入栈(上次在网上查找到的栈的代码)中实现计算结果的目的,以实现评分的目的;然后,对减号,除号,运算符个数,设定参数重复性,网页版的问题进行解决。

  具体解决:

    创建式子:将数字和运算符放入两个数组中,随机生成符号的个数,随机生成数的个数,使用DFS实现括号的插入,插入的位置随机。

    计算:将生成的式子放入栈中计算。

    减号:(实现有错,暂未实现)使用遍历,遍历到减号,减号左右的保证左大于右(问题详细:连续减号和后面为乘除的情况)

    除号:(实现有错,暂未实现)使用遍历,遍历到除号,除号左右保证真分数(问题详细:连续除法不能保证)

    运算符个数:创建式子的时候,控制运算符的个数即可。

    设定参数:主要设置了范围,在随机生成运算数的时候设定范围(传参)

    网页版:(尝试,未实现,仅做出界面)使用JSP,不太会JAVA到JSP语句的转换。

三 源代码:

import java.util.Arrays;
import java.util.Scanner;
import java.util.Stack;   //
class CreateExp {
    private int a[];
    private char s[];
    private char sym[];
    private char brackets[];
    private int num[];

    public CreateExp() 
    {
        a = new int[100];
        s = new char[4];
        s[0] = '+';
        s[1] = '-';
        s[2] = '*';
        s[3] = '/';
        sym = new char[100];
        brackets = new char[100];
        num = new int[100];
    }

    public void dfs(int s, int e)// 使用dfs递归添加括号
    {
        if ((int) (Math.random() * 4) == 0) // 四分之一的概率,不进行任何操作
        {
            return;
        }
        if (e - s <= 1)// 只有一个元素或没有元素,不进行任何操作
        {
            return;
        }
        int s1 = (int) (Math.random() * (e - s - 1)) + s;// 随机生成插入括号的位置
        int e1 = (int) (Math.random() * (e - s1)) + s1;
        while (s1 < s || e1 > e || s1 >= e1 || (s1 == s && e1 == e))// 避免无用括号
        {
            s1 = (int) (Math.random() * (e - s - 1)) + s;
            e1 = (int) (Math.random() * (e - s1)) + s1;
        }
        if (brackets[s1] == ')' || brackets[e1] == '(')
        {
            return;
        }
        brackets[s1] = '(';
        brackets[e1] = ')';
        num[s1]++;
        num[e1]++;
        dfs(s, s1 - 1);// 插入括号的左边几个元素
        dfs(e1 + 1, e);// 括号之间的几个元素
        dfs(s1, e1);// 括号右边的几个元素
    }

    String Create(int max,int min) 
    {
        int n;
        String ans;
        ans = "";
        n = (int) (Math.random() * (3)) + 2;
        ;
        Arrays.fill(brackets, '.');
        Arrays.fill(num, 0);
        
        for (int i = 1; i <= n; i++) 
        {
            a[i] = (int) ((max-min+1)*Math.random() ) + min;
            sym[i] = s[(int) (Math.random() * (4))];
        }
        /*for(int i = 1; i <= n; i++)
        {   
            int middle;
            if(sym[i]=='-'&&a[i]<a[i+1])
            {
                middle=a[i];
                a[i]=a[i+1];
                a[i+1]=middle;
            }
                
        }*/
        dfs(1, n);
        while ((brackets[1] == '(') && (num[1]-- != 0)) 
        {
            ans += '(';
        }
        ans += a[1] + "";
        for (int i = 2; i <= n; i++) 
        {
            ans += sym[i];
            while (brackets[i] == '(' && num[i]-- != 0) 
            {
                ans += '(';
            }
            ans += a[i] + "";
            while (brackets[i] == ')' && num[i]-- != 0) 
            {
                ans += ')';
            }
        }
        return ans;
    }
}
public class CalculatorUtil //计算  
{ 
    private Stack<Character> priStack = new Stack<Character>();// 操作符栈    
    private Stack<Integer> numStack = new Stack<Integer>();// 操作数栈    
   
    public double caculate(String str) 
    {  //计算  
        String temp;//临时存放读取的字符
        StringBuffer tempNum = new StringBuffer(); //  用来临时存放数字字符串(当为多位数时) 
        StringBuffer string = new StringBuffer().append(str);  //用来保存,提高效率 
       
        while (string.length() != 0) //当没有余留才计算完成
        {    
            temp = string.substring(0, 1);    
            string.delete(0, 1);  
           // 判断temp,当temp为操作符时 
            if (!isNum(temp)) 
            {   
                // 此时的tempNum内即为需要操作的数,取出数,压栈,并且清空tempNum   
                if (!"".equals(tempNum.toString())) 
                { 
                    // 当表达式的第一个符号为括号   
                    int num=Integer.parseInt(tempNum.toString());
                    numStack.push(num);
                    tempNum.delete(0, tempNum.length());    
                } 
             // 用当前取得的运算符与栈顶运算符比较优先级:若高于,则因为会先运算,放入栈顶;若等于,因为出现在后面,所以会后计算,所以栈顶元素出栈,取出操作数运算;   
                // 若小于,则同理,取出栈顶元素运算,将结果入操作数栈。   
            
                // 判断当前运算符与栈顶元素优先级,取出元素,进行计算(因为优先级可能小于栈顶元素,还小于第二个元素等等,需要用循环判断)   
                while (!compare(temp.charAt(0)) && (!priStack.empty())) { 
                    int a =  numStack.pop();  //第二个和第一个运算数  
                    int b =  numStack.pop();   
                    char ope = priStack.pop();    
                    int result = 0;  //运算结果 
                    switch (ope) 
                    {    
                    // 将操作结果放入操作数栈 
                        case '+':    
                            result = b + a;    
                            numStack.push(result);    
                            break;    
                        case '-':    
                            result = b - a;    
                            numStack.push(result);    
                            break;    
                        case '*':    
                            result = b * a;    
                            numStack.push(result);    
                            break;    
                        case '/':    
                            result = b / a;    
                            numStack.push(result);    
                            break;    
                    }    
    
                }
             // 判断当前运算符与栈顶元素优先级, 如果高,或者低于平,计算完后,将当前操作符号,放入操作符栈 
                if (temp.charAt(0) != '#') 
                {    
                    priStack.push(new Character(temp.charAt(0)));    
                 // 当栈顶为'(',而当前元素为')'时,则是括号内以算完,去掉括号
                    if (temp.charAt(0) == ')')
                    {
                        priStack.pop();    
                        priStack.pop();    
                    }    
                }    
            } 
            else    // 当为非操作符时(数字)
                // 将读到的这一位数接到以读出的数后(当不是个位数的时候)
                tempNum = tempNum.append(temp);  
        }    
        return numStack.pop();    
    }    
    //判断传入的字符是不是0-9的数字  
    private boolean isNum(String temp)
    {    
        return temp.matches("[0-9]");    
    }    
    
    //比较当前操作符与栈顶元素操作符优先级,如果比栈顶元素优先级高,则返回true,否则返回false  
     
    // 需要进行比较的字符  
    //比较结果 true代表比栈顶元素优先级高,false代表比栈顶元素优先级低  
    
    private boolean compare(char str) 
    {    
        // 当为空时,显然 当前优先级最低,返回高 
        if (priStack.empty()) 
        {    
            return true;    
        }    
        // 如果栈顶为'('显然,优先级最低,')'不可能为栈顶。
        char last = (char) priStack.lastElement();    
        if (last == '(') 
        {    
            return true;    
        }    
        switch (str) 
        {    
            case '#':    
                return false;  
            case '(':      
                return true;    
            case ')':    
                return false;    
            case '*': 
            {    
                if (last == '+' || last == '-')    
                    return true;    
                else    
                    return false;    
            }    
            case '/': 
            {    
                if (last == '+' || last == '-')    
                    return true;    
                else    
                    return false;    
            }     
            case '+':    
                return false;    
            case '-':    
                return false;    
            }    
            return true;    
        }    
    
    public static void main(String args[]) 
    {    
        /*CalculatorUtil operate = new CalculatorUtil();    
        double t = operate.caculate("(3+4*(4*10-15/9)#");      
        System.out.println(t);  */
        
        Scanner in=new Scanner(System.in);      
        System.out.println("请输入要输出的算式的个数:");
        int n=in.nextInt();
        
        System.out.println("请输入数据范围  请先输入上界 再输入下界");
        Scanner in1=new Scanner(System.in);
        int max1=in1.nextInt();
        int min1=in1.nextInt();
        
        int result;
        int count=0;
        
        for(int x=0;x<n;x++)
        {   
            CreateExp exp = new CreateExp()  ;
            CalculatorUtil cal =new CalculatorUtil() ;
            String expString = exp.Create(max1,min1);
            double ans = cal.caculate(expString+"#");
            System.out.println(expString+"=");
            System.out.println("请输入结果:");
             result=in.nextInt();
            if(result==ans)
            {
                System.out.println("结果正确!");
                count++;
            }
            if(result!=ans)
            {
                System.out.println("结果错误 答案为: "+ans);
            }
        }
        System.out.println("共做对"+count+"个,"+"做错"+(n-count)+"个。");
    }    
    
}

四、运行截图:

五。反思:本以为符号的个数少,可以直接用枚举的方法,但是,无法进行计算,进行评判,还是回来继续用了栈。另外,减号和除号的不太会处理,总是有错误!

我们一起研究了栈,想把大神的程序看懂移植过来,但是失败了。我们也打算用枚举,结果输出一直有错,最后不得不放弃这个偷懒的做法。

原文地址:https://www.cnblogs.com/3066405538a/p/5372026.html