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.
relentless/ElementaryQuestionStrategy....

152 lines
5.6 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.

import java.util.Random;
public class ElementaryQuestionStrategy implements QuestionStrategy {
private final Random random = new Random();
private final String[] operators = {"+", "-", "*", "/"};
@Override
public String generateQuestion() {
int operandsCount = random.nextInt(4) + 2; // 操作数 2~5
StringBuilder sb = new StringBuilder();
// 生成运算符序列
String[] chosenOps = generateOperators(operandsCount);
// 随机决定括号
int[] parentheses = decideParentheses(operandsCount, chosenOps);
// 拼接表达式
buildExpression(sb, operandsCount, chosenOps, parentheses);
return sb.toString();
}
/**
* 生成运算符序列
* 根据操作数数量,随机生成对应数量的运算符数组
*
* @param operandsCount 操作数数量
* @return 随机生成的运算符数组,长度为操作数数量-1
*/
private String[] generateOperators(int operandsCount) {
String[] chosenOps = new String[operandsCount - 1];
for (int i = 0; i < operandsCount - 1; i++) {
chosenOps[i] = operators[random.nextInt(operators.length)];
}
return chosenOps;
}
/**
* 决定是否需要括号,并返回括号的起始和结束位置
* 对于3个及以上操作数随机决定是否添加括号并检查括号是否有效
*
* @param operandsCount 操作数数量
* @param chosenOps 运算符数组
* @return 包含开括号和闭括号位置的数组,格式为[openParenIndex, closeParenIndex]-1表示不添加括号
*/
private int[] decideParentheses(int operandsCount, String[] chosenOps) {
int openParenIndex = -1;
int closeParenIndex = -1;
if (operandsCount > 2 && random.nextBoolean()) {
openParenIndex = random.nextInt(operandsCount - 1);
closeParenIndex = random.nextInt(operandsCount - openParenIndex - 1) + openParenIndex + 1;
// 如果括号包裹整个表达式,则不需要括号
if (openParenIndex == 0 && closeParenIndex == operandsCount - 1) {
openParenIndex = -1;
closeParenIndex = -1;
} else {
// 检查括号内的运算符优先级是否相同,如果相同则不需要括号
boolean samePrecedence = checkPrecedenceEquality(chosenOps, openParenIndex, closeParenIndex);
if (samePrecedence) {
openParenIndex = -1;
closeParenIndex = -1;
}
}
}
return new int[]{openParenIndex, closeParenIndex};
}
/**
* 判断括号内的运算符优先级是否相同
* 用于决定是否需要添加括号(如果优先级都相同,则括号是冗余的)
*
* @param chosenOps 运算符数组
* @param openParenIndex 开括号位置
* @param closeParenIndex 闭括号位置
* @return 如果括号内所有运算符优先级相同则返回true否则返回false
*/
private boolean checkPrecedenceEquality(String[] chosenOps, int openParenIndex, int closeParenIndex) {
int precedence = getPrecedence(chosenOps[openParenIndex]);
for (int i = openParenIndex; i < closeParenIndex; i++) {
if (getPrecedence(chosenOps[i]) != precedence) {
return false;
}
}
return true;
}
/**
* 构建数学表达式
* 根据操作数、运算符和括号位置,拼接完整的数学表达式
*
* @param sb StringBuilder对象用于构建表达式
* @param operandsCount 操作数数量
* @param chosenOps 运算符数组
* @param parentheses 括号位置数组,格式为[openParenIndex, closeParenIndex]
*/
private void buildExpression(StringBuilder sb, int operandsCount, String[] chosenOps, int[] parentheses) {
int prevNum = random.nextInt(100) + 1;
int openParenIndex = parentheses[0];
int closeParenIndex = parentheses[1];
// 处理第一个操作数可能的开括号
if (openParenIndex == 0) sb.append("(");
sb.append(prevNum);
// 处理最后一个操作数可能的闭括号
if (closeParenIndex == operandsCount - 1) sb.append(")");
// 构建剩余的操作数和运算符
for (int i = 1; i < operandsCount; i++) {
String op = chosenOps[i - 1];
sb.append(" ").append(op).append(" ");
// 根据运算符生成下一个操作数
int num = generateOperand(op, prevNum);
// 添加括号(如果需要)
if (i == openParenIndex) sb.append("(");
sb.append(num);
if (i == closeParenIndex) sb.append(")");
prevNum = num; // 更新上一个数字,供下一轮计算使用
}
}
/**
* 生成每个操作数
* 确保生成的操作数符合题目要求,特别是减法不会导致负数结果
*
* @param op 当前运算符
* @param prevNum 前一个操作数的值
* @return 生成的操作数
*/
private int generateOperand(String op, int prevNum) {
int num = random.nextInt(100) + 1; // 默认生成1-100之间的随机数
// 如果是减法运算符,确保不会出现负数结果
if (op.equals("-") && num > prevNum) {
num = random.nextInt(prevNum) + 1; // 保证 num <= prevNum避免负数结果
}
return num;
}
private int getPrecedence(String op) {
if (op.equals("+") || op.equals("-")) return 1;
if (op.equals("*") || op.equals("/")) return 2;
return 0;
}
}