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.
113 lines
4.0 KiB
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());
|
|
}
|
|
} |