|
|
package service;
|
|
|
|
|
|
import model.Question;
|
|
|
import java.util.List;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.Stack;
|
|
|
|
|
|
public class PrimaryQuestionGenerator extends AbstractQuestionGenerator {
|
|
|
private static final String[] OPERATORS = {"+", "-" , "×", "÷"};
|
|
|
|
|
|
public PrimaryQuestionGenerator() {
|
|
|
super("小学");
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public Question generateQuestion() {
|
|
|
int operandCount = random.nextInt(4) + 2; // 2-5个操作数
|
|
|
List<Integer> operands = new ArrayList<>();
|
|
|
List<String> operators = new ArrayList<>();
|
|
|
|
|
|
// 生成操作数和运算符,确保结果为非负整数
|
|
|
boolean valid = false;
|
|
|
String content = "";
|
|
|
int answer = 0;
|
|
|
int attempts = 0;
|
|
|
int maxAttempts = 100; // 添加最大尝试次数
|
|
|
|
|
|
while (!valid && attempts < maxAttempts) {
|
|
|
attempts++; // 计数
|
|
|
operands.clear();
|
|
|
operators.clear();
|
|
|
// 生成操作数
|
|
|
for (int i = 0; i < operandCount; i++) {
|
|
|
operands.add(getRandomOperand());
|
|
|
}
|
|
|
// 生成运算符,限制最多只有一个除法
|
|
|
boolean hasDivision = false;
|
|
|
for (int i = 0; i < operandCount - 1; i++) {
|
|
|
String op;
|
|
|
// 如果已经有除法了,就只生成其他运算符
|
|
|
if (hasDivision) {
|
|
|
String[] otherOperators = {"+", "-", "×"};
|
|
|
op = getRandomOperator(otherOperators);
|
|
|
} else {
|
|
|
op = getRandomOperator(OPERATORS);
|
|
|
if (op.equals("÷")) {
|
|
|
hasDivision = true;
|
|
|
// 确保除法能整除:调整被除数
|
|
|
int divisor = operands.get(i + 1);
|
|
|
int dividend = operands.get(i);
|
|
|
// 如果当前不能整除,调整被除数
|
|
|
if (divisor == 0 || dividend % divisor != 0) {
|
|
|
// 如果除数为0,重新生成除数
|
|
|
if (divisor == 0) {
|
|
|
divisor = random.nextInt(9) + 2; // 2-10
|
|
|
operands.set(i + 1, divisor);
|
|
|
}
|
|
|
// 调整被除数为除数的倍数
|
|
|
int multiple = random.nextInt(3) + 1; // 1-3倍
|
|
|
operands.set(i, divisor * multiple);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
operators.add(op);
|
|
|
}
|
|
|
content = buildPrimaryContent(operands, operators);
|
|
|
answer = calculateExpression(content);
|
|
|
valid = answer >= 0; // 验证答案是否非负整数
|
|
|
}
|
|
|
// 如果多次尝试都失败,生成一个简单的保底题目
|
|
|
if (!valid) {
|
|
|
return generateSimpleQuestion();
|
|
|
}
|
|
|
OptionsResult options = generateOptions(answer);
|
|
|
return new Question(content, options, options.getCorrectIndex(), difficulty);
|
|
|
}
|
|
|
|
|
|
// 生成简单的保底题目
|
|
|
private Question generateSimpleQuestion() {
|
|
|
int a = random.nextInt(10) + 5; // 5-14
|
|
|
int b = random.nextInt(5) + 1; // 1-5
|
|
|
String[] simpleOps = {"+", "×"};
|
|
|
String op = simpleOps[random.nextInt(simpleOps.length)];
|
|
|
|
|
|
String content;
|
|
|
int answer;
|
|
|
|
|
|
if (op.equals("+")) {
|
|
|
content = a + " + " + b + " = ?";
|
|
|
answer = a + b;
|
|
|
} else {
|
|
|
content = a + " × " + b + " = ?";
|
|
|
answer = a * b;
|
|
|
}
|
|
|
OptionsResult options = generateOptions(answer);
|
|
|
return new Question(content, options, options.getCorrectIndex(), difficulty);
|
|
|
}
|
|
|
|
|
|
private String buildPrimaryContent(List<Integer> operands, List<String> operators) {
|
|
|
StringBuilder content = new StringBuilder();
|
|
|
|
|
|
// 随机决定是否加括号(只在有3个及以上操作数时)
|
|
|
boolean useParentheses = random.nextDouble() < 0.6 && operands.size() > 2;
|
|
|
|
|
|
if (useParentheses) {
|
|
|
// 随机决定括号包含的操作数数量(至少2个,最多 operands.size()-1 个)
|
|
|
int parenLength = random.nextInt(operands.size() - 1) + 2;
|
|
|
// 随机决定括号的起始位置
|
|
|
int start = random.nextInt(operands.size() - parenLength + 1);
|
|
|
int end = start + parenLength - 1;
|
|
|
|
|
|
for (int i = 0; i < operands.size(); i++) {
|
|
|
if (i == start) {
|
|
|
content.append("(");
|
|
|
}
|
|
|
content.append(operands.get(i));
|
|
|
if (i == end) {
|
|
|
content.append(")");
|
|
|
}
|
|
|
if (i < operators.size()) {
|
|
|
content.append(" ").append(operators.get(i)).append(" ");
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
for (int i = 0; i < operands.size(); i++) {
|
|
|
content.append(operands.get(i));
|
|
|
if (i < operators.size()) {
|
|
|
content.append(" ").append(operators.get(i)).append(" ");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
content.append(" = ?");
|
|
|
return content.toString();
|
|
|
}
|
|
|
|
|
|
private int calculateExpression(String expression) {
|
|
|
try {
|
|
|
// 移除 "= ?" 部分
|
|
|
String mathExpr = expression.replace(" = ?", "").trim();
|
|
|
return evaluate(mathExpr);
|
|
|
} catch (Exception e) {
|
|
|
return -1; // 计算失败
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private int evaluate(String expression) {
|
|
|
Stack<Integer> numbers = new Stack<>();
|
|
|
Stack<Character> ops = new Stack<>();
|
|
|
|
|
|
for (int i = 0; i < expression.length(); i++) {
|
|
|
char c = expression.charAt(i);
|
|
|
|
|
|
if (c == ' ') {
|
|
|
continue; // 跳过空格
|
|
|
}
|
|
|
|
|
|
if (Character.isDigit(c)) {
|
|
|
// 读取完整数字
|
|
|
int num = 0;
|
|
|
while (i < expression.length() && Character.isDigit(expression.charAt(i))) {
|
|
|
num = num * 10 + (expression.charAt(i) - '0');
|
|
|
i++;
|
|
|
}
|
|
|
i--;
|
|
|
numbers.push(num);
|
|
|
} else if (c == '(') {
|
|
|
ops.push(c);
|
|
|
} else if (c == ')') {
|
|
|
// 计算括号内的所有运算
|
|
|
while (ops.peek() != '(') {
|
|
|
int result = applyOp(ops.pop(), numbers.pop(), numbers.pop());
|
|
|
if (result < 0) return -1; // 检查中间结果是否负数
|
|
|
numbers.push(result);
|
|
|
}
|
|
|
ops.pop(); // 弹出 '('
|
|
|
} else if (isOperator(c)) {
|
|
|
// 处理运算符优先级:如果栈顶运算符优先级更高或相等,先计算栈顶的
|
|
|
while (!ops.isEmpty() && hasPrecedence(ops.peek(), c)) {
|
|
|
int result = applyOp(ops.pop(), numbers.pop(), numbers.pop());
|
|
|
if (result < 0) return -1; // 检查中间结果是否负数
|
|
|
numbers.push(result);
|
|
|
}
|
|
|
ops.push(c);
|
|
|
}
|
|
|
}
|
|
|
// 计算剩余表达式
|
|
|
while (!ops.isEmpty()) {
|
|
|
int result = applyOp(ops.pop(), numbers.pop(), numbers.pop());
|
|
|
if (result < 0) return -1; // 检查中间结果是否负数
|
|
|
numbers.push(result);
|
|
|
}
|
|
|
return numbers.pop();
|
|
|
}
|
|
|
|
|
|
private boolean hasPrecedence(char op1, char op2) {
|
|
|
if (op1 == '(' || op1 == ')') {
|
|
|
return false;
|
|
|
}
|
|
|
// op1是栈顶运算符,op2是当前运算符
|
|
|
// 如果栈顶运算符优先级高于或等于当前运算符,返回true(先计算栈顶的)
|
|
|
if ((op1 == '×' || op1 == '÷') && (op2 == '+' || op2 == '-')) {
|
|
|
return true;
|
|
|
}
|
|
|
// 同级运算符从左到右计算
|
|
|
if ((op1 == '+' || op1 == '-') && (op2 == '+' || op2 == '-')) {
|
|
|
return true;
|
|
|
}
|
|
|
if ((op1 == '×' || op1 == '÷') && (op2 == '×' || op2 == '÷')) {
|
|
|
return true;
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
private boolean isOperator(char c) {
|
|
|
return c == '+' || c == '-' || c == '×' || c == '÷';
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private int applyOp(char op, int b, int a) {
|
|
|
switch (op) {
|
|
|
case '+':
|
|
|
return a + b;
|
|
|
case '-':
|
|
|
int result = a - b;
|
|
|
return result >= 0 ? result : -1; // 确保非负
|
|
|
case '×':
|
|
|
return a * b;
|
|
|
case '÷':
|
|
|
if (b == 0 || a % b != 0) {
|
|
|
return -1; // 除数为0或不整除
|
|
|
}
|
|
|
return a / b;
|
|
|
default:
|
|
|
return -1;
|
|
|
}
|
|
|
}
|
|
|
} |