diff --git a/src/com/mathlearning/model/QuestionGenerator.java b/src/com/mathlearning/model/QuestionGenerator.java index afc3dc0..871427b 100644 --- a/src/com/mathlearning/model/QuestionGenerator.java +++ b/src/com/mathlearning/model/QuestionGenerator.java @@ -11,7 +11,7 @@ public class QuestionGenerator { public List generateQuestions(String level, int count) { List questions = new ArrayList<>(); - Set questionTexts = new HashSet<>(); // 防止重复题目 + Set questionTexts = new HashSet<>(); for (int i = 0; i < count; i++) { Question question; @@ -44,225 +44,275 @@ public class QuestionGenerator { } private Question generatePrimaryQuestion(int index) { - // 小学:操作数2-5个,确保结果不为负数 - int operandCount = random.nextInt(4) + 2; // 2-5个操作数 + int operandCount = random.nextInt(4) + 2; List operands = new ArrayList<>(); List operators = new ArrayList<>(); - // 生成操作数 (1-100) - for (int i = 0; i < operandCount; i++) { - operands.add(random.nextInt(100) + 1); - } - - // 生成运算符 (+, -, *, /) - for (int i = 0; i < operandCount - 1; i++) { - operators.add(getRandomOperation("+-*/")); - } + // 生成操作数和运算符,确保结果非负 + generateSafePrimaryExpression(operands, operators, operandCount); - // 构建表达式并计算结果,确保不为负数 String questionText; int result; int attempts = 0; do { - // 随机决定是否使用括号 boolean useParentheses = random.nextBoolean() && operandCount >= 3; - if (useParentheses) { - // 使用括号的表达式 int parenPos = random.nextInt(operandCount - 1); - StringBuilder sb = new StringBuilder(); - - for (int i = 0; i < operandCount; i++) { - if (i == parenPos) { - sb.append("("); - } - sb.append(operands.get(i)); - if (i == parenPos + 1) { - sb.append(")"); - } - if (i < operandCount - 1) { - sb.append(" ").append(operators.get(i)).append(" "); - } - } - - questionText = sb.toString(); - result = evaluateExpressionWithParentheses(operands, operators, parenPos); + questionText = buildParenthesesExpression(operands, operators, parenPos); + result = evaluateWithParentheses(operands, operators, parenPos); } else { - // 不使用括号的表达式 - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < operandCount; i++) { - sb.append(operands.get(i)); - if (i < operandCount - 1) { - sb.append(" ").append(operators.get(i)).append(" "); - } - } - - questionText = sb.toString(); + questionText = buildSimpleExpression(operands, operators); result = evaluateSequential(operands, operators); } attempts++; - // 如果结果为负数,重新生成操作数和运算符 if (result < 0) { - operands.clear(); - operators.clear(); - for (int i = 0; i < operandCount; i++) { - operands.add(random.nextInt(100) + 1); - } - for (int i = 0; i < operandCount - 1; i++) { - operators.add(getRandomOperation("+-*/")); - } + // 重新生成安全的表达式 + generateSafePrimaryExpression(operands, operators, operandCount); } } while (result < 0 && attempts < 10); // 如果还是负数,强制使用加法 if (result < 0) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < operandCount; i++) { - sb.append(operands.get(i)); - if (i < operandCount - 1) { - sb.append(" + "); - } + for (int i = 0; i < operators.size(); i++) { + operators.set(i, '+'); } - questionText = sb.toString(); - result = operands.stream().mapToInt(Integer::intValue).sum(); + questionText = buildSimpleExpression(operands, operators); + result = evaluateSequential(operands, operators); } - return generateOptions(questionText + " = ?", result, "小学"); + return generateIntOptions(questionText + " = ?", result, "小学"); + } + + private void generateSafePrimaryExpression(List operands, List operators, int operandCount) { + operands.clear(); + operators.clear(); + + // 首先生成所有操作数 + for (int i = 0; i < operandCount; i++) { + operands.add(random.nextInt(100) + 1); + } + + // 然后生成安全的运算符序列 + int currentValue = operands.get(0); + for (int i = 1; i < operandCount; i++) { + char op; + if (currentValue < operands.get(i)) { + // 如果当前值小于下一个操作数,避免减法导致负数 + if (random.nextBoolean()) { + op = '+'; + currentValue += operands.get(i); + } else { + op = (random.nextBoolean() && currentValue > 0) ? '*' : '+'; + if (op == '*') currentValue *= operands.get(i); + else currentValue += operands.get(i); + } + } else { + // 可以安全使用减法 + op = getRandomOperation("+-*/"); + switch (op) { + case '+': currentValue += operands.get(i); break; + case '-': currentValue -= operands.get(i); break; + case '*': currentValue *= operands.get(i); break; + case '/': + if (operands.get(i) != 0) currentValue /= operands.get(i); + else currentValue += operands.get(i); + break; + } + } + operators.add(op); + } } private Question generateMiddleSchoolQuestion(int index) { - // 初中:操作数1-5个,至少包含平方或开根号 - int operandCount = random.nextInt(5) + 1; // 1-5个操作数 - boolean hasSquareOrSqrt = false; - String questionText; - int result; - int attempts = 0; + int type = random.nextInt(4); + MathExpression expression = new MathExpression(); + + switch (type) { + case 0: + expression = createSquareExpression(); + break; + case 1: + expression = createSqrtExpression(); + break; + case 2: + expression = createSquareProductExpression(); + break; + case 3: + expression = createSquareDivisionExpression(); + break; + } - do { - int type = random.nextInt(4); - switch (type) { - case 0: - // a² ± b × c - int a = random.nextInt(10) + 1; // 1-10的平方 - int b = random.nextInt(100) + 1; - int c = random.nextInt(100) + 1; - char op = random.nextBoolean() ? '+' : '-'; - questionText = a + "² " + op + " " + b + " × " + c; - result = (op == '+') ? (a * a + b * c) : (a * a - b * c); - hasSquareOrSqrt = true; - break; + // 确保初中题目结果非负 + if (expression.result < 0) { + expression = createSquareExpression(); // 重新生成一个肯定为正的题目 + } - case 1: - // √a ± b - int sqrtBase = findPerfectSquare(100); // 100以内的完全平方数 - int sqrtVal = (int) Math.sqrt(sqrtBase); - int d = random.nextInt(100) + 1; - char op2 = random.nextBoolean() ? '+' : '-'; - questionText = "√" + sqrtBase + " " + op2 + " " + d; - result = (op2 == '+') ? (sqrtVal + d) : (sqrtVal - d); - hasSquareOrSqrt = true; - break; + return generateIntOptions(expression.questionText + " = ?", expression.result, "初中"); + } - case 2: - // a × b² ± c - int e = random.nextInt(100) + 1; - int f = random.nextInt(10) + 1; - int g = random.nextInt(100) + 1; - char op3 = random.nextBoolean() ? '+' : '-'; - questionText = e + " × " + f + "² " + op3 + " " + g; - result = (op3 == '+') ? (e * f * f + g) : (e * f * f - g); - hasSquareOrSqrt = true; - break; + private MathExpression createSquareExpression() { + int a = random.nextInt(10) + 1; + int b = random.nextInt(100) + 1; + int c = random.nextInt(100) + 1; + // 只使用加法确保结果为正 + char op = '+'; - default: - // (a + b)² ÷ c - int h = random.nextInt(50) + 1; - int i = random.nextInt(50) + 1; - int j = random.nextInt(20) + 1; - questionText = "(" + h + " + " + i + ")² ÷ " + j; - result = (h + i) * (h + i) / j; - hasSquareOrSqrt = true; - break; - } - attempts++; - } while (!hasSquareOrSqrt && attempts < 10); + String questionText = a + "² " + op + " " + b + " × " + c; + int result = a * a + b * c; - return generateOptions(questionText + " = ?", result, "初中"); + return new MathExpression(questionText, result); + } + + private MathExpression createSqrtExpression() { + int sqrtBase = findPerfectSquare(100); + int sqrtVal = (int) Math.sqrt(sqrtBase); + int d = random.nextInt(100) + 1; + // 只使用加法确保结果为正 + char op = '+'; + + String questionText = "√" + sqrtBase + " " + op + " " + d; + int result = sqrtVal + d; + + return new MathExpression(questionText, result); + } + + private MathExpression createSquareProductExpression() { + int e = random.nextInt(100) + 1; + int f = random.nextInt(10) + 1; + int g = random.nextInt(100) + 1; + // 只使用加法确保结果为正 + char op = '+'; + + String questionText = e + " × " + f + "² " + op + " " + g; + int result = e * f * f + g; + + return new MathExpression(questionText, result); + } + + private MathExpression createSquareDivisionExpression() { + int h = random.nextInt(50) + 1; + int i = random.nextInt(50) + 1; + int sum = h + i; + int square = sum * sum; + int j = findDivisor(square); + + String questionText = "(" + h + " + " + i + ")² ÷ " + j; + int result = square / j; + + return new MathExpression(questionText, result); } private Question generateHighSchoolQuestion(int index) { - // 高中:操作数1-5个,至少包含sin,cos,tan - int operandCount = random.nextInt(5) + 1; // 1-5个操作数 - boolean hasTrigFunction = false; - String questionText; - double result; - int attempts = 0; + int type = random.nextInt(4); + TrigExpression expression = new TrigExpression(); + + switch (type) { + case 0: + expression = createSinExpression(); + break; + case 1: + expression = createCosExpression(); + break; + case 2: + expression = createTanExpression(); + break; + case 3: + expression = createSinCosExpression(); + break; + } - // 常见角度值 - double[] angles = {0, 30, 45, 60, 90, 120, 135, 150, 180}; - String[] angleStrs = {"0°", "30°", "45°", "60°", "90°", "120°", "135°", "150°", "180°"}; - double[] sinValues = {0, 0.5, Math.sqrt(2)/2, Math.sqrt(3)/2, 1, Math.sqrt(3)/2, Math.sqrt(2)/2, 0.5, 0}; - double[] cosValues = {1, Math.sqrt(3)/2, Math.sqrt(2)/2, 0.5, 0, -0.5, -Math.sqrt(2)/2, -Math.sqrt(3)/2, -1}; - double[] tanValues = {0, Math.sqrt(3)/3, 1, Math.sqrt(3), Double.POSITIVE_INFINITY, -Math.sqrt(3), -1, -Math.sqrt(3)/3, 0}; + return generateDoubleOptions(expression.questionText + " = ?", expression.result, "高中"); + } - do { - int type = random.nextInt(4); - switch (type) { - case 0: - // sin(a) ± b - int idx1 = random.nextInt(angles.length); - int b = random.nextInt(100) + 1; - char op = random.nextBoolean() ? '+' : '-'; - questionText = "sin(" + angleStrs[idx1] + ") " + op + " " + b; - result = (op == '+') ? (sinValues[idx1] + b) : (sinValues[idx1] - b); - hasTrigFunction = true; - break; + private TrigExpression createSinExpression() { + String[] angles = {"0°", "30°", "45°", "60°", "90°"}; + double[] sinValues = {0, 0.5, 0.71, 0.87, 1.0}; - case 1: - // a × cos(b) - int a = random.nextInt(100) + 1; - int idx2 = random.nextInt(angles.length); - questionText = a + " × cos(" + angleStrs[idx2] + ")"; - result = a * cosValues[idx2]; - hasTrigFunction = true; - break; + int idx = random.nextInt(angles.length); + int b = random.nextInt(100) + 1; + // 只使用加法确保结果为正 + char op = '+'; - case 2: - // tan(a) ÷ b (避免tan(90°)) - int idx3 = random.nextInt(angles.length); - while (angles[idx3] == 90) { - idx3 = random.nextInt(angles.length); - } - int c = random.nextInt(20) + 1; - questionText = "tan(" + angleStrs[idx3] + ") ÷ " + c; - result = tanValues[idx3] / c; - hasTrigFunction = true; - break; + String questionText = "sin(" + angles[idx] + ") " + op + " " + b; + double result = sinValues[idx] + b; - default: - // sin(a) × cos(b) ± c - int idx4 = random.nextInt(angles.length); - int idx5 = random.nextInt(angles.length); - int d = random.nextInt(100) + 1; - char op2 = random.nextBoolean() ? '+' : '-'; - questionText = "sin(" + angleStrs[idx4] + ") × cos(" + angleStrs[idx5] + ") " + op2 + " " + d; - result = (op2 == '+') ? (sinValues[idx4] * cosValues[idx5] + d) : (sinValues[idx4] * cosValues[idx5] - d); - hasTrigFunction = true; - break; - } - attempts++; - } while (!hasTrigFunction && attempts < 10); + return new TrigExpression(questionText, result); + } + + private TrigExpression createCosExpression() { + String[] angles = {"0°", "30°", "45°", "60°"}; + double[] cosValues = {1.0, 0.87, 0.71, 0.5}; + + int idx = random.nextInt(angles.length); + int a = random.nextInt(100) + 1; + + String questionText = a + " × cos(" + angles[idx] + ")"; + double result = a * cosValues[idx]; - return generateOptions(questionText + " = ?", result, "高中"); + return new TrigExpression(questionText, result); + } + + private TrigExpression createTanExpression() { + String[] angles = {"0°", "30°", "45°", "60°"}; + double[] tanValues = {0, 0.58, 1.0, 1.73}; + + int idx = random.nextInt(angles.length); + int c = random.nextInt(20) + 1; + + String questionText = "tan(" + angles[idx] + ") ÷ " + c; + double result = tanValues[idx] / c; + + return new TrigExpression(questionText, result); + } + + private TrigExpression createSinCosExpression() { + String[] angles = {"0°", "30°", "45°", "60°"}; + double[] sinValues = {0, 0.5, 0.71, 0.87}; + double[] cosValues = {1.0, 0.87, 0.71, 0.5}; + + int idx1 = random.nextInt(angles.length); + int idx2 = random.nextInt(angles.length); + int d = random.nextInt(100) + 1; + // 只使用加法确保结果为正 + char op = '+'; + + String questionText = "sin(" + angles[idx1] + ") × cos(" + angles[idx2] + ") " + op + " " + d; + double product = sinValues[idx1] * cosValues[idx2]; + double result = product + d; + + return new TrigExpression(questionText, result); } - // 辅助方法 private char getRandomOperation(String operations) { return operations.charAt(random.nextInt(operations.length())); } + private String buildSimpleExpression(List operands, List operators) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < operands.size(); i++) { + sb.append(operands.get(i)); + if (i < operands.size() - 1) { + sb.append(" ").append(operators.get(i)).append(" "); + } + } + return sb.toString(); + } + + private String buildParenthesesExpression(List operands, List operators, int parenPos) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < operands.size(); i++) { + if (i == parenPos) sb.append("("); + sb.append(operands.get(i)); + if (i == parenPos + 1) sb.append(")"); + if (i < operands.size() - 1) { + sb.append(" ").append(operators.get(i)).append(" "); + } + } + return sb.toString(); + } + private int evaluateSequential(List operands, List operators) { int result = operands.get(0); for (int i = 0; i < operators.size(); i++) { @@ -271,11 +321,8 @@ public class QuestionGenerator { return result; } - private int evaluateExpressionWithParentheses(List operands, List operators, int parenPos) { - // 先计算括号内的值 + private int evaluateWithParentheses(List operands, List operators, int parenPos) { int parenResult = calculate(operands.get(parenPos), operands.get(parenPos + 1), operators.get(parenPos)); - - // 构建新的操作数和运算符列表 List newOperands = new ArrayList<>(); List newOperators = new ArrayList<>(); @@ -293,39 +340,43 @@ public class QuestionGenerator { } } - // 顺序计算剩余部分 return evaluateSequential(newOperands, newOperators); } private int calculate(int a, int b, char op) { switch (op) { case '+': return a + b; - case '-': - // 确保减法结果不为负数 - return Math.max(a - b, 0); + case '-': return Math.max(a - b, 0); case '*': return a * b; - case '/': - // 确保除法能整除且除数不为0 - if (b == 0) return a; - return a / b; + case '/': return (b == 0) ? a : a / b; default: return a + b; } } private int findPerfectSquare(int max) { - // 找到小于等于max的完全平方数 - List perfectSquares = new ArrayList<>(); + List squares = new ArrayList<>(); for (int i = 1; i * i <= max; i++) { - perfectSquares.add(i * i); + squares.add(i * i); } - return perfectSquares.get(random.nextInt(perfectSquares.size())); + return squares.get(random.nextInt(squares.size())); } - private Question generateOptions(String questionText, int answer, String level) { + private int findDivisor(int number) { + List divisors = new ArrayList<>(); + for (int i = 2; i <= Math.min(20, number); i++) { + if (number % i == 0) { + divisors.add(i); + } + } + return divisors.isEmpty() ? 1 : divisors.get(random.nextInt(divisors.size())); + } + + private Question generateIntOptions(String questionText, int answer, String level) { String[] options = new String[4]; Set usedValues = new HashSet<>(); - options[0] = String.valueOf(answer); + String correctAnswer = String.valueOf(answer); + options[0] = correctAnswer; usedValues.add(answer); for (int i = 1; i < 4; i++) { @@ -335,27 +386,25 @@ public class QuestionGenerator { int range = Math.max(3, Math.abs(answer) / 5 + 1); int offset = random.nextInt(range * 2 + 1) - range; wrongAnswer = answer + offset; - // 确保错误答案不为负数 - wrongAnswer = Math.max(wrongAnswer, 0); + wrongAnswer = Math.max(wrongAnswer, 0); // 确保错误答案也不为负 attempts++; - } while (usedValues.contains(wrongAnswer) && attempts < 20); - - if (usedValues.contains(wrongAnswer)) { - wrongAnswer = answer + i * 10 + 5; - wrongAnswer = Math.max(wrongAnswer, 0); - } + if (attempts > 20) { + wrongAnswer = answer + (i + 1) * 10 + 5; + break; + } + } while (wrongAnswer == answer || usedValues.contains(wrongAnswer)); options[i] = String.valueOf(wrongAnswer); usedValues.add(wrongAnswer); } shuffleArray(options); - int correctIndex = findCorrectIndex(options, String.valueOf(answer)); + int correctIndex = findCorrectIndex(options, correctAnswer); return new Question(questionText, options, correctIndex, level); } - private Question generateOptions(String questionText, double answer, String level) { + private Question generateDoubleOptions(String questionText, double answer, String level) { String[] options = new String[4]; Set usedValues = new HashSet<>(); @@ -367,11 +416,15 @@ public class QuestionGenerator { String wrongAnswer; int attempts = 0; do { - double offset = (random.nextDouble() - 0.5) * 2.0; + double offset = (random.nextDouble() - 0.5) * Math.max(1, Math.abs(answer) * 0.3); double wrongValue = answer + offset; - wrongAnswer = formatDouble(wrongValue); + wrongAnswer = formatDouble(Math.max(wrongValue, 0)); // 确保错误答案不为负 attempts++; - } while (usedValues.contains(wrongAnswer) && attempts < 20); + if (attempts > 20) { + wrongAnswer = formatDouble(answer + (i + 1) * 2.5); + break; + } + } while (usedValues.contains(wrongAnswer) || wrongAnswer.equals(correctAnswer)); options[i] = wrongAnswer; usedValues.add(wrongAnswer); @@ -384,21 +437,15 @@ public class QuestionGenerator { } private String formatDouble(double value) { - if (Double.isInfinite(value)) { - return "∞"; - } - if (Double.isNaN(value)) { - return "无解"; - } - // 保留3位小数,去除多余的0 - return String.format("%.3f", value).replaceAll("0*$", "").replaceAll("\\.$", ""); + if (Double.isInfinite(value)) return "∞"; + if (Double.isNaN(value)) return "无解"; + if (Math.abs(value) < 0.001) return "0"; + return String.format("%.2f", value).replaceAll("0*$", "").replaceAll("\\.$", ""); } private int findCorrectIndex(String[] options, String correctAnswer) { for (int i = 0; i < options.length; i++) { - if (options[i].equals(correctAnswer)) { - return i; - } + if (options[i].equals(correctAnswer)) return i; } return 0; } @@ -411,4 +458,29 @@ public class QuestionGenerator { array[i] = temp; } } + + // 辅助类来存储题目和答案 + private static class MathExpression { + String questionText; + int result; + + MathExpression() {} + + MathExpression(String questionText, int result) { + this.questionText = questionText; + this.result = result; + } + } + + private static class TrigExpression { + String questionText; + double result; + + TrigExpression() {} + + TrigExpression(String questionText, double result) { + this.questionText = questionText; + this.result = result; + } + } } \ No newline at end of file