|
|
package com.mathlearning.model;
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.HashSet;
|
|
|
import java.util.List;
|
|
|
import java.util.Random;
|
|
|
import java.util.Set;
|
|
|
|
|
|
public class QuestionGenerator {
|
|
|
private Random random = new Random();
|
|
|
|
|
|
public List<Question> generateQuestions(String level, int count) {
|
|
|
List<Question> questions = new ArrayList<>();
|
|
|
Set<String> questionTexts = new HashSet<>(); // 防止重复题目
|
|
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
Question question;
|
|
|
int attempt = 0;
|
|
|
do {
|
|
|
question = generateQuestion(level, i);
|
|
|
attempt++;
|
|
|
} while (questionTexts.contains(question.getQuestionText()) && attempt < 10);
|
|
|
|
|
|
if (question != null) {
|
|
|
questionTexts.add(question.getQuestionText());
|
|
|
questions.add(question);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return questions;
|
|
|
}
|
|
|
|
|
|
private Question generateQuestion(String level, int index) {
|
|
|
switch (level) {
|
|
|
case "小学":
|
|
|
return generatePrimaryQuestion(index);
|
|
|
case "初中":
|
|
|
return generateMiddleSchoolQuestion(index);
|
|
|
case "高中":
|
|
|
return generateHighSchoolQuestion(index);
|
|
|
default:
|
|
|
return generatePrimaryQuestion(index);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private Question generatePrimaryQuestion(int index) {
|
|
|
int operandCount = random.nextInt(4) + 2; // 2-5个操作数
|
|
|
int[] operands = new int[operandCount];
|
|
|
int[] operations = new int[operandCount - 1];
|
|
|
|
|
|
String questionText;
|
|
|
double answer;
|
|
|
|
|
|
do {
|
|
|
// 生成操作数
|
|
|
for (int i = 0; i < operandCount; i++) {
|
|
|
operands[i] = random.nextInt(100) + 1; // 1-100
|
|
|
}
|
|
|
|
|
|
// 生成操作符 (0: +, 1: -, 2: *, 3: /)
|
|
|
for (int i = 0; i < operations.length; i++) {
|
|
|
operations[i] = random.nextInt(4);
|
|
|
}
|
|
|
|
|
|
// 改进的括号逻辑:在可能改变计算顺序的地方加括号
|
|
|
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++;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
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, "小学");
|
|
|
}
|
|
|
|
|
|
private Question generateMiddleSchoolQuestion(int index) {
|
|
|
int operandCount = random.nextInt(5) + 1; // 1-5个操作数
|
|
|
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:开根号
|
|
|
|
|
|
String questionText;
|
|
|
double answer;
|
|
|
|
|
|
// 确保至少有一个平方或开根号运算符
|
|
|
boolean hasSpecialOperation = false;
|
|
|
for (int i = 0; i < operandCount; i++) {
|
|
|
hasSpecialOp[i] = random.nextDouble() < 0.6; // 60%的概率有特殊运算
|
|
|
if (hasSpecialOp[i]) {
|
|
|
hasSpecialOperation = true;
|
|
|
specialOpTypes[i] = random.nextInt(2); // 随机选择平方或开根号
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 如果没有特殊运算,强制至少一个
|
|
|
if (!hasSpecialOperation && operandCount > 0) {
|
|
|
int idx = random.nextInt(operandCount);
|
|
|
hasSpecialOp[idx] = true;
|
|
|
specialOpTypes[idx] = random.nextInt(2);
|
|
|
}
|
|
|
|
|
|
// 生成合适的题目
|
|
|
do {
|
|
|
// 生成操作数
|
|
|
for (int i = 0; i < operandCount; i++) {
|
|
|
operands[i] = random.nextInt(100) + 1; // 1-100
|
|
|
}
|
|
|
|
|
|
// 生成操作符 (0: +, 1: -, 2: *, 3: /)
|
|
|
for (int i = 0; i < operations.length; i++) {
|
|
|
operations[i] = random.nextInt(4);
|
|
|
}
|
|
|
|
|
|
// 构建题目并计算答案
|
|
|
questionText = buildMiddleSchoolQuestionText(operands, operations, hasSpecialOp, specialOpTypes);
|
|
|
answer = calculateMiddleSchoolAnswer(operands, operations, hasSpecialOp, specialOpTypes);
|
|
|
|
|
|
} while (Double.isNaN(answer) || Double.isInfinite(answer) || Math.abs(answer) > 10000);
|
|
|
|
|
|
return generateOptions(questionText, answer, "初中");
|
|
|
}
|
|
|
|
|
|
private Question generateHighSchoolQuestion(int index) {
|
|
|
int operandCount = random.nextInt(5) + 1; // 1-5个操作数
|
|
|
int[] operands = new int[operandCount];
|
|
|
int[] operations = new int[Math.max(0, operandCount - 1)]; // 操作符数组
|
|
|
boolean[] hasTrigOp = new boolean[operandCount]; // 标记哪些操作数有三角函数
|
|
|
int[] trigFunctions = new int[operandCount]; // 0:sin, 1:cos, 2:tan
|
|
|
|
|
|
String questionText;
|
|
|
double answer;
|
|
|
|
|
|
// 确保至少有一个三角函数运算符
|
|
|
boolean hasTrigOperation = false;
|
|
|
for (int i = 0; i < operandCount; i++) {
|
|
|
hasTrigOp[i] = random.nextDouble() < 0.6; // 60%的概率有三角函数
|
|
|
if (hasTrigOp[i]) {
|
|
|
hasTrigOperation = true;
|
|
|
trigFunctions[i] = random.nextInt(3); // 随机选择三角函数
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 如果没有三角函数,强制至少一个
|
|
|
if (!hasTrigOperation && operandCount > 0) {
|
|
|
int idx = random.nextInt(operandCount);
|
|
|
hasTrigOp[idx] = true;
|
|
|
trigFunctions[idx] = random.nextInt(3);
|
|
|
}
|
|
|
|
|
|
// 生成合适的题目
|
|
|
do {
|
|
|
// 生成操作数
|
|
|
for (int i = 0; i < operandCount; i++) {
|
|
|
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: /)
|
|
|
for (int i = 0; i < operations.length; i++) {
|
|
|
operations[i] = random.nextInt(4);
|
|
|
}
|
|
|
|
|
|
// 构建题目并计算答案
|
|
|
questionText = buildHighSchoolQuestionText(operands, operations, hasTrigOp, trigFunctions);
|
|
|
answer = calculateHighSchoolAnswer(operands, operations, hasTrigOp, trigFunctions);
|
|
|
|
|
|
} while (Double.isNaN(answer) || Double.isInfinite(answer) || Math.abs(answer) > 10000);
|
|
|
|
|
|
return generateOptions(questionText, answer, "高中");
|
|
|
}
|
|
|
|
|
|
// 小学题目相关方法
|
|
|
private int findSuitableParenthesesPosition(int[] operations) {
|
|
|
// 寻找加减法操作符的位置,在这些地方加括号才有意义
|
|
|
List<Integer> 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 == 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(" = ?");
|
|
|
|
|
|
return sb.toString();
|
|
|
}
|
|
|
|
|
|
private double calculatePrimaryAnswerWithParentheses(int[] operands, int[] operations, int parenStart, int parenEnd) {
|
|
|
// 如果没有括号,使用原来的计算方法
|
|
|
if (parenStart == -1 || parenEnd == -1) {
|
|
|
return calculatePrimaryAnswer(operands, operations);
|
|
|
}
|
|
|
|
|
|
// 复制操作数和操作符
|
|
|
List<Integer> numbers = new ArrayList<>();
|
|
|
List<Integer> ops = new ArrayList<>();
|
|
|
|
|
|
for (int operand : operands) {
|
|
|
numbers.add(operand);
|
|
|
}
|
|
|
for (int operation : operations) {
|
|
|
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<Integer> numbers, List<Integer> ops, int start, int end) {
|
|
|
// 提取括号内的数字和操作符
|
|
|
List<Integer> parenNumbers = new ArrayList<>();
|
|
|
List<Integer> 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<Integer> numbers, List<Integer> 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<Integer> numbers, List<Integer> ops) {
|
|
|
// 转换为double列表进行计算(精确计算)
|
|
|
List<Double> 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) {
|
|
|
double left = doubleNumbers.get(i);
|
|
|
double right = doubleNumbers.get(i + 1);
|
|
|
double result;
|
|
|
|
|
|
if (op == 2) {
|
|
|
result = left * right;
|
|
|
} else {
|
|
|
if (right == 0) return Double.NaN;
|
|
|
result = left / right;
|
|
|
}
|
|
|
|
|
|
doubleNumbers.set(i, result);
|
|
|
doubleNumbers.remove(i + 1);
|
|
|
ops.remove(i);
|
|
|
i--;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 处理加减法(精确计算)
|
|
|
double result = doubleNumbers.get(0);
|
|
|
for (int i = 0; i < ops.size(); i++) {
|
|
|
int op = ops.get(i);
|
|
|
double nextNum = doubleNumbers.get(i + 1);
|
|
|
|
|
|
if (op == 0) {
|
|
|
result += nextNum;
|
|
|
} else {
|
|
|
if (result < nextNum) return -1;
|
|
|
result -= nextNum;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 只在最后结果保留2位小数
|
|
|
return Math.round(result * 100) / 100.0;
|
|
|
}
|
|
|
|
|
|
private double calculatePrimaryAnswer(int[] operands, int[] operations) {
|
|
|
// 转换为double列表(精确计算)
|
|
|
List<Double> numbers = new ArrayList<>();
|
|
|
List<Integer> ops = new ArrayList<>();
|
|
|
|
|
|
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--;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 处理加减法(精确计算)
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
// 初中题目相关方法
|
|
|
private String buildMiddleSchoolQuestionText(int[] operands, int[] operations, boolean[] hasSpecialOp, int[] specialOpTypes) {
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
for (int i = 0; i < operands.length; i++) {
|
|
|
if (i > 0) {
|
|
|
switch (operations[i - 1]) {
|
|
|
case 0: sb.append(" + "); break;
|
|
|
case 1: sb.append(" - "); break;
|
|
|
case 2: sb.append(" × "); break;
|
|
|
case 3: sb.append(" ÷ "); break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (hasSpecialOp[i]) {
|
|
|
if (specialOpTypes[i] == 0) {
|
|
|
// 平方
|
|
|
sb.append(operands[i]).append("²");
|
|
|
} else {
|
|
|
// 开根号
|
|
|
sb.append("√").append(operands[i]);
|
|
|
}
|
|
|
} else {
|
|
|
sb.append(operands[i]);
|
|
|
}
|
|
|
}
|
|
|
sb.append(" = ?");
|
|
|
|
|
|
return sb.toString();
|
|
|
}
|
|
|
|
|
|
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]) {
|
|
|
if (specialOpTypes[i] == 0) {
|
|
|
// 平方
|
|
|
processedValues[i] = operands[i] * operands[i];
|
|
|
} else {
|
|
|
// 开根号
|
|
|
processedValues[i] = Math.sqrt(operands[i]);
|
|
|
}
|
|
|
} else {
|
|
|
processedValues[i] = operands[i];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 然后按照小学的计算逻辑处理(精确计算)
|
|
|
List<Double> numbers = new ArrayList<>();
|
|
|
List<Integer> ops = new ArrayList<>();
|
|
|
|
|
|
for (double value : processedValues) {
|
|
|
numbers.add(value);
|
|
|
}
|
|
|
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--;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 处理加减法(精确计算)
|
|
|
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 {
|
|
|
result -= nextNum;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 只在最后结果保留2位小数
|
|
|
return Math.round(result * 100) / 100.0;
|
|
|
}
|
|
|
|
|
|
// 高中题目相关方法
|
|
|
private String buildHighSchoolQuestionText(int[] operands, int[] operations, boolean[] hasTrigOp, int[] trigFunctions) {
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
for (int i = 0; i < operands.length; i++) {
|
|
|
if (i > 0) {
|
|
|
switch (operations[i - 1]) {
|
|
|
case 0: sb.append(" + "); break;
|
|
|
case 1: sb.append(" - "); break;
|
|
|
case 2: sb.append(" × "); break;
|
|
|
case 3: sb.append(" ÷ "); break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (hasTrigOp[i]) {
|
|
|
switch (trigFunctions[i]) {
|
|
|
case 0: sb.append("sin(").append(operands[i]).append("°)"); break;
|
|
|
case 1: sb.append("cos(").append(operands[i]).append("°)"); break;
|
|
|
case 2: sb.append("tan(").append(operands[i]).append("°)"); break;
|
|
|
}
|
|
|
} else {
|
|
|
sb.append(operands[i]);
|
|
|
}
|
|
|
}
|
|
|
sb.append(" = ?");
|
|
|
|
|
|
return sb.toString();
|
|
|
}
|
|
|
|
|
|
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:
|
|
|
// 避免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<Double> numbers = new ArrayList<>();
|
|
|
List<Integer> ops = new ArrayList<>();
|
|
|
|
|
|
for (double value : processedValues) {
|
|
|
numbers.add(value);
|
|
|
}
|
|
|
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--;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 处理加减法(精确计算)
|
|
|
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 {
|
|
|
result -= nextNum;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 只在最后结果保留2位小数
|
|
|
return Math.round(result * 100) / 100.0;
|
|
|
}
|
|
|
|
|
|
// 生成选项的通用方法
|
|
|
private Question generateOptions(String questionText, double answer, String level) {
|
|
|
String[] options = new String[4];
|
|
|
Set<String> usedValues = new HashSet<>();
|
|
|
|
|
|
// 检查答案是否为整数
|
|
|
boolean isIntegerAnswer = (answer == (int) answer);
|
|
|
|
|
|
// 格式化正确答案
|
|
|
String correctAnswer = formatAnswer(answer);
|
|
|
options[0] = correctAnswer;
|
|
|
usedValues.add(correctAnswer);
|
|
|
|
|
|
// 生成错误答案
|
|
|
for (int i = 1; i < 4; i++) {
|
|
|
String wrongAnswer;
|
|
|
int attempts = 0;
|
|
|
do {
|
|
|
// 基于正确答案生成错误答案
|
|
|
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);
|
|
|
|
|
|
options[i] = wrongAnswer;
|
|
|
usedValues.add(wrongAnswer);
|
|
|
}
|
|
|
|
|
|
// 打乱选项顺序
|
|
|
shuffleArray(options);
|
|
|
|
|
|
// 找到正确答案的新位置
|
|
|
int correctIndex = 0;
|
|
|
for (int i = 0; i < options.length; i++) {
|
|
|
if (options[i].equals(correctAnswer)) {
|
|
|
correctIndex = i;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
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);
|
|
|
String temp = array[i];
|
|
|
array[i] = array[j];
|
|
|
array[j] = temp;
|
|
|
}
|
|
|
}
|
|
|
} |