|
|
|
|
@ -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<String> operands = new ArrayList<>(middleSchoolEquation.operands());
|
|
|
|
|
List<Operator> 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";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|