|
|
|
|
@ -7,186 +7,186 @@ import java.util.Random;
|
|
|
|
|
import java.util.Set;
|
|
|
|
|
|
|
|
|
|
public abstract class BaseQuestionGenerator {
|
|
|
|
|
protected Random random = new Random();
|
|
|
|
|
protected Random random = new Random();
|
|
|
|
|
|
|
|
|
|
public abstract Question generateQuestion(int index);
|
|
|
|
|
public abstract Question generateQuestion(int index);
|
|
|
|
|
|
|
|
|
|
protected void generateOperands(int[] operands, int min, int max) {
|
|
|
|
|
for (int i = 0; i < operands.length; i++) {
|
|
|
|
|
operands[i] = random.nextInt(max - min + 1) + min;
|
|
|
|
|
}
|
|
|
|
|
protected void generateOperands(int[] operands, int min, int max) {
|
|
|
|
|
for (int i = 0; i < operands.length; i++) {
|
|
|
|
|
operands[i] = random.nextInt(max - min + 1) + min;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void generateOperations(int[] operations, int operationTypes) {
|
|
|
|
|
for (int i = 0; i < operations.length; i++) {
|
|
|
|
|
operations[i] = random.nextInt(operationTypes);
|
|
|
|
|
}
|
|
|
|
|
protected void generateOperations(int[] operations, int operationTypes) {
|
|
|
|
|
for (int i = 0; i < operations.length; i++) {
|
|
|
|
|
operations[i] = random.nextInt(operationTypes);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void appendOperator(StringBuilder sb, int operation) {
|
|
|
|
|
switch (operation) {
|
|
|
|
|
case 0: sb.append(" + "); break;
|
|
|
|
|
case 1: sb.append(" - "); break;
|
|
|
|
|
case 2: sb.append(" × "); break;
|
|
|
|
|
case 3: sb.append(" ÷ "); break;
|
|
|
|
|
}
|
|
|
|
|
protected void appendOperator(StringBuilder sb, int operation) {
|
|
|
|
|
switch (operation) {
|
|
|
|
|
case 0: sb.append(" + "); break;
|
|
|
|
|
case 1: sb.append(" - "); break;
|
|
|
|
|
case 2: sb.append(" × "); break;
|
|
|
|
|
case 3: sb.append(" ÷ "); break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected boolean isValidAnswer(double answer) {
|
|
|
|
|
return !(answer < 0 || Double.isNaN(answer) || Double.isInfinite(answer));
|
|
|
|
|
}
|
|
|
|
|
protected boolean isValidAnswer(double answer) {
|
|
|
|
|
return !(answer < 0 || Double.isNaN(answer) || Double.isInfinite(answer));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 修改:使用double进行精确计算,只在最后保留小数
|
|
|
|
|
protected double calculateExpression(double[] values, int[] operations) {
|
|
|
|
|
List<Double> numbers = new ArrayList<>();
|
|
|
|
|
List<Integer> ops = new ArrayList<>();
|
|
|
|
|
// 修改:使用double进行精确计算,只在最后保留小数
|
|
|
|
|
protected double calculateExpression(double[] values, int[] operations) {
|
|
|
|
|
List<Double> numbers = new ArrayList<>();
|
|
|
|
|
List<Integer> ops = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
for (double value : values) numbers.add(value);
|
|
|
|
|
for (int operation : operations) ops.add(operation);
|
|
|
|
|
for (double value : values) numbers.add(value);
|
|
|
|
|
for (int operation : operations) ops.add(operation);
|
|
|
|
|
|
|
|
|
|
processMultiplicationAndDivision(numbers, ops);
|
|
|
|
|
return processAdditionAndSubtraction(numbers, ops);
|
|
|
|
|
}
|
|
|
|
|
processMultiplicationAndDivision(numbers, ops);
|
|
|
|
|
return processAdditionAndSubtraction(numbers, ops);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected double calculateWithoutParentheses(int[] operands, int[] operations) {
|
|
|
|
|
double[] doubleOperands = new double[operands.length];
|
|
|
|
|
for (int i = 0; i < operands.length; i++) {
|
|
|
|
|
doubleOperands[i] = operands[i];
|
|
|
|
|
}
|
|
|
|
|
return calculateExpression(doubleOperands, operations);
|
|
|
|
|
protected double calculateWithoutParentheses(int[] operands, int[] operations) {
|
|
|
|
|
double[] doubleOperands = new double[operands.length];
|
|
|
|
|
for (int i = 0; i < operands.length; i++) {
|
|
|
|
|
doubleOperands[i] = operands[i];
|
|
|
|
|
}
|
|
|
|
|
return calculateExpression(doubleOperands, operations);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected double calculateWithoutParentheses(List<Integer> numbers, List<Integer> ops) {
|
|
|
|
|
double[] doubleNumbers = new double[numbers.size()];
|
|
|
|
|
for (int i = 0; i < numbers.size(); i++) {
|
|
|
|
|
doubleNumbers[i] = numbers.get(i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int[] intOps = new int[ops.size()];
|
|
|
|
|
for (int i = 0; i < ops.size(); i++) {
|
|
|
|
|
intOps[i] = ops.get(i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return calculateExpression(doubleNumbers, intOps);
|
|
|
|
|
protected double calculateWithoutParentheses(List<Integer> numbers, List<Integer> ops) {
|
|
|
|
|
double[] doubleNumbers = new double[numbers.size()];
|
|
|
|
|
for (int i = 0; i < numbers.size(); i++) {
|
|
|
|
|
doubleNumbers[i] = numbers.get(i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void processMultiplicationAndDivision(List<Double> numbers, List<Integer> ops) {
|
|
|
|
|
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 = performOperation(left, right, op);
|
|
|
|
|
|
|
|
|
|
numbers.set(i, result);
|
|
|
|
|
numbers.remove(i + 1);
|
|
|
|
|
ops.remove(i);
|
|
|
|
|
i--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
int[] intOps = new int[ops.size()];
|
|
|
|
|
for (int i = 0; i < ops.size(); i++) {
|
|
|
|
|
intOps[i] = ops.get(i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private double processAdditionAndSubtraction(List<Double> numbers, List<Integer> ops) {
|
|
|
|
|
double result = numbers.get(0);
|
|
|
|
|
return calculateExpression(doubleNumbers, intOps);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < ops.size(); i++) {
|
|
|
|
|
int op = ops.get(i);
|
|
|
|
|
double nextNum = numbers.get(i + 1);
|
|
|
|
|
result = performOperation(result, nextNum, op);
|
|
|
|
|
}
|
|
|
|
|
private void processMultiplicationAndDivision(List<Double> numbers, List<Integer> ops) {
|
|
|
|
|
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 = performOperation(left, right, op);
|
|
|
|
|
|
|
|
|
|
// 修改:只在最后一步进行四舍五入保留2位小数
|
|
|
|
|
return Math.round(result * 100) / 100.0;
|
|
|
|
|
numbers.set(i, result);
|
|
|
|
|
numbers.remove(i + 1);
|
|
|
|
|
ops.remove(i);
|
|
|
|
|
i--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private double performOperation(double left, double right, int operation) {
|
|
|
|
|
switch (operation) {
|
|
|
|
|
case 0: return left + right;
|
|
|
|
|
case 1: return left - right;
|
|
|
|
|
case 2: return left * right;
|
|
|
|
|
case 3:
|
|
|
|
|
if (right == 0) return Double.NaN;
|
|
|
|
|
// 修改:直接进行除法,不在这里保留小数
|
|
|
|
|
return left / right;
|
|
|
|
|
default: return left;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected Question generateOptions(String questionText, double answer, String level) {
|
|
|
|
|
String[] options = new String[4];
|
|
|
|
|
Set<String> usedValues = new HashSet<>();
|
|
|
|
|
|
|
|
|
|
// 修改:判断是否为整数的逻辑
|
|
|
|
|
boolean isIntegerAnswer = Math.abs(answer - Math.round(answer)) < 1e-10;
|
|
|
|
|
String correctAnswer = formatAnswer(answer, isIntegerAnswer);
|
|
|
|
|
|
|
|
|
|
options[0] = correctAnswer;
|
|
|
|
|
usedValues.add(correctAnswer);
|
|
|
|
|
|
|
|
|
|
generateWrongOptions(options, usedValues, answer, isIntegerAnswer);
|
|
|
|
|
shuffleArray(options);
|
|
|
|
|
private double processAdditionAndSubtraction(List<Double> numbers, List<Integer> ops) {
|
|
|
|
|
double result = numbers.get(0);
|
|
|
|
|
|
|
|
|
|
int correctIndex = findCorrectIndex(options, correctAnswer);
|
|
|
|
|
return new Question(questionText, options, correctIndex, level);
|
|
|
|
|
for (int i = 0; i < ops.size(); i++) {
|
|
|
|
|
int op = ops.get(i);
|
|
|
|
|
double nextNum = numbers.get(i + 1);
|
|
|
|
|
result = performOperation(result, nextNum, op);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void generateWrongOptions(String[] options, Set<String> usedValues,
|
|
|
|
|
double answer, boolean isIntegerAnswer) {
|
|
|
|
|
for (int i = 1; i < 4; i++) {
|
|
|
|
|
String wrongAnswer;
|
|
|
|
|
int attempts = 0;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
double wrongValue = generateWrongValue(answer, isIntegerAnswer);
|
|
|
|
|
wrongAnswer = formatAnswer(wrongValue,
|
|
|
|
|
Math.abs(wrongValue - Math.round(wrongValue)) < 1e-10);
|
|
|
|
|
attempts++;
|
|
|
|
|
} while (usedValues.contains(wrongAnswer) && attempts < 20);
|
|
|
|
|
|
|
|
|
|
options[i] = wrongAnswer;
|
|
|
|
|
usedValues.add(wrongAnswer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private double generateWrongValue(double answer, boolean isIntegerAnswer) {
|
|
|
|
|
double offset = (random.nextDouble() * 5) + 1;
|
|
|
|
|
|
|
|
|
|
if (isIntegerAnswer) {
|
|
|
|
|
int intAnswer = (int) Math.round(answer);
|
|
|
|
|
int intOffset = random.nextInt(10) + 1;
|
|
|
|
|
double wrongValue = random.nextBoolean() ?
|
|
|
|
|
intAnswer + intOffset : Math.max(1, intAnswer - intOffset);
|
|
|
|
|
return wrongValue;
|
|
|
|
|
} else {
|
|
|
|
|
double wrongValue = random.nextBoolean() ? answer + offset : answer - offset;
|
|
|
|
|
// 修改:错误选项也保留2位小数
|
|
|
|
|
return Math.round(wrongValue * 100) / 100.0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 修改:格式化答案,只在最后一步处理小数位数
|
|
|
|
|
private String formatAnswer(double answer, boolean isInteger) {
|
|
|
|
|
if (isInteger) {
|
|
|
|
|
return String.valueOf((int) Math.round(answer));
|
|
|
|
|
} else {
|
|
|
|
|
// 确保显示2位小数,即使末尾是0
|
|
|
|
|
return String.format("%.2f", answer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 修改:只在最后一步进行四舍五入保留2位小数
|
|
|
|
|
return Math.round(result * 100) / 100.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private int findCorrectIndex(String[] options, String correctAnswer) {
|
|
|
|
|
for (int i = 0; i < options.length; i++) {
|
|
|
|
|
if (options[i].equals(correctAnswer)) return i;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
private double performOperation(double left, double right, int operation) {
|
|
|
|
|
switch (operation) {
|
|
|
|
|
case 0: return left + right;
|
|
|
|
|
case 1: return left - right;
|
|
|
|
|
case 2: return left * right;
|
|
|
|
|
case 3:
|
|
|
|
|
if (right == 0) return Double.NaN;
|
|
|
|
|
// 修改:直接进行除法,不在这里保留小数
|
|
|
|
|
return left / right;
|
|
|
|
|
default: return left;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected Question generateOptions(String questionText, double answer, String level) {
|
|
|
|
|
String[] options = new String[4];
|
|
|
|
|
Set<String> usedValues = new HashSet<>();
|
|
|
|
|
|
|
|
|
|
// 修改:判断是否为整数的逻辑
|
|
|
|
|
boolean isIntegerAnswer = Math.abs(answer - Math.round(answer)) < 1e-10;
|
|
|
|
|
String correctAnswer = formatAnswer(answer, isIntegerAnswer);
|
|
|
|
|
|
|
|
|
|
options[0] = correctAnswer;
|
|
|
|
|
usedValues.add(correctAnswer);
|
|
|
|
|
|
|
|
|
|
generateWrongOptions(options, usedValues, answer, isIntegerAnswer);
|
|
|
|
|
shuffleArray(options);
|
|
|
|
|
|
|
|
|
|
int correctIndex = findCorrectIndex(options, correctAnswer);
|
|
|
|
|
return new Question(questionText, options, correctIndex, level);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void generateWrongOptions(String[] options, Set<String> usedValues,
|
|
|
|
|
double answer, boolean isIntegerAnswer) {
|
|
|
|
|
for (int i = 1; i < 4; i++) {
|
|
|
|
|
String wrongAnswer;
|
|
|
|
|
int attempts = 0;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
double wrongValue = generateWrongValue(answer, isIntegerAnswer);
|
|
|
|
|
wrongAnswer = formatAnswer(wrongValue,
|
|
|
|
|
Math.abs(wrongValue - Math.round(wrongValue)) < 1e-10);
|
|
|
|
|
attempts++;
|
|
|
|
|
} while (usedValues.contains(wrongAnswer) && attempts < 20);
|
|
|
|
|
|
|
|
|
|
options[i] = wrongAnswer;
|
|
|
|
|
usedValues.add(wrongAnswer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private double generateWrongValue(double answer, boolean isIntegerAnswer) {
|
|
|
|
|
double offset = (random.nextDouble() * 5) + 1;
|
|
|
|
|
|
|
|
|
|
if (isIntegerAnswer) {
|
|
|
|
|
int intAnswer = (int) Math.round(answer);
|
|
|
|
|
int intOffset = random.nextInt(10) + 1;
|
|
|
|
|
double wrongValue = random.nextBoolean() ?
|
|
|
|
|
intAnswer + intOffset : Math.max(1, intAnswer - intOffset);
|
|
|
|
|
return wrongValue;
|
|
|
|
|
} else {
|
|
|
|
|
double wrongValue = random.nextBoolean() ? answer + offset : answer - offset;
|
|
|
|
|
// 修改:错误选项也保留2位小数
|
|
|
|
|
return Math.round(wrongValue * 100) / 100.0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 修改:格式化答案,只在最后一步处理小数位数
|
|
|
|
|
private String formatAnswer(double answer, boolean isInteger) {
|
|
|
|
|
if (isInteger) {
|
|
|
|
|
return String.valueOf((int) Math.round(answer));
|
|
|
|
|
} else {
|
|
|
|
|
// 确保显示2位小数,即使末尾是0
|
|
|
|
|
return String.format("%.2f", answer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private int findCorrectIndex(String[] options, String correctAnswer) {
|
|
|
|
|
for (int i = 0; i < options.length; i++) {
|
|
|
|
|
if (options[i].equals(correctAnswer)) return i;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|