|
|
|
@ -9,8 +9,6 @@ public class ExpressionUtils {
|
|
|
|
|
|
|
|
|
|
|
|
private static final String[] OPS_PRIMARY = {"+", "-", "*", "/"};
|
|
|
|
private static final String[] OPS_PRIMARY = {"+", "-", "*", "/"};
|
|
|
|
private static final String[] OPS_MIDDLE = {"+", "-", "*", "/"};
|
|
|
|
private static final String[] OPS_MIDDLE = {"+", "-", "*", "/"};
|
|
|
|
//private static final String[] OPS_HIGH = {"+", "-", "*", "/", "^2", "sqrt"};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static String randomNumber() {
|
|
|
|
public static String randomNumber() {
|
|
|
|
return String.valueOf(rand.nextInt(100) + 1);
|
|
|
|
return String.valueOf(rand.nextInt(100) + 1);
|
|
|
|
@ -18,106 +16,130 @@ public class ExpressionUtils {
|
|
|
|
|
|
|
|
|
|
|
|
// --- 表达式求解和选项生成辅助方法 ---
|
|
|
|
// --- 表达式求解和选项生成辅助方法 ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static double solveExpression(String expr) {
|
|
|
|
public static double solveExpression(String expr) {
|
|
|
|
// 预处理表达式,确保格式正确
|
|
|
|
|
|
|
|
String parsableExpr = expr
|
|
|
|
String parsableExpr = expr
|
|
|
|
.replaceAll("(\\d+|\\([^)]+\\))\\^2", "pow($1, 2)") // 处理平方
|
|
|
|
.replaceAll("(\\d+|\\([^)]+\\))\\^2", "pow($1, 2)")
|
|
|
|
.replaceAll("sqrt\\s+(\\d+)", "sqrt($1)") // 处理缺少括号的sqrt
|
|
|
|
.replaceAll("sqrt\\s+(\\d+)", "sqrt($1)")
|
|
|
|
.replaceAll("\\s+", " "); // 标准化空格
|
|
|
|
.replaceAll("\\s+", " ");
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
return new Object() {
|
|
|
|
return new ExpressionParser(parsableExpr).parse();
|
|
|
|
int pos = -1, ch;
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
System.err.println("Error parsing expression: '" + expr + "'. Parsable version: '" + parsableExpr + "'. Error: " + e.getMessage());
|
|
|
|
|
|
|
|
return Double.NaN;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nextChar() {
|
|
|
|
|
|
|
|
ch = (++pos < parsableExpr.length()) ? parsableExpr.charAt(pos) : -1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
boolean eat(int charToEat) {
|
|
|
|
private static class ExpressionParser {
|
|
|
|
while (ch == ' ') nextChar();
|
|
|
|
private final String expr;
|
|
|
|
if (ch == charToEat) {
|
|
|
|
private int pos = -1, ch;
|
|
|
|
nextChar();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
double parse() {
|
|
|
|
public ExpressionParser(String expr) {
|
|
|
|
nextChar();
|
|
|
|
this.expr = expr;
|
|
|
|
double x = parseExpression();
|
|
|
|
}
|
|
|
|
if (pos < parsableExpr.length()) throw new RuntimeException("Unexpected: " + (char) ch);
|
|
|
|
|
|
|
|
|
|
|
|
void nextChar() {
|
|
|
|
|
|
|
|
ch = (++pos < expr.length()) ? expr.charAt(pos) : -1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
boolean eat(int charToEat) {
|
|
|
|
|
|
|
|
while (ch == ' ') {
|
|
|
|
|
|
|
|
nextChar();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ch == charToEat) {
|
|
|
|
|
|
|
|
nextChar();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
double parse() {
|
|
|
|
|
|
|
|
nextChar();
|
|
|
|
|
|
|
|
double x = parseExpression();
|
|
|
|
|
|
|
|
if (pos < expr.length()) {
|
|
|
|
|
|
|
|
throw new RuntimeException("Unexpected: " + (char) ch);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return x;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
double parseExpression() {
|
|
|
|
|
|
|
|
double x = parseTerm();
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
|
|
|
if (eat('+')) {
|
|
|
|
|
|
|
|
x += parseTerm();
|
|
|
|
|
|
|
|
} else if (eat('-')) {
|
|
|
|
|
|
|
|
x -= parseTerm();
|
|
|
|
|
|
|
|
} else {
|
|
|
|
return x;
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
double parseExpression() {
|
|
|
|
double parseTerm() {
|
|
|
|
double x = parseTerm();
|
|
|
|
double x = parseFactor();
|
|
|
|
for (; ; ) {
|
|
|
|
for (;;) {
|
|
|
|
if (eat('+')) x += parseTerm(); // addition
|
|
|
|
if (eat('*')) {
|
|
|
|
else if (eat('-')) x -= parseTerm(); // subtraction
|
|
|
|
x *= parseFactor();
|
|
|
|
else return x;
|
|
|
|
} else if (eat('/')) {
|
|
|
|
|
|
|
|
double divisor = parseFactor();
|
|
|
|
|
|
|
|
if (divisor == 0) {
|
|
|
|
|
|
|
|
throw new ArithmeticException("Division by zero");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
x /= divisor;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
double parseTerm() {
|
|
|
|
double parseFactor() {
|
|
|
|
double x = parseFactor();
|
|
|
|
if (eat('+')) {
|
|
|
|
for (; ; ) {
|
|
|
|
return parseFactor();
|
|
|
|
if (eat('*')) x *= parseFactor(); // multiplication
|
|
|
|
}
|
|
|
|
else if (eat('/')) { // division
|
|
|
|
if (eat('-')) {
|
|
|
|
double divisor = parseFactor();
|
|
|
|
return -parseFactor();
|
|
|
|
if (divisor == 0) throw new ArithmeticException("Division by zero");
|
|
|
|
}
|
|
|
|
x /= divisor;
|
|
|
|
|
|
|
|
} else return x;
|
|
|
|
double x;
|
|
|
|
}
|
|
|
|
int startPos = this.pos;
|
|
|
|
|
|
|
|
if (eat('(')) {
|
|
|
|
|
|
|
|
x = parseExpression();
|
|
|
|
|
|
|
|
eat(')');
|
|
|
|
|
|
|
|
} else if ((ch >= '0' && ch <= '9') || ch == '.') {
|
|
|
|
|
|
|
|
while ((ch >= '0' && ch <= '9') || ch == '.') {
|
|
|
|
|
|
|
|
nextChar();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
x = Double.parseDouble(expr.substring(startPos, this.pos));
|
|
|
|
|
|
|
|
} else if (ch >= 'a' && ch <= 'z') {
|
|
|
|
|
|
|
|
while (ch >= 'a' && ch <= 'z') {
|
|
|
|
|
|
|
|
nextChar();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
String func = expr.substring(startPos, this.pos);
|
|
|
|
|
|
|
|
x = parseFunction(func); // 委托给新的辅助方法
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
throw new RuntimeException("Unexpected: " + (char) ch);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return x;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
double parseFactor() {
|
|
|
|
|
|
|
|
if (eat('+')) return parseFactor(); // unary plus
|
|
|
|
|
|
|
|
if (eat('-')) return -parseFactor(); // unary minus
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
double x;
|
|
|
|
|
|
|
|
int startPos = this.pos;
|
|
|
|
|
|
|
|
if (eat('(')) { // parentheses
|
|
|
|
|
|
|
|
x = parseExpression();
|
|
|
|
|
|
|
|
eat(')');
|
|
|
|
|
|
|
|
} else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
|
|
|
|
|
|
|
|
while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
|
|
|
|
|
|
|
|
x = Double.parseDouble(parsableExpr.substring(startPos, this.pos));
|
|
|
|
|
|
|
|
} else if (ch >= 'a' && ch <= 'z') { // functions
|
|
|
|
|
|
|
|
while (ch >= 'a' && ch <= 'z') nextChar();
|
|
|
|
|
|
|
|
String func = parsableExpr.substring(startPos, this.pos);
|
|
|
|
|
|
|
|
x = parseFactor();
|
|
|
|
|
|
|
|
switch (func) {
|
|
|
|
|
|
|
|
case "sqrt":
|
|
|
|
|
|
|
|
x = Math.sqrt(x);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "sin":
|
|
|
|
|
|
|
|
x = Math.sin(Math.toRadians(x));
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "cos":
|
|
|
|
|
|
|
|
x = Math.cos(Math.toRadians(x));
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "tan":
|
|
|
|
|
|
|
|
x = Math.tan(Math.toRadians(x));
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "pow":
|
|
|
|
|
|
|
|
eat(',');
|
|
|
|
|
|
|
|
double y = parseExpression();
|
|
|
|
|
|
|
|
x = Math.pow(x, y);
|
|
|
|
|
|
|
|
eat(')');
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
throw new RuntimeException("Unknown function: " + func);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
throw new RuntimeException("Unexpected: " + (char) ch);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return x;
|
|
|
|
private double parseFunction(String func) {
|
|
|
|
}
|
|
|
|
double x = parseFactor();
|
|
|
|
}.parse();
|
|
|
|
switch (func) {
|
|
|
|
} catch (Exception e) {
|
|
|
|
case "sqrt": return Math.sqrt(x);
|
|
|
|
System.err.println("Error parsing expression: '" + expr + "'. Parsable version: '" + parsableExpr + "'. Error: " + e.getMessage());
|
|
|
|
case "sin": return Math.sin(Math.toRadians(x));
|
|
|
|
return Double.NaN;
|
|
|
|
case "cos": return Math.cos(Math.toRadians(x));
|
|
|
|
|
|
|
|
case "tan": return Math.tan(Math.toRadians(x));
|
|
|
|
|
|
|
|
case "pow":
|
|
|
|
|
|
|
|
eat(',');
|
|
|
|
|
|
|
|
double y = parseExpression();
|
|
|
|
|
|
|
|
eat(')');
|
|
|
|
|
|
|
|
return Math.pow(x, y);
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
throw new RuntimeException("Unknown function: " + func);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -132,61 +154,44 @@ public class ExpressionUtils {
|
|
|
|
return String.format("%.2f", result);
|
|
|
|
return String.format("%.2f", result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* MODIFICATION: 确保为整数答案生成整数干扰项
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private static String generateDistractor(double correctResult, List<String> existingOptions) {
|
|
|
|
private static String generateDistractor(double correctResult, List<String> existingOptions) {
|
|
|
|
if (Double.isNaN(correctResult)) {
|
|
|
|
if (Double.isNaN(correctResult)) {
|
|
|
|
return String.valueOf(rand.nextInt(100));
|
|
|
|
return String.valueOf(rand.nextInt(100));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查正确答案是否为整数
|
|
|
|
|
|
|
|
boolean isIntegerResult = Math.abs(correctResult - Math.round(correctResult)) < 0.0001;
|
|
|
|
boolean isIntegerResult = Math.abs(correctResult - Math.round(correctResult)) < 0.0001;
|
|
|
|
|
|
|
|
|
|
|
|
double distractor;
|
|
|
|
double distractor;
|
|
|
|
String distractorStr;
|
|
|
|
String distractorStr;
|
|
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
do {
|
|
|
|
if (isIntegerResult) {
|
|
|
|
if (isIntegerResult) {
|
|
|
|
// 生成一个整数干扰项
|
|
|
|
int offset = rand.nextInt(20) + 1;
|
|
|
|
int offset = rand.nextInt(20) + 1; // 扩大随机范围以避免重复
|
|
|
|
|
|
|
|
distractor = rand.nextBoolean() ? Math.round(correctResult) + offset : Math.round(correctResult) - offset;
|
|
|
|
distractor = rand.nextBoolean() ? Math.round(correctResult) + offset : Math.round(correctResult) - offset;
|
|
|
|
// 确保干扰项不为负数
|
|
|
|
|
|
|
|
if (distractor < 0) {
|
|
|
|
if (distractor < 0) {
|
|
|
|
distractor = Math.round(correctResult) + offset;
|
|
|
|
distractor = Math.round(correctResult) + offset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
// 答案是小数,则生成小数干扰项
|
|
|
|
distractor = correctResult + (rand.nextInt(10) + 1) * (rand.nextBoolean() ? 1 : -1) + (rand.nextDouble() * 0.9);
|
|
|
|
distractor = correctResult +
|
|
|
|
|
|
|
|
(rand.nextInt(10) + 1) * (rand.nextBoolean() ? 1 : -1) +
|
|
|
|
|
|
|
|
(rand.nextDouble() * 0.9);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
distractorStr = formatResult(distractor);
|
|
|
|
distractorStr = formatResult(distractor);
|
|
|
|
} while (existingOptions.contains(distractorStr)); // 确保选项不重复
|
|
|
|
} while (existingOptions.contains(distractorStr));
|
|
|
|
return distractorStr;
|
|
|
|
return distractorStr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static List<String> generateOptions(double correctResult) {
|
|
|
|
public static List<String> generateOptions(double correctResult) {
|
|
|
|
List<String> options = new ArrayList<>();
|
|
|
|
List<String> options = new ArrayList<>();
|
|
|
|
options.add(formatResult(correctResult));
|
|
|
|
options.add(formatResult(correctResult));
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
options.add(generateDistractor(correctResult, options));
|
|
|
|
options.add(generateDistractor(correctResult, options));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Collections.shuffle(options);
|
|
|
|
Collections.shuffle(options);
|
|
|
|
return options;
|
|
|
|
return options;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static Problem createProblem(String expression) {
|
|
|
|
public static Problem createProblem(String expression) {
|
|
|
|
double result = solveExpression(expression);
|
|
|
|
double result = solveExpression(expression);
|
|
|
|
|
|
|
|
|
|
|
|
// 如果解析失败,返回null,让调用者重新生成
|
|
|
|
|
|
|
|
if (Double.isNaN(result)) {
|
|
|
|
if (Double.isNaN(result)) {
|
|
|
|
return null;
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
String correctAnswerOption = formatResult(result);
|
|
|
|
String correctAnswerOption = formatResult(result);
|
|
|
|
List<String> options = generateOptions(result);
|
|
|
|
List<String> options = generateOptions(result);
|
|
|
|
return new Problem(expression, result, options, correctAnswerOption);
|
|
|
|
return new Problem(expression, result, options, correctAnswerOption);
|
|
|
|
@ -204,46 +209,31 @@ public class ExpressionUtils {
|
|
|
|
return divisors.get(rand.nextInt(divisors.size()));
|
|
|
|
return divisors.get(rand.nextInt(divisors.size()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* MODIFICATION: 增加答案非负、括号数量和操作数关联的逻辑
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public static Problem generatePrimaryExpr() {
|
|
|
|
public static Problem generatePrimaryExpr() {
|
|
|
|
Problem problem;
|
|
|
|
Problem problem;
|
|
|
|
double result;
|
|
|
|
double result;
|
|
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
do {
|
|
|
|
// 生成 2 到 4 个操作数
|
|
|
|
|
|
|
|
int operands = rand.nextInt(3) + 2;
|
|
|
|
int operands = rand.nextInt(3) + 2;
|
|
|
|
List<String> parts = new ArrayList<>();
|
|
|
|
List<String> parts = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < operands; i++) {
|
|
|
|
for (int i = 0; i < operands; i++) {
|
|
|
|
if (i > 0) {
|
|
|
|
if (i > 0) {
|
|
|
|
parts.add(OPS_PRIMARY[rand.nextInt(OPS_PRIMARY.length)]);
|
|
|
|
parts.add(OPS_PRIMARY[rand.nextInt(OPS_PRIMARY.length)]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 仅当操作数 >= 3 时,才有可能生成括号
|
|
|
|
|
|
|
|
if (operands >= 3 && rand.nextBoolean() && i < operands - 1) {
|
|
|
|
if (operands >= 3 && rand.nextBoolean() && i < operands - 1) {
|
|
|
|
int num1 = rand.nextInt(50) + 1;
|
|
|
|
int num1 = rand.nextInt(50) + 1;
|
|
|
|
String innerOp = OPS_PRIMARY[rand.nextInt(OPS_PRIMARY.length)];
|
|
|
|
String innerOp = OPS_PRIMARY[rand.nextInt(OPS_PRIMARY.length)];
|
|
|
|
int num2;
|
|
|
|
int num2 = innerOp.equals("/") ? getDivisor(num1) : rand.nextInt(50) + 1;
|
|
|
|
if (innerOp.equals("/")) {
|
|
|
|
|
|
|
|
num2 = getDivisor(num1);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
num2 = rand.nextInt(50) + 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
parts.add("(" + num1 + " " + innerOp + " " + num2 + ")");
|
|
|
|
parts.add("(" + num1 + " " + innerOp + " " + num2 + ")");
|
|
|
|
i++; // 括号表达式计为2个操作数
|
|
|
|
i++;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
int num;
|
|
|
|
int num;
|
|
|
|
if (i > 0 && parts.get(parts.size() - 1).equals("/")) {
|
|
|
|
if (i > 0 && parts.get(parts.size() - 1).equals("/")) {
|
|
|
|
String dividendStr = parts.get(parts.size() - 2);
|
|
|
|
String dividendStr = parts.get(parts.size() - 2);
|
|
|
|
if (dividendStr.startsWith("(")) {
|
|
|
|
if (dividendStr.startsWith("(")) {
|
|
|
|
// 为避免 (a+b)/c 这种复杂情况无法保证整数结果,直接替换运算符
|
|
|
|
parts.set(parts.size() - 1, OPS_PRIMARY[rand.nextInt(3)]);
|
|
|
|
parts.set(parts.size() - 1, OPS_PRIMARY[rand.nextInt(3)]); // +, -, *
|
|
|
|
|
|
|
|
num = rand.nextInt(100) + 1;
|
|
|
|
num = rand.nextInt(100) + 1;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
int dividend = Integer.parseInt(dividendStr);
|
|
|
|
num = getDivisor(Integer.parseInt(dividendStr));
|
|
|
|
num = getDivisor(dividend);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
num = rand.nextInt(100) + 1;
|
|
|
|
num = rand.nextInt(100) + 1;
|
|
|
|
@ -253,19 +243,15 @@ public class ExpressionUtils {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
String expression = String.join(" ", parts);
|
|
|
|
String expression = String.join(" ", parts);
|
|
|
|
problem = createProblem(expression);
|
|
|
|
problem = createProblem(expression);
|
|
|
|
result = problem.getResult();
|
|
|
|
result = (problem != null) ? problem.getResult() : Double.NaN;
|
|
|
|
// 循环直到答案为非负整数
|
|
|
|
|
|
|
|
} while (result < 0 || Double.isNaN(result) || Math.abs(result - Math.round(result)) > 0.0001);
|
|
|
|
} while (result < 0 || Double.isNaN(result) || Math.abs(result - Math.round(result)) > 0.0001);
|
|
|
|
|
|
|
|
|
|
|
|
return problem;
|
|
|
|
return problem;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static Problem generateMiddleExpr() {
|
|
|
|
public static Problem generateMiddleExpr() {
|
|
|
|
int operands = rand.nextInt(5) + 1;
|
|
|
|
int operands = rand.nextInt(5) + 1;
|
|
|
|
StringBuilder expr = new StringBuilder();
|
|
|
|
StringBuilder expr = new StringBuilder();
|
|
|
|
boolean hasSquareOrSqrt = false;
|
|
|
|
boolean hasSquareOrSqrt = false;
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < operands; i++) {
|
|
|
|
for (int i = 0; i < operands; i++) {
|
|
|
|
if (i > 0) {
|
|
|
|
if (i > 0) {
|
|
|
|
expr.append(" ").append(OPS_MIDDLE[rand.nextInt(OPS_MIDDLE.length)]).append(" ");
|
|
|
|
expr.append(" ").append(OPS_MIDDLE[rand.nextInt(OPS_MIDDLE.length)]).append(" ");
|
|
|
|
@ -278,81 +264,74 @@ public class ExpressionUtils {
|
|
|
|
expr.append(num);
|
|
|
|
expr.append(num);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!hasSquareOrSqrt) {
|
|
|
|
if (!hasSquareOrSqrt) {
|
|
|
|
expr.append(" + ").append(rand.nextInt(50) + 1).append("^2");
|
|
|
|
expr.append(" + ").append(rand.nextInt(50) + 1).append("^2");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return createProblem(expr.toString());
|
|
|
|
return createProblem(expr.toString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static Problem generateHighExpr() {
|
|
|
|
public static Problem generateHighExpr() {
|
|
|
|
int attempts = 0;
|
|
|
|
|
|
|
|
final int maxAttempts = 10;
|
|
|
|
final int maxAttempts = 10;
|
|
|
|
|
|
|
|
for (int attempts = 0; attempts < maxAttempts; attempts++) {
|
|
|
|
while (attempts < maxAttempts) {
|
|
|
|
String expression = buildHighLevelExpression();
|
|
|
|
int operands = rand.nextInt(3) + 2; // 2-4个操作数
|
|
|
|
Problem problem = createProblem(expression);
|
|
|
|
StringBuilder expr = new StringBuilder();
|
|
|
|
if (problem != null) {
|
|
|
|
boolean hasTrig = false;
|
|
|
|
return problem;
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < operands; i++) {
|
|
|
|
|
|
|
|
if (i > 0) {
|
|
|
|
|
|
|
|
String[] validOps = {"+", "-", "*", "/"};
|
|
|
|
|
|
|
|
String op = validOps[rand.nextInt(validOps.length)];
|
|
|
|
|
|
|
|
expr.append(" ").append(op).append(" ");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 强制至少有一个操作数是三角函数
|
|
|
|
|
|
|
|
if (!hasTrig && (i == operands - 1 || rand.nextBoolean())) {
|
|
|
|
|
|
|
|
String[] funcs = {"sin", "cos", "tan"};
|
|
|
|
|
|
|
|
String func = funcs[rand.nextInt(funcs.length)];
|
|
|
|
|
|
|
|
int angle = rand.nextInt(90) + 1; // 1-90度
|
|
|
|
|
|
|
|
expr.append(func).append("(").append(angle).append(")");
|
|
|
|
|
|
|
|
hasTrig = true;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// 其他操作数可以是普通数字、平方或开方
|
|
|
|
|
|
|
|
int num = rand.nextInt(100) + 1;
|
|
|
|
|
|
|
|
if (rand.nextBoolean() && hasTrig) { // 确保已经有三角函数后再添加其他函数
|
|
|
|
|
|
|
|
if (rand.nextBoolean()) {
|
|
|
|
|
|
|
|
expr.append(num).append("^2");
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
expr.append("sqrt(").append(num).append(")");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
expr.append(num);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
System.err.println("生成高中题目失败,尝试次数: " + (attempts + 1) + ", 表达式: " + expression);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return createProblem("sin(30) + cos(60)"); // Fallback
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果没有三角函数,强制添加一个
|
|
|
|
private static String buildHighLevelExpression() {
|
|
|
|
if (!hasTrig) {
|
|
|
|
int operands = rand.nextInt(3) + 2; // 2-4个操作数
|
|
|
|
String[] funcs = {"sin", "cos", "tan"};
|
|
|
|
StringBuilder expr = new StringBuilder();
|
|
|
|
String func = funcs[rand.nextInt(funcs.length)];
|
|
|
|
boolean hasTrig = false;
|
|
|
|
int angle = rand.nextInt(90) + 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (rand.nextBoolean()) {
|
|
|
|
for (int i = 0; i < operands; i++) {
|
|
|
|
// 在开头添加
|
|
|
|
if (i > 0) {
|
|
|
|
expr.insert(0, func + "(" + angle + ") + ");
|
|
|
|
String[] validOps = {"+", "-", "*", "/"};
|
|
|
|
} else {
|
|
|
|
expr.append(" ").append(validOps[rand.nextInt(validOps.length)]).append(" ");
|
|
|
|
// 在结尾添加
|
|
|
|
}
|
|
|
|
expr.append(" + ").append(func).append("(").append(angle).append(")");
|
|
|
|
if (!hasTrig && (i == operands - 1 || rand.nextBoolean())) {
|
|
|
|
}
|
|
|
|
appendTrigonometricFunction(expr);
|
|
|
|
hasTrig = true;
|
|
|
|
hasTrig = true;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
appendOtherOperand(expr, hasTrig);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ensureTrigonometricFunction(expr, hasTrig);
|
|
|
|
|
|
|
|
return expr.toString();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
String expression = expr.toString();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 验证表达式
|
|
|
|
private static void appendTrigonometricFunction(StringBuilder expr) {
|
|
|
|
Problem problem = createProblem(expression);
|
|
|
|
String[] funcs = {"sin", "cos", "tan"};
|
|
|
|
if (problem != null) {
|
|
|
|
String func = funcs[rand.nextInt(funcs.length)];
|
|
|
|
return problem;
|
|
|
|
int angle = rand.nextInt(90) + 1;
|
|
|
|
}
|
|
|
|
expr.append(func).append("(").append(angle).append(")");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
attempts++;
|
|
|
|
|
|
|
|
System.err.println("生成高中题目失败,尝试次数: " + attempts + ", 表达式: " + expression);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return createProblem("sin(30) + cos(60)");
|
|
|
|
private static void appendOtherOperand(StringBuilder expr, boolean hasTrig) {
|
|
|
|
|
|
|
|
int num = rand.nextInt(100) + 1;
|
|
|
|
|
|
|
|
if (hasTrig && rand.nextBoolean()) {
|
|
|
|
|
|
|
|
expr.append(rand.nextBoolean() ? num + "^2" : "sqrt(" + num + ")");
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
expr.append(num);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static void ensureTrigonometricFunction(StringBuilder expr, boolean hasTrig) {
|
|
|
|
|
|
|
|
if (!hasTrig) {
|
|
|
|
|
|
|
|
String trigPart = " + " + "sin" + "(" + (rand.nextInt(90) + 1) + ")";
|
|
|
|
|
|
|
|
if (rand.nextBoolean()) {
|
|
|
|
|
|
|
|
expr.insert(0, trigPart.substring(3) + " + ");
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
expr.append(trigPart);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|