package util; import generator.Problem; import java.util.*; public class ExpressionUtils { private static Random rand = new Random(); private static final String[] OPS_PRIMARY = {"+", "-", "*", "/"}; private static final String[] OPS_MIDDLE = {"+", "-", "*", "/"}; //private static final String[] OPS_HIGH = {"+", "-", "*", "/", "^2", "sqrt"}; public static String randomNumber() { return String.valueOf(rand.nextInt(100) + 1); } // --- 表达式求解和选项生成辅助方法 --- public static double solveExpression(String expr) { // 预处理表达式,确保格式正确 String parsableExpr = expr .replaceAll("(\\d+|\\([^)]+\\))\\^2", "pow($1, 2)") // 处理平方 .replaceAll("sqrt\\s+(\\d+)", "sqrt($1)") // 处理缺少括号的sqrt .replaceAll("\\s+", " "); // 标准化空格 try { return new Object() { int pos = -1, ch; void nextChar() { ch = (++pos < parsableExpr.length()) ? parsableExpr.charAt(pos) : -1; } boolean eat(int charToEat) { while (ch == ' ') nextChar(); if (ch == charToEat) { nextChar(); return true; } return false; } double parse() { nextChar(); double x = parseExpression(); if (pos < parsableExpr.length()) throw new RuntimeException("Unexpected: " + (char) ch); return x; } double parseExpression() { double x = parseTerm(); for (; ; ) { if (eat('+')) x += parseTerm(); // addition else if (eat('-')) x -= parseTerm(); // subtraction else return x; } } double parseTerm() { double x = parseFactor(); for (; ; ) { if (eat('*')) x *= parseFactor(); // multiplication else if (eat('/')) { // division double divisor = parseFactor(); if (divisor == 0) throw new ArithmeticException("Division by zero"); x /= divisor; } else return x; } } double parseFactor() { if (eat('+')) return parseFactor(); // unary plus if (eat('-')) return -parseFactor(); // unary minus double x; int startPos = this.pos; if (eat('(')) { // parentheses x = parseExpression(); eat(')'); } else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers while ((ch >= '0' && ch <= '9') || ch == '.') nextChar(); x = Double.parseDouble(parsableExpr.substring(startPos, this.pos)); } else if (ch >= 'a' && ch <= 'z') { // functions while (ch >= 'a' && ch <= 'z') nextChar(); String func = parsableExpr.substring(startPos, this.pos); x = parseFactor(); switch (func) { case "sqrt": x = Math.sqrt(x); break; case "sin": x = Math.sin(Math.toRadians(x)); break; case "cos": x = Math.cos(Math.toRadians(x)); break; case "tan": x = Math.tan(Math.toRadians(x)); break; case "pow": eat(','); double y = parseExpression(); x = Math.pow(x, y); eat(')'); break; default: throw new RuntimeException("Unknown function: " + func); } } else { throw new RuntimeException("Unexpected: " + (char) ch); } return x; } }.parse(); } catch (Exception e) { System.err.println("Error parsing expression: '" + expr + "'. Parsable version: '" + parsableExpr + "'. Error: " + e.getMessage()); return Double.NaN; } } private static String formatResult(double result) { if (Double.isNaN(result)) { return "Error"; } if (Math.abs(result - Math.round(result)) < 0.0001) { return String.valueOf((int) Math.round(result)); } return String.format("%.2f", result); } /** * MODIFICATION: 确保为整数答案生成整数干扰项 */ private static String generateDistractor(double correctResult, List existingOptions) { if (Double.isNaN(correctResult)) { return String.valueOf(rand.nextInt(100)); } // 检查正确答案是否为整数 boolean isIntegerResult = Math.abs(correctResult - Math.round(correctResult)) < 0.0001; double distractor; String distractorStr; do { if (isIntegerResult) { // 生成一个整数干扰项 int offset = rand.nextInt(20) + 1; // 扩大随机范围以避免重复 distractor = rand.nextBoolean() ? Math.round(correctResult) + offset : Math.round(correctResult) - offset; // 确保干扰项不为负数 if (distractor < 0) { distractor = Math.round(correctResult) + offset; } } else { // 答案是小数,则生成小数干扰项 distractor = correctResult + (rand.nextInt(10) + 1) * (rand.nextBoolean() ? 1 : -1) + (rand.nextDouble() * 0.9); } distractorStr = formatResult(distractor); } while (existingOptions.contains(distractorStr)); // 确保选项不重复 return distractorStr; } public static List generateOptions(double correctResult) { List options = new ArrayList<>(); options.add(formatResult(correctResult)); for (int i = 0; i < 3; i++) { options.add(generateDistractor(correctResult, options)); } Collections.shuffle(options); return options; } public static Problem createProblem(String expression) { double result = solveExpression(expression); // 如果解析失败,返回null,让调用者重新生成 if (Double.isNaN(result)) { return null; } String correctAnswerOption = formatResult(result); List options = generateOptions(result); return new Problem(expression, result, options, correctAnswerOption); } // --- 题目生成方法 --- private static int getDivisor(int dividend) { List divisors = new ArrayList<>(); for (int j = 1; j <= dividend; j++) { if (dividend % j == 0) { divisors.add(j); } } return divisors.get(rand.nextInt(divisors.size())); } /** * MODIFICATION: 增加答案非负、括号数量和操作数关联的逻辑 */ public static Problem generatePrimaryExpr() { Problem problem; double result; do { // 生成 2 到 4 个操作数 int operands = rand.nextInt(3) + 2; List parts = new ArrayList<>(); for (int i = 0; i < operands; i++) { if (i > 0) { parts.add(OPS_PRIMARY[rand.nextInt(OPS_PRIMARY.length)]); } // 仅当操作数 >= 3 时,才有可能生成括号 if (operands >= 3 && rand.nextBoolean() && i < operands - 1) { int num1 = rand.nextInt(50) + 1; String innerOp = OPS_PRIMARY[rand.nextInt(OPS_PRIMARY.length)]; int num2; if (innerOp.equals("/")) { num2 = getDivisor(num1); } else { num2 = rand.nextInt(50) + 1; } parts.add("(" + num1 + " " + innerOp + " " + num2 + ")"); i++; // 括号表达式计为2个操作数 } else { int num; if (i > 0 && parts.get(parts.size() - 1).equals("/")) { String dividendStr = parts.get(parts.size() - 2); if (dividendStr.startsWith("(")) { // 为避免 (a+b)/c 这种复杂情况无法保证整数结果,直接替换运算符 parts.set(parts.size() - 1, OPS_PRIMARY[rand.nextInt(3)]); // +, -, * num = rand.nextInt(100) + 1; } else { int dividend = Integer.parseInt(dividendStr); num = getDivisor(dividend); } } else { num = rand.nextInt(100) + 1; } parts.add(String.valueOf(num)); } } String expression = String.join(" ", parts); problem = createProblem(expression); result = problem.getResult(); // 循环直到答案为非负整数 } while (result < 0 || Double.isNaN(result) || Math.abs(result - Math.round(result)) > 0.0001); return problem; } public static Problem generateMiddleExpr() { int operands = rand.nextInt(5) + 1; StringBuilder expr = new StringBuilder(); boolean hasSquareOrSqrt = false; for (int i = 0; i < operands; i++) { if (i > 0) { expr.append(" ").append(OPS_MIDDLE[rand.nextInt(OPS_MIDDLE.length)]).append(" "); } int num = rand.nextInt(100) + 1; if (!hasSquareOrSqrt && rand.nextBoolean()) { expr.append(rand.nextBoolean() ? "sqrt(" + num + ")" : num + "^2"); hasSquareOrSqrt = true; } else { expr.append(num); } } if (!hasSquareOrSqrt) { expr.append(" + ").append(rand.nextInt(50) + 1).append("^2"); } return createProblem(expr.toString()); } public static Problem generateHighExpr() { int attempts = 0; final int maxAttempts = 10; while (attempts < maxAttempts) { int operands = rand.nextInt(3) + 2; // 2-4个操作数 StringBuilder expr = new StringBuilder(); boolean hasTrig = false; for (int i = 0; i < operands; i++) { if (i > 0) { String[] validOps = {"+", "-", "*", "/"}; String op = validOps[rand.nextInt(validOps.length)]; expr.append(" ").append(op).append(" "); } // 强制至少有一个操作数是三角函数 if (!hasTrig && (i == operands - 1 || rand.nextBoolean())) { String[] funcs = {"sin", "cos", "tan"}; String func = funcs[rand.nextInt(funcs.length)]; int angle = rand.nextInt(90) + 1; // 1-90度 expr.append(func).append("(").append(angle).append(")"); hasTrig = true; } else { // 其他操作数可以是普通数字、平方或开方 int num = rand.nextInt(100) + 1; if (rand.nextBoolean() && hasTrig) { // 确保已经有三角函数后再添加其他函数 if (rand.nextBoolean()) { expr.append(num).append("^2"); } else { expr.append("sqrt(").append(num).append(")"); } } else { expr.append(num); } } } // 如果没有三角函数,强制添加一个 if (!hasTrig) { String[] funcs = {"sin", "cos", "tan"}; String func = funcs[rand.nextInt(funcs.length)]; int angle = rand.nextInt(90) + 1; if (rand.nextBoolean()) { // 在开头添加 expr.insert(0, func + "(" + angle + ") + "); } else { // 在结尾添加 expr.append(" + ").append(func).append("(").append(angle).append(")"); } hasTrig = true; } String expression = expr.toString(); // 验证表达式 Problem problem = createProblem(expression); if (problem != null) { return problem; } attempts++; System.err.println("生成高中题目失败,尝试次数: " + attempts + ", 表达式: " + expression); } return createProblem("sin(30) + cos(60)"); } }