|
|
import java.util.*;
|
|
|
|
|
|
/**
|
|
|
* 数学表达式计算器
|
|
|
* 支持基本四则运算、括号、平方、开根号和三角函数
|
|
|
*/
|
|
|
public class ExpressionEvaluator {
|
|
|
|
|
|
/**
|
|
|
* 计算数学表达式的值
|
|
|
*/
|
|
|
public static double evaluate(String expression) {
|
|
|
try {
|
|
|
// 预处理表达式:处理特殊符号
|
|
|
expression = preprocessExpression(expression);
|
|
|
|
|
|
// 使用栈和递归下降解析器计算
|
|
|
return parseExpression(expression);
|
|
|
} catch (Exception e) {
|
|
|
System.err.println("计算表达式失败: " + expression + " - " + e.getMessage());
|
|
|
e.printStackTrace();
|
|
|
return 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 预处理表达式:将特殊符号转换为可计算的形式
|
|
|
*/
|
|
|
private static String preprocessExpression(String expr) {
|
|
|
// 移除所有空格
|
|
|
expr = expr.replaceAll("\\s+", "");
|
|
|
|
|
|
// 处理三角函数:sin(30°) -> 计算实际值
|
|
|
expr = replaceTrigFunctions(expr);
|
|
|
|
|
|
// 处理平方符号:5² -> (5*5)
|
|
|
expr = replaceSquares(expr);
|
|
|
|
|
|
// 处理开根号:√25 -> 5
|
|
|
expr = replaceSqrts(expr);
|
|
|
|
|
|
return expr;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 替换三角函数为实际计算值
|
|
|
*/
|
|
|
private static String replaceTrigFunctions(String expr) {
|
|
|
// 处理 sin(角度°)
|
|
|
expr = replaceFunction(expr, "sin");
|
|
|
// 处理 cos(角度°)
|
|
|
expr = replaceFunction(expr, "cos");
|
|
|
// 处理 tan(角度°)
|
|
|
expr = replaceFunction(expr, "tan");
|
|
|
|
|
|
return expr;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 替换单个三角函数
|
|
|
*/
|
|
|
private static String replaceFunction(String expr, String funcName) {
|
|
|
String pattern = funcName + "\\((\\d+)°\\)";
|
|
|
java.util.regex.Pattern p = java.util.regex.Pattern.compile(pattern);
|
|
|
java.util.regex.Matcher m = p.matcher(expr);
|
|
|
|
|
|
StringBuffer sb = new StringBuffer();
|
|
|
while (m.find()) {
|
|
|
int degrees = Integer.parseInt(m.group(1));
|
|
|
double radians = Math.toRadians(degrees);
|
|
|
double result = 0;
|
|
|
|
|
|
switch (funcName) {
|
|
|
case "sin":
|
|
|
result = Math.sin(radians);
|
|
|
break;
|
|
|
case "cos":
|
|
|
result = Math.cos(radians);
|
|
|
break;
|
|
|
case "tan":
|
|
|
result = Math.tan(radians);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
// 替换为括号包裹的数值
|
|
|
m.appendReplacement(sb, "(" + result + ")");
|
|
|
}
|
|
|
m.appendTail(sb);
|
|
|
|
|
|
return sb.toString();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 替换平方符号
|
|
|
*/
|
|
|
private static String replaceSquares(String expr) {
|
|
|
java.util.regex.Pattern p = java.util.regex.Pattern.compile("(\\d+)²");
|
|
|
java.util.regex.Matcher m = p.matcher(expr);
|
|
|
|
|
|
StringBuffer sb = new StringBuffer();
|
|
|
while (m.find()) {
|
|
|
int num = Integer.parseInt(m.group(1));
|
|
|
int result = num * num;
|
|
|
m.appendReplacement(sb, String.valueOf(result));
|
|
|
}
|
|
|
m.appendTail(sb);
|
|
|
|
|
|
return sb.toString();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 替换开根号
|
|
|
*/
|
|
|
private static String replaceSqrts(String expr) {
|
|
|
java.util.regex.Pattern p = java.util.regex.Pattern.compile("√(\\d+)");
|
|
|
java.util.regex.Matcher m = p.matcher(expr);
|
|
|
|
|
|
StringBuffer sb = new StringBuffer();
|
|
|
while (m.find()) {
|
|
|
int num = Integer.parseInt(m.group(1));
|
|
|
double result = Math.sqrt(num);
|
|
|
m.appendReplacement(sb, String.valueOf(result));
|
|
|
}
|
|
|
m.appendTail(sb);
|
|
|
|
|
|
return sb.toString();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 解析并计算表达式(支持运算符优先级)
|
|
|
*/
|
|
|
private static double parseExpression(String expr) {
|
|
|
return parseAddSubtract(expr, new int[]{0});
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 解析加减法(最低优先级)
|
|
|
*/
|
|
|
private static double parseAddSubtract(String expr, int[] pos) {
|
|
|
double left = parseMultiplyDivide(expr, pos);
|
|
|
|
|
|
while (pos[0] < expr.length()) {
|
|
|
char op = expr.charAt(pos[0]);
|
|
|
if (op == '+' || op == '-') {
|
|
|
pos[0]++;
|
|
|
double right = parseMultiplyDivide(expr, pos);
|
|
|
if (op == '+') {
|
|
|
left += right;
|
|
|
} else {
|
|
|
left -= right;
|
|
|
}
|
|
|
} else {
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return left;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 解析乘除法(中等优先级)
|
|
|
*/
|
|
|
private static double parseMultiplyDivide(String expr, int[] pos) {
|
|
|
double left = parseFactor(expr, pos);
|
|
|
|
|
|
while (pos[0] < expr.length()) {
|
|
|
char op = expr.charAt(pos[0]);
|
|
|
if (op == '*' || op == '/') {
|
|
|
pos[0]++;
|
|
|
double right = parseFactor(expr, pos);
|
|
|
if (op == '*') {
|
|
|
left *= right;
|
|
|
} else {
|
|
|
if (right != 0) {
|
|
|
left /= right;
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return left;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 解析因子(数字或括号表达式)
|
|
|
*/
|
|
|
private static double parseFactor(String expr, int[] pos) {
|
|
|
// 跳过空格
|
|
|
while (pos[0] < expr.length() && expr.charAt(pos[0]) == ' ') {
|
|
|
pos[0]++;
|
|
|
}
|
|
|
|
|
|
// 处理括号
|
|
|
if (pos[0] < expr.length() && expr.charAt(pos[0]) == '(') {
|
|
|
pos[0]++; // 跳过 '('
|
|
|
double result = parseAddSubtract(expr, pos);
|
|
|
pos[0]++; // 跳过 ')'
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
// 处理负号
|
|
|
boolean negative = false;
|
|
|
if (pos[0] < expr.length() && expr.charAt(pos[0]) == '-') {
|
|
|
negative = true;
|
|
|
pos[0]++;
|
|
|
}
|
|
|
|
|
|
// 解析数字
|
|
|
int start = pos[0];
|
|
|
while (pos[0] < expr.length() &&
|
|
|
(Character.isDigit(expr.charAt(pos[0])) || expr.charAt(pos[0]) == '.')) {
|
|
|
pos[0]++;
|
|
|
}
|
|
|
|
|
|
double value = Double.parseDouble(expr.substring(start, pos[0]));
|
|
|
return negative ? -value : value;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 生成错误答案选项
|
|
|
*/
|
|
|
public static String[] generateWrongAnswers(double correctAnswer, int count) {
|
|
|
Set<String> wrongAnswers = new HashSet<>();
|
|
|
Random random = new Random();
|
|
|
|
|
|
while (wrongAnswers.size() < count) {
|
|
|
double wrongValue;
|
|
|
|
|
|
// 生成不同类型的错误答案
|
|
|
int type = random.nextInt(4);
|
|
|
switch (type) {
|
|
|
case 0: // 加减一个随机数
|
|
|
wrongValue = correctAnswer + (random.nextInt(20) - 10);
|
|
|
break;
|
|
|
case 1: // 乘以一个小数
|
|
|
wrongValue = correctAnswer * (0.5 + random.nextDouble());
|
|
|
break;
|
|
|
case 2: // 除以一个数
|
|
|
wrongValue = correctAnswer / (1.5 + random.nextDouble() * 2);
|
|
|
break;
|
|
|
default: // 完全随机
|
|
|
wrongValue = random.nextInt(100) + 1;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
String wrongAnswerStr = formatAnswer(wrongValue);
|
|
|
String correctAnswerStr = formatAnswer(correctAnswer);
|
|
|
|
|
|
if (!wrongAnswerStr.equals(correctAnswerStr)) {
|
|
|
wrongAnswers.add(wrongAnswerStr);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return wrongAnswers.toArray(new String[0]);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 格式化答案显示
|
|
|
*/
|
|
|
public static String formatAnswer(double answer) {
|
|
|
if (Math.abs(answer - Math.round(answer)) < 0.001) {
|
|
|
return String.valueOf(Math.round(answer));
|
|
|
} else {
|
|
|
return String.format("%.2f", answer);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 验证表达式语法是否正确
|
|
|
*/
|
|
|
public static boolean isValidMathExpression(String expression) {
|
|
|
// 基本语法检查
|
|
|
if (expression == null || expression.trim().isEmpty()) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 检查括号匹配
|
|
|
int balance = 0;
|
|
|
for (char c : expression.toCharArray()) {
|
|
|
if (c == '(') balance++;
|
|
|
if (c == ')') balance--;
|
|
|
if (balance < 0) return false;
|
|
|
}
|
|
|
if (balance != 0) return false;
|
|
|
|
|
|
// 检查运算符位置
|
|
|
String[] tokens = expression.split("\\s+");
|
|
|
for (int i = 0; i < tokens.length; i++) {
|
|
|
String token = tokens[i];
|
|
|
|
|
|
// 检查连续的运算符
|
|
|
if (isOperator(token) && i > 0 && isOperator(tokens[i-1])) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 检查数字后面直接跟左括号
|
|
|
if (i > 0 && isNumber(token) && i < tokens.length - 1 && "(".equals(tokens[i+1])) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 检查右括号后面直接跟数字
|
|
|
if (i > 0 && ")".equals(token) && i < tokens.length - 1 && isNumber(tokens[i+1])) {
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
private static boolean isOperator(String token) {
|
|
|
return "+".equals(token) || "-".equals(token) || "*".equals(token) || "/".equals(token);
|
|
|
}
|
|
|
|
|
|
private static boolean isNumber(String str) {
|
|
|
try {
|
|
|
Double.parseDouble(str);
|
|
|
return true;
|
|
|
} catch (NumberFormatException e) {
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
}
|