diff --git a/src/com/mathlearning/model/QuestionGenerator.java b/src/com/mathlearning/model/QuestionGenerator.java index d45a941..2f8dda4 100644 --- a/src/com/mathlearning/model/QuestionGenerator.java +++ b/src/com/mathlearning/model/QuestionGenerator.java @@ -46,12 +46,11 @@ public class QuestionGenerator { private Question generatePrimaryQuestion(int index) { int operandCount = random.nextInt(4) + 2; // 2-5个操作数 int[] operands = new int[operandCount]; - int[] operations = new int[operandCount - 1]; // 操作符数组 + int[] operations = new int[operandCount - 1]; String questionText; - int answer; + double answer; - // 生成合适的题目,确保计算过程中不出现负数 do { // 生成操作数 for (int i = 0; i < operandCount; i++) { @@ -63,11 +62,27 @@ public class QuestionGenerator { operations[i] = random.nextInt(4); } - // 构建题目并计算答案 - questionText = buildPrimaryQuestionText(operands, operations); - answer = calculatePrimaryAnswer(operands, operations); + // 改进的括号逻辑:在可能改变计算顺序的地方加括号 + 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++; + } + } + } - } while (answer < 0 || !isValidPrimaryCalculation(operands, operations)); + 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, "小学"); } @@ -77,7 +92,7 @@ public class QuestionGenerator { 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:开根号 - 新增:记录具体操作类型 + int[] specialOpTypes = new int[operandCount]; // 0:平方, 1:开根号 String questionText; double answer; @@ -88,7 +103,7 @@ public class QuestionGenerator { hasSpecialOp[i] = random.nextDouble() < 0.6; // 60%的概率有特殊运算 if (hasSpecialOp[i]) { hasSpecialOperation = true; - specialOpTypes[i] = random.nextInt(2); // 随机选择平方或开根号,并记录下来 + specialOpTypes[i] = random.nextInt(2); // 随机选择平方或开根号 } } @@ -149,11 +164,16 @@ public class QuestionGenerator { // 生成合适的题目 do { - // 生成操作数 (角度值,通常用特殊角度) + // 生成操作数 for (int i = 0; i < operandCount; i++) { - // 使用常见角度:0, 30, 45, 60, 90等 - int[] commonAngles = {0, 30, 45, 60, 90, 120, 135, 150, 180}; - operands[i] = commonAngles[random.nextInt(commonAngles.length)]; + 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: /) @@ -167,31 +187,69 @@ public class QuestionGenerator { } while (Double.isNaN(answer) || Double.isInfinite(answer) || Math.abs(answer) > 10000); - return generateOptions(questionText, (int)Math.round(answer), "高中"); + return generateOptions(questionText, answer, "高中"); } // 小学题目相关方法 - private String buildPrimaryQuestionText(int[] operands, int[] operations) { + 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 > 0) { - switch (operations[i - 1]) { + // 在操作数前添加左括号 + 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(operands[i]); } sb.append(" = ?"); return sb.toString(); } - private int calculatePrimaryAnswer(int[] operands, int[] operations) { - // 先处理乘除法,再处理加减法 + 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<>(); @@ -202,33 +260,91 @@ public class QuestionGenerator { 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) { - int left = numbers.get(i); - int right = numbers.get(i + 1); - int result; + double left = doubleNumbers.get(i); + double right = doubleNumbers.get(i + 1); + double result; if (op == 2) { result = left * right; } else { - if (right == 0 || left % right != 0) return -1; + if (right == 0) return Double.NaN; result = left / right; } - numbers.set(i, result); - numbers.remove(i + 1); + doubleNumbers.set(i, result); + doubleNumbers.remove(i + 1); ops.remove(i); i--; } } - // 处理加减法 - int result = numbers.get(0); + // 处理加减法(精确计算) + double result = doubleNumbers.get(0); for (int i = 0; i < ops.size(); i++) { int op = ops.get(i); - int nextNum = numbers.get(i + 1); + double nextNum = doubleNumbers.get(i + 1); if (op == 0) { result += nextNum; @@ -238,33 +354,60 @@ public class QuestionGenerator { } } - return result; + // 只在最后结果保留2位小数 + return Math.round(result * 100) / 100.0; } - private boolean isValidPrimaryCalculation(int[] operands, int[] operations) { - try { - int current = operands[0]; + private double calculatePrimaryAnswer(int[] operands, int[] operations) { + // 转换为double列表(精确计算) + List numbers = new ArrayList<>(); + List ops = new ArrayList<>(); - for (int i = 0; i < operations.length; i++) { - int next = operands[i + 1]; - int op = operations[i]; - - if (op == 1) { - if (current < next) return false; - current = current - next; - } else if (op == 0) { - current = current + next; - } else if (op == 2) { - current = current * next; - } else if (op == 3) { - if (next == 0 || current % next != 0) return false; - current = current / next; + 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--; } - return current >= 0; - } catch (Exception e) { - return false; } + + // 处理加减法(精确计算) + 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; } // 初中题目相关方法 @@ -299,7 +442,7 @@ public class QuestionGenerator { } 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]) { @@ -315,7 +458,7 @@ public class QuestionGenerator { } } - // 然后按照小学的计算逻辑处理 + // 然后按照小学的计算逻辑处理(精确计算) List numbers = new ArrayList<>(); List ops = new ArrayList<>(); @@ -326,7 +469,7 @@ public class QuestionGenerator { ops.add(operation); } - // 处理乘除法 + // 处理乘除法(精确计算) for (int i = 0; i < ops.size(); i++) { int op = ops.get(i); if (op == 2 || op == 3) { @@ -348,7 +491,7 @@ public class QuestionGenerator { } } - // 处理加减法 + // 处理加减法(精确计算) double result = numbers.get(0); for (int i = 0; i < ops.size(); i++) { int op = ops.get(i); @@ -361,7 +504,8 @@ public class QuestionGenerator { } } - return result; + // 只在最后结果保留2位小数 + return Math.round(result * 100) / 100.0; } // 高中题目相关方法 @@ -394,22 +538,34 @@ public class QuestionGenerator { } 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: processedValues[i] = Math.tan(radians); break; + 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<>(); @@ -420,7 +576,7 @@ public class QuestionGenerator { ops.add(operation); } - // 处理乘除法 + // 处理乘除法(精确计算) for (int i = 0; i < ops.size(); i++) { int op = ops.get(i); if (op == 2 || op == 3) { @@ -442,7 +598,7 @@ public class QuestionGenerator { } } - // 处理加减法 + // 处理加减法(精确计算) double result = numbers.get(0); for (int i = 0; i < ops.size(); i++) { int op = ops.get(i); @@ -455,37 +611,59 @@ public class QuestionGenerator { } } - return result; + // 只在最后结果保留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<>(); + Set usedValues = new HashSet<>(); - // 添加正确答案,保留1位小数 - options[0] = String.format("%.1f", answer); - usedValues.add(Math.round(answer * 10) / 10.0); // 保留1位小数进行比较 + // 检查答案是否为整数 + boolean isIntegerAnswer = (answer == (int) answer); + + // 格式化正确答案 + String correctAnswer = formatAnswer(answer); + options[0] = correctAnswer; + usedValues.add(correctAnswer); // 生成错误答案 for (int i = 1; i < 4; i++) { - double wrongAnswer; + String wrongAnswer; int attempts = 0; do { - // 根据答案大小生成合适的错误答案 - double range = Math.max(2, Math.abs(answer) / 4 + 1); - double offset = (random.nextDouble() * range * 2) - range; - wrongAnswer = answer + offset; - wrongAnswer = Math.round(wrongAnswer * 10) / 10.0; // 保留1位小数 + // 基于正确答案生成错误答案 + 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); - if (usedValues.contains(wrongAnswer)) { - wrongAnswer = answer + (i * 0.5) + 0.3; - wrongAnswer = Math.round(wrongAnswer * 10) / 10.0; - } - - options[i] = String.format("%.1f", wrongAnswer); + options[i] = wrongAnswer; usedValues.add(wrongAnswer); } @@ -494,9 +672,8 @@ public class QuestionGenerator { // 找到正确答案的新位置 int correctIndex = 0; - String correctAnswerStr = String.format("%.1f", answer); for (int i = 0; i < options.length; i++) { - if (options[i].equals(correctAnswerStr)) { + if (options[i].equals(correctAnswer)) { correctIndex = i; break; } @@ -505,6 +682,14 @@ public class QuestionGenerator { 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);