用Java实现支持科学计算的数学公式运算类
通过对现有的后序表达式计算方法,经过稍许改动实现支持科学计算
废话不多说,直接上代码
计算类代码如下:
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class ExpressionEvaluationClass {
// 此方法用于将算术表达式解析成包含操作数和操作符的链表
public static List<String> Parser(String expression) {
List<String> list = new ArrayList<String>();// 用于存储表达式的链表
expression = PiChange(expression);//处理π
expression = EChange(expression);//处理e
//判断负数,并将-号转换为负数符号
if (expression.charAt(0) == '-') {
StringBuilder sb = new StringBuilder(expression);
sb.setCharAt(0, 'f');
expression = sb.toString();
}
String newExperssion = expression.replace("(-", "(f");
//将符号值转化为计算代号
newExperssion = newExperssion.replace("lg", "G");
newExperssion = newExperssion.replace("log", "g");
newExperssion = newExperssion.replace("ln", "L");
newExperssion = newExperssion.replace("sin", "S");
newExperssion = newExperssion.replace("cos", "C");
newExperssion = newExperssion.replace("tan", "T");
newExperssion = newExperssion.replace("arcSin", "i");
newExperssion = newExperssion.replace("arcCos", "c");
newExperssion = newExperssion.replace("arcTan", "t");
newExperssion = newExperssion.replace("cot", "o");
newExperssion = newExperssion.replace("sec", "e");
newExperssion = newExperssion.replace("csc", "s");
newExperssion = newExperssion.replace("!", "!1");
newExperssion = newExperssion.replace("°", "°1");
//取出表达式中的空格符号
newExperssion = newExperssion.replace("\u00A0", "");
newExperssion = newExperssion.replaceAll("\\u00A0+", "");
String str = "";
for (int i = 0; i < newExperssion.length(); i++) {
char c = newExperssion.charAt(i);
if (Character.isDigit(c) || c == '.') {
str += c;
} else {
if (str.length() > 0) {// 此判断是因为有+(这种符号相连的情况
//System.out.println(str);
list.add(str);
str = "";
}
//System.out.println(c);
list.add(String.valueOf(c));
}
}
if (str.length() > 0) {// 此判断是因为可能表达式不是以=结尾
//System.out.println(str);
list.add(str);
str = "";
}
return list;
}
// 此类用于将中序表达式转译成后序表达式
public static List<String> Trans(List<String> list) {
Stack<String> stack;// 用于存储操作符的栈
List<String> postfixList;// 用于存储后序表达式的链表
stack = new Stack<String>();
postfixList = new ArrayList<String>();
for (String str : list) {
// 这个分支是当前项是操作符号的情况
if (str.equals("+") || str.equals("-") || str.equals("*") || str.equals("/") || str.equals("%") || str.equals("(") || str.equals(")") ||
str.equals("g") || str.equals("L") || str.equals("G") || str.equals("S") || str.equals("C") || str.equals("T") || str.equals("i") ||
str.equals("c") || str.equals("t") || str.equals("o") || str.equals("e") || str.equals("s") || str.equals("!") || str.equals("f")
|| str.equals("√") || str.equals("°") || str.equals("^")) {
String opThis = str;
if (stack.size() == 0) {
// 如果栈为空,直接把操作符推入栈
stack.push(opThis);
} else if (str.equals("(")) {
// 如果操作符是左括号,直接推入栈
stack.push(opThis);
} else if (str.equals(")")) {
// 如果操作符是右括号,则往前找左括号,将左括号之后的操作符放到后续表达式列表中
while (stack.peek().equals("(") == false) { // stack.peek()是取栈顶元素而不弹出
postfixList.add(stack.pop());
}
stack.pop();// 左括号丢弃,由此完成了去括号的过程
} else {
// 看栈顶元素,如果它优先级大于等于当前操作符的优先级,则弹出放到后续表达式列表中
while (stack.size() > 0 && (getOpLevel(stack.peek()) >= getOpLevel(opThis))) {
postfixList.add(stack.pop());
}
stack.push(opThis);// 当前操作符入栈
}
} else {
// 这个分支是当前项是操作数的情况
postfixList.add(str);// 操作数直接入栈
}
}
// 将栈中余下的操作符弹出放到后续表达式列表中
while (stack.size() > 0) {
String opTop = stack.pop();
postfixList.add(opTop);
}
return postfixList;
}
// 取得操作符的等级
static int getOpLevel(String op) {
if (op.equals("+") || op.equals("-")) {
return 0;
} else if (op.equals("*") || op.equals("/") || op.equals("%")) {
return 1;
} else if (op.equals("g") || op.equals("L") || op.equals("G") || op.equals("S") || op.equals("C") || op.equals("T") || op.equals("i") ||
op.equals("c") || op.equals("t") || op.equals("o") || op.equals("e") || op.equals("s") || op.equals("!")) {
return 2;
} else if (op.equals("√") || op.equals("°") || op.equals("^")) {
return 3;
}else if(op.equals("f")) {
return 4;
}
return -1;
}
// 此方法用于计算后续表达式的值 调用顺序4
public static String Calculator(List<String> list) {
Stack<String> stack = new Stack<String>();
for (String str : list) {//遍历
// 这个分支是当前项是操作符号的情况
if (str.equals("+") || str.equals("-") || str.equals("*") || str.equals("/") || str.equals("%") || str.equals("(") || str.equals(")") ||
str.equals("g") || str.equals("L") || str.equals("G") || str.equals("S") || str.equals("C") || str.equals("T") || str.equals("i") ||
str.equals("c") || str.equals("t") || str.equals("o") || str.equals("e") || str.equals("s") || str.equals("!") || str.equals("f")
|| str.equals("√") || str.equals("°") || str.equals("^")) {
double result = 0;
if (str.equals("+")) {
double op2 = Double.valueOf(stack.pop());
double op1 = Double.valueOf(stack.pop());
result = op1 + op2;
} else if (str.equals("-")) {
double op2 = Double.valueOf(stack.pop());
double op1 = Double.valueOf(stack.pop());
result = op1 - op2;
} else if (str.equals("*")) {
double op2 = Double.valueOf(stack.pop());
double op1 = Double.valueOf(stack.pop());
result = op1 * op2;
} else if (str.equals("/")) {
double op2 = Double.valueOf(stack.pop());
double op1 = Double.valueOf(stack.pop());
result = op1 / op2;
} else if (str.equals("%")) {
double op2 = Double.valueOf(stack.pop());
double op1 = Double.valueOf(stack.pop());
result = op1 % op2;
} else if (str.equals("f")) {//负数符号
double op = Double.valueOf(stack.pop());
result = -1 * op;
} else if (str.equals("g")) {//log
double op2 = Double.valueOf(stack.pop());
double op1 = Double.valueOf(stack.pop());
if (op1 <= 0) {
return "对数log的操作数须大于零";
}
if (op2 <= 0) {
return "对数log的操作数须大于零";
}
result = Math.log(op2) / Math.log(op1);
} else if (str.equals("L")) {//ln
double op = Double.valueOf(stack.pop());
if (op <= 0) {
return "对数ln的操作数须大于零";
}
result = Math.log(op);
} else if (str.equals("G")) {//lg
double op = Double.valueOf(stack.pop());
if (op <= 0) {
return "对数lg的操作数须大于零";
}
result = Math.log10(op);
} else if (str.equals("S")) {//sin
double op = Double.valueOf(stack.pop());
result = Math.sin(op);
} else if (str.equals("C")) {//cos
double op = Double.valueOf(stack.pop());
result = Math.cos(op);
} else if (str.equals("T")) {//tan
double op = Double.valueOf(stack.pop());
if (Math.abs(op % Math.PI * 2) == 1) {
return "tan取值有错误";
}
result = Math.tan(op);
} else if (str.equals("i")) {//arcsin
double op = Double.valueOf(stack.pop());
if (op > 1 || op < (-1)) {
return "arcSin的取值有错误";
} else {
result = Math.asin(op);
}
} else if (str.equals("c")) {//arccos
double op = Double.valueOf(stack.pop());
if (op > 1 || op < (-1)) {
return "arcCos的取值有错误";
} else {
result = Math.acos(op);
}
} else if (str.equals("t")) {//arctan
double op = Double.valueOf(stack.pop());
result = Math.atan(op);
} else if (str.equals("o")) {//cot
double op = Double.valueOf(stack.pop());
//角度
double so = Math.sin(op);
double co = Math.cos(op);
result = co / so;
} else if (str.equals("e")) {//sec
double op = Double.valueOf(stack.pop());
double ec = Math.cos(op);
if (ec == 0) {
return "sec取值有错误";
} else {
result = 1 / ec;
}
} else if (str.equals("s")) {//csc
double op = Double.valueOf(stack.pop());
double ss = Math.sin(op);
if (ss == 0) {
return "csc取值有错误";
} else {
result = 1 / ss;
}
} else if (str.equals("!")) {//阶乘
stack.pop();
double op1 = Double.valueOf(stack.pop());
if (op1 < 0) {
return "阶乘数有误";
}
result = getN(op1);
} else if (str.equals("√")) {//开方
double op2 = Double.valueOf(stack.pop());
double op1 = Double.valueOf(stack.pop());
//负值不能开偶次方
if ((op2 < 0 && op1 % 2 == 0 || op1 == 0)) {
return "负数不能开偶次方!";
}
if (op2 < 0) {
result = -Math.pow(-op2, 1 / op1);
} else {
result = Math.pow(op2, 1 / op1);
}
} else if (str.equals("°")) {//角度制转弧度制
stack.pop();
double op1 = Double.valueOf(stack.pop());
result = (op1) * Math.PI / 180;
} else if (str.equals("^")) {//幂
double op2 = Double.valueOf(stack.pop());
double op1 = Double.valueOf(stack.pop());
if (op2 > 0 && op2 < 1) {//幂次数大于零小于1,是开方操作
if (op1 < 0 && ((1 / op2) % 2 == 0)) {
return "负数不能开偶次方!";
}
if (op1 < 0) {
result = -Math.pow(-op1, op2);
}
} else {
result = Math.pow(op1, op2);
}
}
stack.push((new DecimalFormat("0.#####").format(result)));
} else {
// 如果是操作数先直接入栈
stack.push(str);
}
}
// 取得结果
return stack.peek();
}
private static double getN(double n) {//计算阶乘的方法
if (n == 0)
return 1;
double sum = 1.0;
for (int s = 1; s <= n; s++) {
sum *= s;
}
return sum;
}
private static String PiChange(String ss) {//π转换
String aa2 = ss;
int ssLast = ss.lastIndexOf('π');
if (ssLast >= 0) {
if (ssLast != 0 && ss.charAt(ssLast - 1) >= '0' && ss.charAt(ssLast - 1) <= '9') {//π不在首位并且前面一位是数字
StringBuilder sb = new StringBuilder(ss);
sb.setCharAt(ssLast, '*');//将π替换为*
sb.insert(ssLast + 1, Math.PI);//在*后面加上*Math.PI
aa2 = sb.toString();//转为字符串格式
} else {
StringBuilder sb = new StringBuilder(ss);
sb.setCharAt(ssLast, '\u00A0');
sb.insert(ssLast + 1, Math.PI + "");
aa2 = sb.toString();
aa2 = aa2.replace("\u00A0", "");
}
System.out.println(aa2);
while (aa2.lastIndexOf('π') >= 0) {
aa2 = PiChange(aa2);
}
}
return aa2;
}
private static String EChange(String ss) {//е转换
String aa2 = ss;
int ssLast = ss.lastIndexOf('е');
if (ssLast > 0) {
if (ssLast != 0 && ss.charAt(ssLast - 1) >= '0' && ss.charAt(ssLast - 1) <= '9') {//е不在首位并且前面一位是数字
StringBuilder sb = new StringBuilder(ss);
sb.setCharAt(ssLast, '*');//将е替换为*
sb.insert(ssLast + 1, Math.E);//在*后面加上*Math.E
aa2 = sb.toString();//转为字符串格式
} else {
StringBuilder sb = new StringBuilder(ss);
sb.setCharAt(ssLast, '\u00A0');
sb.insert(ssLast + 1, Math.E + "");
aa2 = sb.toString();
aa2 = aa2.replace("\u00A0", "");
}
while (aa2.lastIndexOf('е') >= 0) {
aa2 = EChange(aa2);
}
}
return aa2;
}
}
怎么使用:
try {
List<String> list = ExpressionEvaluationClass.Parser("-2.5+sin(π/2)/9!+2log8+lg10+(-3)√(-9)");//将算术表达式解析成包含操作数和操作符的链表
<String> list2 = ExpressionEvaluationClass.Trans(list);//构建后序表达式
System.out.println("计算结果为:"+ExpressionEvaluationClass.Calculator(list2));//计算后序表达式
} catch (Exception e) {
e.printStackTrace();
System.out.println("请检查表达式是否正确");
}
值得注意:
运算符优先级如下:
加、减优先级为0
乘、除、%优先级为1
对数、三角函数、阶乘符号优先级为2
开方、幂运算、度优先级为3
负号优先级为4
负数只支持表达式首位添加负号如:-5+6 ,和在括号内加负号:sin(-5)
三角函数中反三角函数写法为arcSin,三角函数可不加括号,但需要考虑运算符号的优先级,三角函数前不允许直接添加数字正确写法:2*sinπ
开方负号√为双目运算符,第一位为开N次方,第二位为被开方数:2√4
log也是双目运算符,第一位为底数,第二位为真数:2log8
lg、ln为单目运算符:lg10;
表达式中的多数方法在进行计算时,会转换为计算代号:
//将符号值转化为计算代号
newExperssion = newExperssion.replace("lg", "G");
newExperssion = newExperssion.replace("log", "g");
newExperssion = newExperssion.replace("ln", "L");
newExperssion = newExperssion.replace("sin", "S");
newExperssion = newExperssion.replace("cos", "C");
newExperssion = newExperssion.replace("tan", "T");
newExperssion = newExperssion.replace("arcSin", "i");
newExperssion = newExperssion.replace("arcCos", "c");
newExperssion = newExperssion.replace("arcTan", "t");
newExperssion = newExperssion.replace("cot", "o");
newExperssion = newExperssion.replace("sec", "e");
newExperssion = newExperssion.replace("csc", "s");
newExperssion = newExperssion.replace("!", "!1");
newExperssion = newExperssion.replace("°", "°1");
本人利用Android studio编写了一个计算器小程序,一来可以更方便的输入表达式计算,二来可以通过按键限制输入的字符,减少表达式出现错误的可能。