随机生成四则运算式及答案(含括号)
来源:互联网 发布:艾薇儿已经去世 知乎 编辑:程序博客网 时间:2024/06/05 15:38
【原创,转载请注明出处!】
记得我大二时候java课程的学期大作业有这样一题:
根据用户的选项,随机生成n道四则运算式,让用户回答,然后检验答案是否正确。用java的图形界面实现,也就是swing。
当时很困惑随机生成四则运算式该如何实现,如果按当时的思路的话,估计也就是一个数字一个符号这样one by one接下去。
上个星期有些空,然后突然想起了这个题目,然后在群里讨论了下,发现用二叉树来实现比较直观。
对于这样一棵二叉树,每个叶子节点都是数字,每个父节点都是符号,然后中序遍历结果就是我们所需要的四则运算,而且每次父节点返回的时候,可以根据符号进行运算式的计算,中序遍历的结果:
1+2-3*4
但是如何产生括号呢?
有一个简单的办法就是父节点每次返回的时候带上括号,于是运算式就变成了这样:
((1+2)-(3*4))
但是新的问题又来了,对于某些式子,比如这里1+2和3*4的括号是多余的,加上括号并不会改变运算式的计算顺序,对于这样的括号,应该要去掉。
一开始我想的太简单了,'*'、'/'的优先级是高于'+'、'-'的,那么如果运算符是'*'、'/'的话,直接把括号去掉就行了。结果当然是错的,因为没有把括号的优先级考虑进来,
正确的优先级应该是这样的:'('、')'>'*'、'/'>'+'、'-'
通过整理,我们可以得到这样的规律:
假设待去括号的表达式为 (m1 op1 n1) op (m2 op2 n2) 这里m1、n1、m2、m2可能自身就是个表达式,也可能是数字,op、op1、op2为运算符
1、若op为'/',则 (m2 op2 n2)的括号必须保留;
2、若op为'*'或'-',如果op2为'+'或'-',则(m2 op2 n2)的括号必须保留;
3、若op为'*'或'/',如果op1为'+'或'-',则(m1 op1 n1)的括号必须保留;
4、 除此之外,去掉括号不影响表达式的计算顺序。
【这里整理的去括号法则来源于互联网】
至此,我们就能得到一个比较随机的四则运算式。
这里为什么又是比较随机呢?
因为还有一些细节我们尚未考虑,比如除数为0,这里的除数为0有两种情况:
1、随机到的数字为0
2、除号后面的表达式的计算结果为0
对于情况1我们可以把这个数字再Random一下,但是对于情况2我们也要把表达式重新生成一遍吗?
一种比较偷懒也是比较简单的办法是,把除号随机成其他三种符号,这样就能避免除数为0的情况。
但是这样做也有不好的地方,就是符号的随机性被破坏了,也就是说除号出现的可能性降低了。
每个方法有利有弊,可以自己把握。
以上是我及我的小伙伴们的思路,仅供参考,或许有更好的生成方法,可以相互探讨。
下面是code time
BinaryTree.java
/** * * @author kuku713 * @version 1.0 * */import java.util.ArrayList;public class BinaryTree {private TreeNode root;private int num;private ArrayList<TreeNode> opeList = new ArrayList<TreeNode>();public BinaryTree(int num){this.num = num;}public int getNum(){return num;}public void setNum(int num){this.num = num;}public void setTreeNode(TreeNode root){this.root = root;}/** * 获取最终的表达式,必须在CalAndVal()方法后调用 * * @return str */public String toString(){String str = root.toString();str = str.substring(1, str.length()-1);return str;}/** * 计算并验证表达式 * * @return result */public String CalAndVal(){return root.getResult();}/** * 计算二叉树的深度(层数) * * @return deep */public int getDeep(){int i = this.num;int deep = 2;while(i/2 > 0){deep++;i /= 2;}return deep;}/** * 生成二叉树 * */public void createBTree(){TreeNode lchild, rchild, lnode, rnode;if(num == 1){lchild = new TreeNode(String.valueOf(Ran.getNumber(10)), null, null);rchild = new TreeNode(String.valueOf(Ran.getNumber(10)), null, null);root = new TreeNode(String.valueOf(Ran.getOperator()), lchild, rchild);}else{int num1 = 0;int n = getDeep() - 3;boolean[] place = Ran.getChildPlace(num);root = new TreeNode(String.valueOf(Ran.getOperator()), null, null);opeList.add(root);for(int i = 0; i < n; i++){for(int j = 0; j < (int)Math.pow(2, i); j++, num1++){lchild = new TreeNode(String.valueOf(Ran.getOperator()), null, null);rchild = new TreeNode(String.valueOf(Ran.getOperator()), null, null);opeList.get(j + num1).setChild(lchild, rchild);opeList.add(lchild);opeList.add(rchild);}}for(int i = 0; i < place.length; i++){if(place[i]){lnode = new TreeNode(String.valueOf(Ran.getNumber(10)), null, null);rnode = new TreeNode(String.valueOf(Ran.getNumber(10)), null, null);if(i%2 == 0){lchild = new TreeNode(String.valueOf(Ran.getOperator()), lnode, rnode);opeList.add(lchild);opeList.get(num1).setLchild(lchild);}else{rchild = new TreeNode(String.valueOf(Ran.getOperator()), lnode, rnode);opeList.add(rchild);opeList.get(num1).setRchild(rchild);}}else{if(i%2 == 0){lchild = new TreeNode(String.valueOf(Ran.getNumber(10)), null, null);opeList.get(num1).setLchild(lchild);}else{rchild = new TreeNode(String.valueOf(Ran.getNumber(10)), null, null);opeList.get(num1).setRchild(rchild);}}num1 = num1 + i%2;}}}}
Ran.java
/** * * @author kuku713 * @version 1.0 * */import java.util.Random;public class Ran {/** * 获取随机的符号 * * @return operator */public static char getOperator(){char operator = 0;Random ran = new Random();int i = ran.nextInt(4);switch(i){case 0:operator = '+';break;case 1:operator = '-';break;case 2:operator = '*';break;case 3:operator = '/';break;}return operator;}/** * 根据输入的范围获取随机数 * * @param max * @return number */public static int getNumber(int max){int number = 0;Random ran = new Random();number = ran.nextInt(max+1);return number;}/** * 根据运算符的个数随机产生子节点的位置 * * @param num * @return childPlace */public static boolean[] getChildPlace(int num){int d = 0;int size = 0, j=1;while(num >= (int)Math.pow(2, j)){j++;}d = (int)Math.pow(2, j) - 1 - num;size = (int)Math.pow(2, j-1);boolean[] k = new boolean[size];for(int i = 0; i < size; i++){k[i] = true;}for(int i = 0; i < d; i++){Random ran = new Random();int f = ran.nextInt(size);while(k[f] == false){f = ran.nextInt(size);}k[f] = false;}return k;}}
TreeNode.java
/** * * @author kuku713 * @version 1.0 * */public class TreeNode {private String str;private TreeNode rchild = null;private TreeNode lchild = null;public TreeNode(String str){this.str = str;}public TreeNode(String str, TreeNode lchild, TreeNode rchild){this.str = str;this.rchild = rchild;this.lchild = lchild;}public void setChild(TreeNode lchild, TreeNode rchild){this.lchild = lchild;this.rchild = rchild;}public TreeNode getRchild() { return rchild; } public void setRchild(TreeNode rchild) { this.rchild = rchild; } public TreeNode getLchild() { return lchild; } public void setLchild(TreeNode lchild) { this.lchild = lchild; }public String getStr(){return str;}/** * 获取每个节点的运算结果,并检验除法 * 1)除数为0 * 2)不能整除 * 出现以上两种情况的话将该运算符转换成其他三种运算符 * * @return result */public String getResult(){if(hasChild()){switch(str){case "+":return String.valueOf(Integer.parseInt(getLchild().getResult()) + Integer.parseInt(getRchild().getResult()));case "-":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();}elsereturn String.valueOf(Integer.parseInt(getLchild().getResult()) / Integer.parseInt(getRchild().getResult()));}}return str;} /** * 先对每个运算式添加括号,然后根据去括号法则,去掉多余的子式的括号 * * @return string */ 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; } public boolean hasChild(){ if(lchild == null && rchild == null) return false; else return true; }}
Test.java
/** * * @author kuku713 * @version 1.0 * */public class Test {public static void main(String args[]){BinaryTree bTree;for(int i = 0; i < 6; i++){bTree = new BinaryTree(2);bTree.createBTree();String result = bTree.CalAndVal();System.out.println(bTree.toString() + "=" + result);}}}
- 随机生成四则运算式及答案(含括号)
- 四则运算,含括号
- 含括号的四则运算
- 随机生成四则运算
- 随机生成四则运算
- 结对编程—四则运算随机生成
- 结对编程之四则运算随机生成
- 结对编程之四则运算随机生成
- Java 随机生成六位数验证码过程(含大小写字母及数字)
- 含括号和四则运算符的简单表达式的计算【c++ 50 lines】
- 简单四则运算计算器的C++实现(含括号和+-*/的优先级判断)
- 带括号的四则运算
- 逆波兰式实现四则运算表达式计算器支持括号、十六进制
- 随机四则运算生成器
- 带括号的四则运算 汇编
- C/C++带括号四则运算
- C/C++带括号四则运算
- linux运维初级课前实战随机考试题含答案(笔试+上机)
- Tutorial 14 - Camera Control - Part 1
- Android_ListView_Adapter使用和数据动态加载
- Python挑战的解决方案(1-10)
- 【白话经典算法系列之十七】 数组中只出现一次的数
- 飞鸽传书 桌面上常见的窗口:
- 随机生成四则运算式及答案(含括号)
- 【模拟】【数论】最大公约数和最小公倍数问题
- 多线程下的懒汉式和饿汉式
- 网站用户忠诚度
- [Anonymous] 给一个数组a[2n] ,将前后两段数组元素交叉排列, a1..anb1...bn 变成 a1b1...anbn
- jQuery Ajax事件-ajaxStart(callback)
- 老码农:如何写出让自己满意的代码
- 每天一小步——自写服务器与信号处理僵尸子进程
- 自己动手写ndk-gdb