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.
Partner_Project/src/model/PrimaryMaker.java

217 lines
6.7 KiB

package model;
import java.util.Stack;
/**
* 小学题目生成器,确保题目结果非负
*/
public class PrimaryMaker extends Student {
private static final char[] OPERATORS = {'+', '-', '*', '/'};
private static final int MIN_OPERANDS = 2;
private static final int MAX_OPERANDS = 5;
private static final int MAX_OPERAND_VALUE = 100;
private int operandCount;
private int[] operands;
private int parenStart;
private int parenEnd;
public PrimaryMaker(String name, String password, String path) {
super(name, password, path);
}
@Override
protected String makeOneQuestion() {
String question;
while (true) {
question = generateSingleQuestion();
if (isQuestionValid(question)) {
break;
}
}
return question;
}
private String generateSingleQuestion() {
generateRandomParameters();
StringBuilder question = new StringBuilder();
for (int j = 0; j < operandCount; j++) {
appendOperandWithParentheses(question, j);
if (j < operandCount - 1) {
appendOperator(question, j);
}
}
return question.toString();
}
private void generateRandomParameters() {
operandCount = random.nextInt(MAX_OPERANDS - MIN_OPERANDS + 1) + MIN_OPERANDS;
operands = new int[operandCount];
for (int j = 0; j < operandCount; j++) {
operands[j] = random.nextInt(MAX_OPERAND_VALUE) + 1;
}
int[] parenPos = bracketMaker(operandCount);
parenStart = parenPos[0];
parenEnd = parenPos[1];
}
private void appendOperandWithParentheses(StringBuilder question, int index) {
if (index == parenStart) {
question.append("( ");
}
question.append(operands[index]);
if (index == parenEnd) {
question.append(" )");
}
}
private void appendOperator(StringBuilder question, int currentIndex) {
char op = OPERATORS[random.nextInt(OPERATORS.length)];
if (op == '-') {
ensureSubtractionIsValid(currentIndex);
}
if (op == '/') {
ensureDivisionIsValid(currentIndex);
}
question.append(" ").append(op).append(" ");
}
private void ensureSubtractionIsValid(int currentIndex) {
if (operands[currentIndex] < operands[currentIndex + 1]) {
int temp = operands[currentIndex];
operands[currentIndex] = operands[currentIndex + 1];
operands[currentIndex + 1] = temp;
}
}
private void ensureDivisionIsValid(int currentIndex) {
if (operands[currentIndex + 1] == 0) {
operands[currentIndex + 1] = 1;
}
}
private boolean isQuestionValid(String expression) {
try {
evaluateExpression(expression, true);
return true;
} catch (ArithmeticException e) {
return false;
}
}
private double evaluateExpression(String expression, boolean checkNegative)
throws ArithmeticException {
Stack<Double> 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)) {
i = parseAndPushNumber(expression, i, numbers);
} else if (c == '(') {
ops.push(c);
} else if (c == ')') {
processClosingParenthesis(numbers, ops, checkNegative);
} else if (isOperator(c)) {
processOperator(c, numbers, ops, checkNegative);
}
}
processRemainingOperators(numbers, ops, checkNegative);
double finalResult = numbers.pop();
validateResult(finalResult, checkNegative);
return finalResult;
}
private int parseAndPushNumber(String expression, int startIndex,
Stack<Double> numbers) {
StringBuilder sb = new StringBuilder();
int i = startIndex;
while (i < expression.length()
&& (Character.isDigit(expression.charAt(i)) || expression.charAt(i) == '.')) {
sb.append(expression.charAt(i++));
}
numbers.push(Double.parseDouble(sb.toString()));
return i - 1;
}
private void processClosingParenthesis(Stack<Double> numbers, Stack<Character> ops,
boolean checkNegative) {
while (ops.peek() != '(') {
double result = applyOp(ops.pop(), numbers.pop(), numbers.pop());
validateResult(result, checkNegative);
numbers.push(result);
}
ops.pop();
}
private void processOperator(char currentOp, Stack<Double> numbers,
Stack<Character> ops, boolean checkNegative) {
while (!ops.isEmpty() && hasPrecedence(currentOp, ops.peek())) {
double result = applyOp(ops.pop(), numbers.pop(), numbers.pop());
validateResult(result, checkNegative);
numbers.push(result);
}
ops.push(currentOp);
}
private void processRemainingOperators(Stack<Double> numbers, Stack<Character> ops,
boolean checkNegative) {
while (!ops.isEmpty()) {
double result = applyOp(ops.pop(), numbers.pop(), numbers.pop());
validateResult(result, checkNegative);
numbers.push(result);
}
}
private void validateResult(double result, boolean checkNegative) {
if (checkNegative && result < 0) {
throw new ArithmeticException("Negative result");
}
}
private boolean isOperator(char c) {
return c == '+' || c == '-' || c == '*' || c == '/';
}
private boolean hasPrecedence(char op1, char op2) {
if (op2 == '(' || op2 == ')') {
return false;
}
return (op1 != '*' && op1 != '/') || (op2 != '+' && op2 != '-');
}
private double applyOp(char op, double b, double a) {
switch (op) {
case '+':
return a + b;
case '-':
if (a < b) {
throw new ArithmeticException("Negative result in subtraction");
}
return a - b;
case '*':
return a * b;
case '/':
if (b == 0) {
throw new ArithmeticException("Cannot divide by zero");
}
return a / b;
default:
return 0;
}
}
}