You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
jiedui/src/service/PrimaryQuestionGenerator.java

238 lines
7.5 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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;
}
}
}