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.
TestSystem/src/main/java/com/mathapp/problemGenerators/AbstractProblemGenerator.java

113 lines
4.0 KiB

package com.mathapp.problemGenerators;
import com.mathapp.models.Equation;
import com.mathapp.models.Operator;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* 抽象题目生成器(模板方法模式)。
* 提供了生成题目的通用流程和创建基础四则运算的核心方法。
*/
public abstract class AbstractProblemGenerator implements IProblemGenerator {
protected final Random random = new Random();
protected static final int MIN_OPERAND_VALUE = 1;
protected static final int MAX_OPERAND_VALUE = 100;
protected static final int MIN_OPERANDS_COUNT = 2;
protected static final int MAX_OPERANDS_COUNT = 5;
private static final Operator[] AVAILABLE_OPERATORS = {
Operator.ADD, Operator.SUBTRACT, Operator.MULTIPLY, Operator.DIVIDE
};
/**
* 此方法定义了生成指定数量不重复题目的标准流程。
*/
@Override
public final List<Equation> generate(int count, Set<String> existingProblems) {
List<Equation> newProblems = new ArrayList<>(count);
int attempts = 0;
final int maxAttempts = count * 10; // 防止无限循环
while (newProblems.size() < count && attempts < maxAttempts) {
Equation newProblem = createProblem();
if (!existingProblems.contains(newProblem.toNormalizedString())) {
newProblems.add(newProblem);
existingProblems.add(newProblem.toNormalizedString());
}
attempts++;
}
return newProblems;
}
/**
* (模板方法) 子类必须实现此方法以提供具体的题目生成逻辑。
*/
protected abstract Equation createProblem();
/**
* (核心辅助方法) 创建一个基础的四则运算题目。
*/
protected Equation createBaseArithmeticProblem(boolean nonNegativeOnly) {
int operandCount = getRandomNumber(MIN_OPERANDS_COUNT, MAX_OPERANDS_COUNT);
List<String> operands = new ArrayList<>();
List<Operator> operators = new ArrayList<>();
operands.add(String.valueOf(getRandomNumber(MIN_OPERAND_VALUE, MAX_OPERAND_VALUE)));
for (int i = 0; i < operandCount - 1; i++) {
Operator operator = AVAILABLE_OPERATORS[random.nextInt(AVAILABLE_OPERATORS.length)];
addOperandAndOperator(operands, operators, operator, nonNegativeOnly);
}
addParentheses(operands, operators);
return new Equation(operands, operators);
}
protected int getRandomNumber(int min, int max) {
return random.nextInt(max - min + 1) + min;
}
private void addOperandAndOperator(List<String> operands, List<Operator> operators, Operator operator, boolean nonNegativeOnly) {
int nextOperand;
int lastOperand = Integer.parseInt(operands.get(operands.size() - 1).replaceAll("[()]", ""));
if (operator == Operator.SUBTRACT && nonNegativeOnly) {
nextOperand = getRandomNumber(MIN_OPERAND_VALUE, lastOperand);
} else if (operator == Operator.DIVIDE) {
List<Integer> divisors = findDivisors(lastOperand);
int divisor = divisors.get(random.nextInt(divisors.size()));
int quotient = lastOperand / divisor;
operands.set(operands.size() - 1, String.valueOf(divisor * quotient));
nextOperand = divisor;
} else {
nextOperand = getRandomNumber(MIN_OPERAND_VALUE, MAX_OPERAND_VALUE);
}
operands.add(String.valueOf(nextOperand));
operators.add(operator);
}
private void addParentheses(List<String> operands, List<Operator> operators) {
if (operators.size() > 1 && random.nextBoolean()) {
int pos = random.nextInt(operators.size());
Operator op = operators.get(pos);
if (op == Operator.ADD || op == Operator.SUBTRACT) {
operands.set(pos, "(" + operands.get(pos));
operands.set(pos + 1, operands.get(pos + 1) + ")");
}
}
}
private List<Integer> findDivisors(int number) {
if (number == 0) return Collections.singletonList(1);
int absNumber = Math.abs(number);
return IntStream.rangeClosed(1, absNumber)
.filter(i -> absNumber % i == 0)
.boxed()
.collect(Collectors.toList());
}
}