diff --git a/src/main/java/com/mathapp/problemGenerators/AbstractProblemGenerator.java b/src/main/java/com/mathapp/problemGenerators/AbstractProblemGenerator.java index e032289..e228391 100644 --- a/src/main/java/com/mathapp/problemGenerators/AbstractProblemGenerator.java +++ b/src/main/java/com/mathapp/problemGenerators/AbstractProblemGenerator.java @@ -63,7 +63,9 @@ public abstract class AbstractProblemGenerator implements IProblemGenerator { addOperandAndOperator(operands, operators, operator, nonNegativeOnly); } - addParentheses(operands, operators); + if(nonNegativeOnly) { + addParentheses(operands, operators); + } return new Equation(operands, operators); } diff --git a/src/main/java/com/mathapp/problemGenerators/HighSchoolProblemGenerator.java b/src/main/java/com/mathapp/problemGenerators/HighSchoolProblemGenerator.java index f09cd73..db8733d 100644 --- a/src/main/java/com/mathapp/problemGenerators/HighSchoolProblemGenerator.java +++ b/src/main/java/com/mathapp/problemGenerators/HighSchoolProblemGenerator.java @@ -4,47 +4,74 @@ import com.mathapp.models.Equation; import com.mathapp.models.Operator; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; /** - * 高中题目生成器(具体策略)。 - * 它首先生成一个包含初中难度运算(平方/开方)的题目, - * 然后再额外加入一个三角函数运算。 - * 生成的格式与 exp4j 兼容,但依赖计算引擎处理角度单位。 + * 高中题目生成器(最终修正版)。 + * 采用更稳健的“追加”策略,从根本上避免所有索引越界和除零错误。 */ public class HighSchoolProblemGenerator extends MiddleSchoolProblemGenerator { - private static final int[] PREDEFINED_ANGLES = new int[]{0, 30, 45, 60, 90}; - private static final String[] TRIG_FUNCTIONS = new String[]{"sin", "cos", "tan"}; + private static final int[] PREDEFINED_ANGLES = {0, 30, 45, 60, 90}; + private static final String[] TRIG_FUNCTIONS = {"sin", "cos", "tan"}; @Override protected Equation createProblem() { - // 复用初中生成器的逻辑,先生成一个带平方或开方的题目 + // 1. 调用父类方法生成一个包含平方或开方的基础题目 Equation middleSchoolEquation = super.createProblem(); List operands = new ArrayList<>(middleSchoolEquation.operands()); List operators = new ArrayList<>(middleSchoolEquation.operators()); - String function = Arrays.toString(TRIG_FUNCTIONS); - int[] angle = PREDEFINED_ANGLES; + String trigExpression; + Operator connectingOperator; - // 生成对学生友好的三角函数表达式,如 "sin(30)" - // exp4j 的计算将在 QuestionGenerator 中通过自定义函数处理角度 - String trigExpression = String.format("%s(%d)", function, angle); + // 2.【最终修正策略】使用健壮性检查循环,直到生成一个绝对安全的组合 + while (true) { + // 2a. 生成三角函数部分,并确保它不是 tan(90°) + String function; + int angleInDegrees; + do { + function = TRIG_FUNCTIONS[random.nextInt(TRIG_FUNCTIONS.length)]; + angleInDegrees = PREDEFINED_ANGLES[random.nextInt(PREDEFINED_ANGLES.length)]; + } while (function.equals("tan") && angleInDegrees == 90); - // 随机选择一个位置插入三角函数表达式 - int insertPos = random.nextInt(operands.size() + 1); - if (insertPos == operands.size() || operands.isEmpty()) { - operands.add(trigExpression); - if(!operators.isEmpty() || operands.size() > 1) { - operators.add(Operator.ADD); + trigExpression = String.format("%s(%s)", function, getRadianString(angleInDegrees)); + + // 2b. 随机选择一个连接运算符 + connectingOperator = Operator.values()[random.nextInt(Operator.values().length)]; + + // 2c.【健壮性检查】检查这个新运算符是否会与三角函数的值构成除零错误 + boolean isTrigValueZero = (function.equals("sin") && angleInDegrees == 0) || + (function.equals("tan") && angleInDegrees == 0); + + // 如果新运算符是除法,且三角函数的值是0,则这是一个非法组合,必须重试 + if (connectingOperator == Operator.DIVIDE && isTrigValueZero) { + continue; // 非法组合,重新循环 } - } else { - operands.add(insertPos, trigExpression); - operators.add(insertPos, Operator.ADD); // 简单地用加法连接 + + // 如果代码能执行到这里,说明组合是安全的,可以退出循环 + break; } + // 3.【安全的追加操作】将生成的安全组合追加到表达式末尾 + // 这种方法永远不会导致 IndexOutOfBoundsException + operands.add(trigExpression); + operators.add(connectingOperator); return new Equation(operands, operators); } + + /** + * 将特殊的角度值转换为 exp4j 可以计算的、基于 pi 的弧度字符串。 + */ + private String getRadianString(int angleInDegrees) { + switch (angleInDegrees) { + case 0: return "0"; + case 30: return "pi/6"; + case 45: return "pi/4"; + case 60: return "pi/3"; + case 90: return "pi/2"; + default: return "0"; + } + } } \ No newline at end of file diff --git a/src/main/java/com/mathapp/problemGenerators/MiddleSchoolProblemGenerator.java b/src/main/java/com/mathapp/problemGenerators/MiddleSchoolProblemGenerator.java index d8ed83e..30cfc05 100644 --- a/src/main/java/com/mathapp/problemGenerators/MiddleSchoolProblemGenerator.java +++ b/src/main/java/com/mathapp/problemGenerators/MiddleSchoolProblemGenerator.java @@ -8,34 +8,38 @@ import java.util.List; /** * 初中题目生成器(具体策略)。 * 首先生成一个允许出现负数的标准四则运算题目,然后确保至少包含一个平方或开方运算。 - * 生成的格式与 exp4j 兼容。 + * 生成的格式 (例如, "(5^2)" 和 "sqrt(25)") 与 exp4j 库兼容。 */ public class MiddleSchoolProblemGenerator extends AbstractProblemGenerator { @Override protected Equation createProblem() { + // 1. 调用父类的核心方法,并传入 false,允许生成包含负数的标准表达式。 Equation basicEquation = createBaseArithmeticProblem(false); + + // 2. 将基础表达式元素转为可修改的列表 List operands = new ArrayList<>(basicEquation.operands()); List operators = new ArrayList<>(basicEquation.operators()); + // 3. 随机选择一个操作数进行变形 int modifyIndex = random.nextInt(operands.size()); - String targetOperand = operands.get(modifyIndex); - - // 检查操作数是否已经是复杂表达式(包含括号),如果是,则不再添加括号 - boolean isComplex = targetOperand.contains("(") && targetOperand.contains(")"); + String targetOperand = operands.get(modifyIndex).replace("(", "").replace(")", ""); + // 4. 随机决定是进行平方还是开方运算 if (random.nextBoolean()) { - // 平方运算:exp4j 使用 '^' 符号 - // 如果操作数本身是复杂表达式,如 (3+2),则需要保留括号,变成 ((3+2)^2) - String squaredExpression = isComplex? String.format("(%s^2)", targetOperand) : String.format("%s^2", targetOperand); + // 平方运算: 使用 `^` 符号,exp4j 支持此格式 + String squaredExpression = String.format("(%s^2)", targetOperand); operands.set(modifyIndex, squaredExpression); } else { - // 开方运算:exp4j 使用 'sqrt()' 函数 + // 开方运算: 使用 `sqrt()` 函数,exp4j 内置支持此函数 int base = getRandomNumber(2, 10); int valueToRoot = base * base; String sqrtExpression = String.format("sqrt(%d)", valueToRoot); operands.set(modifyIndex, sqrtExpression); } + + // 5. 返回构建好的、与exp4j兼容的方程式 return new Equation(operands, operators); } } +