package com.mathlearning.model; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Random; import java.util.Set; public class QuestionGenerator { private Random random = new Random(); public List generateQuestions(String level, int count) { List questions = new ArrayList<>(); Set questionTexts = new HashSet<>(); // 防止重复题目 for (int i = 0; i < count; i++) { Question question; int attempt = 0; do { question = generateQuestion(level, i); attempt++; } while (questionTexts.contains(question.getQuestionText()) && attempt < 10); if (question != null) { questionTexts.add(question.getQuestionText()); questions.add(question); } } return questions; } private Question generateQuestion(String level, int index) { switch (level) { case "小学": return generatePrimaryQuestion(index); case "初中": return generateMiddleSchoolQuestion(index); case "高中": return generateHighSchoolQuestion(index); default: return generatePrimaryQuestion(index); } } private Question generatePrimaryQuestion(int index) { int operandCount = random.nextInt(4) + 2; // 2-5个操作数 int[] operands = new int[operandCount]; int[] operations = new int[operandCount - 1]; String questionText; double answer; do { // 生成操作数 for (int i = 0; i < operandCount; i++) { operands[i] = random.nextInt(100) + 1; // 1-100 } // 生成操作符 (0: +, 1: -, 2: *, 3: /) for (int i = 0; i < operations.length; i++) { operations[i] = random.nextInt(4); } // 改进的括号逻辑:在可能改变计算顺序的地方加括号 boolean useParentheses = random.nextDouble() < 0.3 && operandCount >= 3; int parenStart = -1; int parenEnd = -1; if (useParentheses) { // 寻找适合加括号的位置(加减法周围) parenStart = findSuitableParenthesesPosition(operations); if (parenStart != -1) { // 括号包含2-3个操作数 parenEnd = parenStart + 1; if (parenEnd < operands.length - 1 && random.nextBoolean()) { parenEnd++; } } } questionText = buildPrimaryQuestionText(operands, operations, parenStart, parenEnd); answer = calculatePrimaryAnswerWithParentheses(operands, operations, parenStart, parenEnd); } while (answer < 0 || Double.isNaN(answer) || Double.isInfinite(answer)); return generateOptions(questionText, answer, "小学"); } private Question generateMiddleSchoolQuestion(int index) { int operandCount = random.nextInt(5) + 1; // 1-5个操作数 int[] operands = new int[operandCount]; int[] operations = new int[Math.max(0, operandCount - 1)]; // 操作符数组 boolean[] hasSpecialOp = new boolean[operandCount]; // 标记哪些操作数有平方或开根号 int[] specialOpTypes = new int[operandCount]; // 0:平方, 1:开根号 String questionText; double answer; // 确保至少有一个平方或开根号运算符 boolean hasSpecialOperation = false; for (int i = 0; i < operandCount; i++) { hasSpecialOp[i] = random.nextDouble() < 0.6; // 60%的概率有特殊运算 if (hasSpecialOp[i]) { hasSpecialOperation = true; specialOpTypes[i] = random.nextInt(2); // 随机选择平方或开根号 } } // 如果没有特殊运算,强制至少一个 if (!hasSpecialOperation && operandCount > 0) { int idx = random.nextInt(operandCount); hasSpecialOp[idx] = true; specialOpTypes[idx] = random.nextInt(2); } // 生成合适的题目 do { // 生成操作数 for (int i = 0; i < operandCount; i++) { operands[i] = random.nextInt(100) + 1; // 1-100 } // 生成操作符 (0: +, 1: -, 2: *, 3: /) for (int i = 0; i < operations.length; i++) { operations[i] = random.nextInt(4); } // 构建题目并计算答案 questionText = buildMiddleSchoolQuestionText(operands, operations, hasSpecialOp, specialOpTypes); answer = calculateMiddleSchoolAnswer(operands, operations, hasSpecialOp, specialOpTypes); } while (Double.isNaN(answer) || Double.isInfinite(answer) || Math.abs(answer) > 10000); return generateOptions(questionText, answer, "初中"); } private Question generateHighSchoolQuestion(int index) { int operandCount = random.nextInt(5) + 1; // 1-5个操作数 int[] operands = new int[operandCount]; int[] operations = new int[Math.max(0, operandCount - 1)]; // 操作符数组 boolean[] hasTrigOp = new boolean[operandCount]; // 标记哪些操作数有三角函数 int[] trigFunctions = new int[operandCount]; // 0:sin, 1:cos, 2:tan String questionText; double answer; // 确保至少有一个三角函数运算符 boolean hasTrigOperation = false; for (int i = 0; i < operandCount; i++) { hasTrigOp[i] = random.nextDouble() < 0.6; // 60%的概率有三角函数 if (hasTrigOp[i]) { hasTrigOperation = true; trigFunctions[i] = random.nextInt(3); // 随机选择三角函数 } } // 如果没有三角函数,强制至少一个 if (!hasTrigOperation && operandCount > 0) { int idx = random.nextInt(operandCount); hasTrigOp[idx] = true; trigFunctions[idx] = random.nextInt(3); } // 生成合适的题目 do { // 生成操作数 for (int i = 0; i < operandCount; i++) { if (hasTrigOp[i]) { // 三角函数使用特殊角度值 int[] specialAngles = {0, 15, 30, 45, 60, 75, 90}; operands[i] = specialAngles[random.nextInt(specialAngles.length)]; } else { // 普通操作数使用1-100的随机数 operands[i] = random.nextInt(100) + 1; } } // 生成操作符 (0: +, 1: -, 2: *, 3: /) for (int i = 0; i < operations.length; i++) { operations[i] = random.nextInt(4); } // 构建题目并计算答案 questionText = buildHighSchoolQuestionText(operands, operations, hasTrigOp, trigFunctions); answer = calculateHighSchoolAnswer(operands, operations, hasTrigOp, trigFunctions); } while (Double.isNaN(answer) || Double.isInfinite(answer) || Math.abs(answer) > 10000); return generateOptions(questionText, answer, "高中"); } // 小学题目相关方法 private int findSuitableParenthesesPosition(int[] operations) { // 寻找加减法操作符的位置,在这些地方加括号才有意义 List suitablePositions = new ArrayList<>(); for (int i = 0; i < operations.length; i++) { // 如果是加减法,且不是第一个操作符 if ((operations[i] == 0 || operations[i] == 1) && i > 0) { suitablePositions.add(i); } } if (suitablePositions.isEmpty()) { // 如果没有找到合适的加减法位置,随机选择一个位置 return operations.length > 1 ? random.nextInt(operations.length - 1) : -1; } // 从合适的位置中随机选择一个 return suitablePositions.get(random.nextInt(suitablePositions.size())); } private String buildPrimaryQuestionText(int[] operands, int[] operations, int parenStart, int parenEnd) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < operands.length; i++) { // 在操作数前添加左括号 if (i == parenStart) { sb.append("("); } // 添加操作数 sb.append(operands[i]); // 在操作数后添加右括号(但要在操作符之前) if (i == parenEnd) { sb.append(")"); } // 添加操作符(如果不是最后一个操作数) if (i < operations.length) { switch (operations[i]) { case 0: sb.append(" + "); break; case 1: sb.append(" - "); break; case 2: sb.append(" × "); break; case 3: sb.append(" ÷ "); break; } } } sb.append(" = ?"); return sb.toString(); } private double calculatePrimaryAnswerWithParentheses(int[] operands, int[] operations, int parenStart, int parenEnd) { // 如果没有括号,使用原来的计算方法 if (parenStart == -1 || parenEnd == -1) { return calculatePrimaryAnswer(operands, operations); } // 复制操作数和操作符 List numbers = new ArrayList<>(); List ops = new ArrayList<>(); for (int operand : operands) { numbers.add(operand); } for (int operation : operations) { ops.add(operation); } // 计算括号内的表达式 double parenthesesResult = calculateParenthesesContent(numbers, ops, parenStart, parenEnd); // 验证括号计算结果是否有效 if (parenthesesResult < 0 || Double.isNaN(parenthesesResult) || Double.isInfinite(parenthesesResult)) { return -1; } // 用括号计算结果替换括号内的所有元素 replaceWithParenthesesResult(numbers, ops, parenStart, parenEnd, parenthesesResult); // 计算剩余表达式 return calculateWithoutParentheses(numbers, ops); } private double calculateParenthesesContent(List numbers, List ops, int start, int end) { // 提取括号内的数字和操作符 List parenNumbers = new ArrayList<>(); List parenOps = new ArrayList<>(); for (int i = start; i <= end; i++) { parenNumbers.add(numbers.get(i)); } for (int i = start; i < end; i++) { parenOps.add(ops.get(i)); } // 计算括号内的表达式(精确计算) return calculateWithoutParentheses(parenNumbers, parenOps); } private void replaceWithParenthesesResult(List numbers, List ops, int start, int end, double result) { // 计算需要移除的元素数量 int numCountToRemove = end - start + 1; int opCountToRemove = end - start; // 移除括号范围内的数字 for (int i = 0; i < numCountToRemove; i++) { numbers.remove(start); } // 移除括号范围内的操作符 for (int i = 0; i < opCountToRemove; i++) { ops.remove(start); } // 插入括号计算结果 numbers.add(start, (int) Math.round(result)); } private double calculateWithoutParentheses(List numbers, List ops) { // 转换为double列表进行计算(精确计算) List doubleNumbers = new ArrayList<>(); for (Integer num : numbers) { doubleNumbers.add(num.doubleValue()); } // 先处理乘除法(精确计算) for (int i = 0; i < ops.size(); i++) { int op = ops.get(i); if (op == 2 || op == 3) { double left = doubleNumbers.get(i); double right = doubleNumbers.get(i + 1); double result; if (op == 2) { result = left * right; } else { if (right == 0) return Double.NaN; result = left / right; } doubleNumbers.set(i, result); doubleNumbers.remove(i + 1); ops.remove(i); i--; } } // 处理加减法(精确计算) double result = doubleNumbers.get(0); for (int i = 0; i < ops.size(); i++) { int op = ops.get(i); double nextNum = doubleNumbers.get(i + 1); if (op == 0) { result += nextNum; } else { if (result < nextNum) return -1; result -= nextNum; } } // 只在最后结果保留2位小数 return Math.round(result * 100) / 100.0; } private double calculatePrimaryAnswer(int[] operands, int[] operations) { // 转换为double列表(精确计算) List numbers = new ArrayList<>(); List ops = new ArrayList<>(); for (int operand : operands) { numbers.add((double) operand); } for (int operation : operations) { ops.add(operation); } // 处理乘除法(精确计算) for (int i = 0; i < ops.size(); i++) { int op = ops.get(i); if (op == 2 || op == 3) { double left = numbers.get(i); double right = numbers.get(i + 1); double result; if (op == 2) { result = left * right; } else { if (right == 0) return Double.NaN; result = left / right; } numbers.set(i, result); numbers.remove(i + 1); ops.remove(i); i--; } } // 处理加减法(精确计算) double result = numbers.get(0); for (int i = 0; i < ops.size(); i++) { int op = ops.get(i); double nextNum = numbers.get(i + 1); if (op == 0) { result += nextNum; } else { if (result < nextNum) return -1; result -= nextNum; } } // 只在最后结果保留2位小数 return Math.round(result * 100) / 100.0; } // 初中题目相关方法 private String buildMiddleSchoolQuestionText(int[] operands, int[] operations, boolean[] hasSpecialOp, int[] specialOpTypes) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < operands.length; i++) { if (i > 0) { switch (operations[i - 1]) { case 0: sb.append(" + "); break; case 1: sb.append(" - "); break; case 2: sb.append(" × "); break; case 3: sb.append(" ÷ "); break; } } if (hasSpecialOp[i]) { if (specialOpTypes[i] == 0) { // 平方 sb.append(operands[i]).append("²"); } else { // 开根号 sb.append("√").append(operands[i]); } } else { sb.append(operands[i]); } } sb.append(" = ?"); return sb.toString(); } private double calculateMiddleSchoolAnswer(int[] operands, int[] operations, boolean[] hasSpecialOp, int[] specialOpTypes) { // 先处理特殊运算(平方和开根号)- 精确计算 double[] processedValues = new double[operands.length]; for (int i = 0; i < operands.length; i++) { if (hasSpecialOp[i]) { if (specialOpTypes[i] == 0) { // 平方 processedValues[i] = operands[i] * operands[i]; } else { // 开根号 processedValues[i] = Math.sqrt(operands[i]); } } else { processedValues[i] = operands[i]; } } // 然后按照小学的计算逻辑处理(精确计算) List numbers = new ArrayList<>(); List ops = new ArrayList<>(); for (double value : processedValues) { numbers.add(value); } for (int operation : operations) { ops.add(operation); } // 处理乘除法(精确计算) for (int i = 0; i < ops.size(); i++) { int op = ops.get(i); if (op == 2 || op == 3) { double left = numbers.get(i); double right = numbers.get(i + 1); double result; if (op == 2) { result = left * right; } else { if (right == 0) return Double.NaN; result = left / right; } numbers.set(i, result); numbers.remove(i + 1); ops.remove(i); i--; } } // 处理加减法(精确计算) double result = numbers.get(0); for (int i = 0; i < ops.size(); i++) { int op = ops.get(i); double nextNum = numbers.get(i + 1); if (op == 0) { result += nextNum; } else { result -= nextNum; } } // 只在最后结果保留2位小数 return Math.round(result * 100) / 100.0; } // 高中题目相关方法 private String buildHighSchoolQuestionText(int[] operands, int[] operations, boolean[] hasTrigOp, int[] trigFunctions) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < operands.length; i++) { if (i > 0) { switch (operations[i - 1]) { case 0: sb.append(" + "); break; case 1: sb.append(" - "); break; case 2: sb.append(" × "); break; case 3: sb.append(" ÷ "); break; } } if (hasTrigOp[i]) { switch (trigFunctions[i]) { case 0: sb.append("sin(").append(operands[i]).append("°)"); break; case 1: sb.append("cos(").append(operands[i]).append("°)"); break; case 2: sb.append("tan(").append(operands[i]).append("°)"); break; } } else { sb.append(operands[i]); } } sb.append(" = ?"); return sb.toString(); } private double calculateHighSchoolAnswer(int[] operands, int[] operations, boolean[] hasTrigOp, int[] trigFunctions) { // 先处理三角函数运算(精确计算) double[] processedValues = new double[operands.length]; for (int i = 0; i < operands.length; i++) { if (hasTrigOp[i]) { double radians = Math.toRadians(operands[i]); switch (trigFunctions[i]) { case 0: processedValues[i] = Math.sin(radians); break; case 1: processedValues[i] = Math.cos(radians); break; case 2: // 避免tan(90°)等无效值 if (operands[i] % 180 == 90 && operands[i] % 360 != 270) { return Double.NaN; } processedValues[i] = Math.tan(radians); break; default: processedValues[i] = 0; } } else { processedValues[i] = operands[i]; } } // 然后按照常规计算逻辑处理(精确计算) List numbers = new ArrayList<>(); List ops = new ArrayList<>(); for (double value : processedValues) { numbers.add(value); } for (int operation : operations) { ops.add(operation); } // 处理乘除法(精确计算) for (int i = 0; i < ops.size(); i++) { int op = ops.get(i); if (op == 2 || op == 3) { double left = numbers.get(i); double right = numbers.get(i + 1); double result; if (op == 2) { result = left * right; } else { if (right == 0) return Double.NaN; result = left / right; } numbers.set(i, result); numbers.remove(i + 1); ops.remove(i); i--; } } // 处理加减法(精确计算) double result = numbers.get(0); for (int i = 0; i < ops.size(); i++) { int op = ops.get(i); double nextNum = numbers.get(i + 1); if (op == 0) { result += nextNum; } else { result -= nextNum; } } // 只在最后结果保留2位小数 return Math.round(result * 100) / 100.0; } // 生成选项的通用方法 private Question generateOptions(String questionText, double answer, String level) { String[] options = new String[4]; Set usedValues = new HashSet<>(); // 检查答案是否为整数 boolean isIntegerAnswer = (answer == (int) answer); // 格式化正确答案 String correctAnswer = formatAnswer(answer); options[0] = correctAnswer; usedValues.add(correctAnswer); // 生成错误答案 for (int i = 1; i < 4; i++) { String wrongAnswer; int attempts = 0; do { // 基于正确答案生成错误答案 double wrongValue; double offset = (random.nextDouble() * 5) + 1; // 1-6的偏移量 if (isIntegerAnswer) { // 如果答案是整数,生成整数错误答案 int intAnswer = (int) answer; int intOffset = random.nextInt(10) + 1; // 1-10的整数偏移 if (random.nextBoolean()) { wrongValue = intAnswer + intOffset; } else { wrongValue = Math.max(1, intAnswer - intOffset); } // 确保是整数 wrongValue = (int) wrongValue; } else { // 如果答案是小数,生成小数错误答案 if (random.nextBoolean()) { wrongValue = answer + offset; } else { wrongValue = answer - offset; } // 保留2位小数 wrongValue = Math.round(wrongValue * 100) / 100.0; } wrongAnswer = formatAnswer(wrongValue); attempts++; } while (usedValues.contains(wrongAnswer) && attempts < 20); options[i] = wrongAnswer; usedValues.add(wrongAnswer); } // 打乱选项顺序 shuffleArray(options); // 找到正确答案的新位置 int correctIndex = 0; for (int i = 0; i < options.length; i++) { if (options[i].equals(correctAnswer)) { correctIndex = i; break; } } return new Question(questionText, options, correctIndex, level); } private String formatAnswer(double answer) { if (answer == (int) answer) { return String.valueOf((int) answer); // 整数不显示小数 } else { return String.format("%.2f", answer); // 小数保留2位 } } private void shuffleArray(String[] array) { for (int i = array.length - 1; i > 0; i--) { int j = random.nextInt(i + 1); String temp = array[i]; array[i] = array[j]; array[j] = temp; } } }