|
|
|
@ -1,8 +1,9 @@
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Random;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 负责生成并添加括号
|
|
|
|
|
* 括号管理器
|
|
|
|
|
*/
|
|
|
|
|
public class BracketManager {
|
|
|
|
|
private static final Random RANDOM = new Random();
|
|
|
|
@ -13,40 +14,114 @@ public class BracketManager {
|
|
|
|
|
* 4 -> 90% 1对 10%2对
|
|
|
|
|
* 5 -> 50% 1对 50%2对
|
|
|
|
|
*/
|
|
|
|
|
public int decideParenthesesPairs(int numCount) {
|
|
|
|
|
private int decideParenthesesPairs(int numCount) {
|
|
|
|
|
return switch (numCount) {
|
|
|
|
|
case 3 -> RANDOM.nextDouble() < 0.8 ? 1 : 0;
|
|
|
|
|
case 4 -> RANDOM.nextDouble() < 0.9 ? 1 : 2;
|
|
|
|
|
case 3 -> RANDOM.nextDouble() < 0.6 ? 1 : 0;
|
|
|
|
|
case 4 -> RANDOM.nextDouble() < 0.8 ? 1 : 2;
|
|
|
|
|
case 5 -> RANDOM.nextDouble() < 0.5 ? 1 : 2;
|
|
|
|
|
default -> 0;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 添加括号
|
|
|
|
|
public String addParentheses(String expression, int pairs, List<Integer> numPositions) {
|
|
|
|
|
String[] tokens = expression.split(" ");
|
|
|
|
|
public String addNestedParentheses(String expression) {
|
|
|
|
|
List<Integer> numPositions = getNumPositions(expression);
|
|
|
|
|
int pairs = decideParenthesesPairs(numPositions.size());
|
|
|
|
|
|
|
|
|
|
String result;
|
|
|
|
|
if (pairs == 1) {
|
|
|
|
|
result = addParentheses(expression, 1, numPositions);
|
|
|
|
|
} else if (pairs == 2) {
|
|
|
|
|
String first = addParentheses(expression, 1, numPositions);
|
|
|
|
|
String second = addParallelOutside(first);
|
|
|
|
|
if (!second.equals(first)) {
|
|
|
|
|
result = second;
|
|
|
|
|
} else {
|
|
|
|
|
result = addNestedInside(first);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
result = expression;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = fixLeftParenthesisSpacing(result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 并列括号
|
|
|
|
|
private String addParallelOutside(String expression) {
|
|
|
|
|
String result = expression;
|
|
|
|
|
List<Integer> numPositions = getNumPositions(expression);
|
|
|
|
|
List<int[]> innerRanges = getInnerBracketRanges(expression);
|
|
|
|
|
|
|
|
|
|
// 找到未被第一层括号覆盖的段落
|
|
|
|
|
List<int[]> outerRanges = new ArrayList<>();
|
|
|
|
|
int lastEnd = -1;
|
|
|
|
|
for (int[] range : innerRanges) {
|
|
|
|
|
if (range[0] - 1 > lastEnd + 1) {
|
|
|
|
|
outerRanges.add(new int[]{lastEnd + 1, range[0] - 1});
|
|
|
|
|
}
|
|
|
|
|
lastEnd = range[1];
|
|
|
|
|
}
|
|
|
|
|
if (lastEnd + 1 < numPositions.size()) {
|
|
|
|
|
outerRanges.add(new int[]{lastEnd + 1, numPositions.size() - 1});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean added = false;
|
|
|
|
|
for (int[] range : outerRanges) {
|
|
|
|
|
String outerExpr = extractTokens(result, range[0], range[1]);
|
|
|
|
|
List<Integer> outerNumPos = getNumPositions(outerExpr);
|
|
|
|
|
int outerPairs = decideParenthesesPairs(outerNumPos.size());
|
|
|
|
|
if (outerPairs > 0) {
|
|
|
|
|
String newOuterExpr = addParentheses(outerExpr, outerPairs, outerNumPos);
|
|
|
|
|
result = replaceTokens(result, range[0], range[1], newOuterExpr);
|
|
|
|
|
added = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return added ? result : expression;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 嵌套括号
|
|
|
|
|
private String addNestedInside(String expression) {
|
|
|
|
|
String result = expression;
|
|
|
|
|
List<int[]> innerRanges = getInnerBracketRanges(expression);
|
|
|
|
|
for (int[] range : innerRanges) {
|
|
|
|
|
String innerExpr = extractTokens(result, range[0], range[1]);
|
|
|
|
|
List<Integer> innerNumPos = getNumPositions(innerExpr);
|
|
|
|
|
int innerPairs = decideParenthesesPairs(innerNumPos.size());
|
|
|
|
|
String newInnerExpr = addParentheses(innerExpr, innerPairs, innerNumPos);
|
|
|
|
|
result = replaceTokens(result, range[0], range[1], newInnerExpr);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 添加括号核心逻辑
|
|
|
|
|
private String addParentheses(String expression, int pairs, List<Integer> numPositions) {
|
|
|
|
|
String[] tokens = expression.split(" "); // 按空格拆分
|
|
|
|
|
int numCount = numPositions.size();
|
|
|
|
|
boolean[] markLeft = new boolean[numCount];
|
|
|
|
|
boolean[] markRight = new boolean[numCount];
|
|
|
|
|
boolean[] markLeft = new boolean[numCount]; // 左括号标记
|
|
|
|
|
boolean[] markRight = new boolean[numCount]; // 右括号标记
|
|
|
|
|
|
|
|
|
|
generateBracketPositions(numCount, numPositions, pairs, tokens, markLeft, markRight);
|
|
|
|
|
String result = insertBrackets(tokens, markLeft, markRight);
|
|
|
|
|
return validateParentheses(result) ? result : expression;
|
|
|
|
|
generateBracketPositions(numCount, numPositions, pairs, tokens, markLeft, markRight); // 生成括号位置
|
|
|
|
|
String result = insertBrackets(tokens, markLeft, markRight); // 插入括号
|
|
|
|
|
return validateParentheses(result) ? result : expression; // 验证括号匹配,非法返回原表达式
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//计算生成括号的位置
|
|
|
|
|
// 生成括号位置
|
|
|
|
|
private void generateBracketPositions(int numCount, List<Integer> numPositions, int pairs, String[] tokens,
|
|
|
|
|
boolean[] markLeft, boolean[] markRight) {
|
|
|
|
|
int attempts = 0;
|
|
|
|
|
for (int p = 0; p < pairs && attempts < 50; p++, attempts++) {
|
|
|
|
|
boolean placed = false;
|
|
|
|
|
for (int tries = 0; tries < 50 && !placed; tries++) {
|
|
|
|
|
int start = RANDOM.nextInt(numCount - 1);
|
|
|
|
|
int end = start + 1 + RANDOM.nextInt(numCount - start - 1);
|
|
|
|
|
if (end >= numCount) end = numCount - 1;
|
|
|
|
|
int start = RANDOM.nextInt(numCount - 1); // 随机起始数字索引
|
|
|
|
|
int end = start + 1 + RANDOM.nextInt(numCount - start - 1); // 随机结束数字索引
|
|
|
|
|
if (end >= numCount) {
|
|
|
|
|
end = numCount - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 20%概率尝试嵌套在已有括号内部
|
|
|
|
|
if (RANDOM.nextDouble() < 0.2) {
|
|
|
|
|
// 40%概率尝试嵌套在已有括号内部
|
|
|
|
|
if (RANDOM.nextDouble() < 0.4) {
|
|
|
|
|
for (int i = 0; i < numCount; i++) {
|
|
|
|
|
if (markLeft[i]) {
|
|
|
|
|
start = i;
|
|
|
|
@ -56,6 +131,7 @@ public class BracketManager {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查是否合法
|
|
|
|
|
if (canPlaceBracket(start, end, markLeft, markRight, numPositions, tokens)) {
|
|
|
|
|
markLeft[start] = true;
|
|
|
|
|
markRight[end] = true;
|
|
|
|
@ -65,63 +141,96 @@ public class BracketManager {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//检测添加括号的合法性
|
|
|
|
|
// 检查括号是否可以放置
|
|
|
|
|
private boolean canPlaceBracket(int start, int end, boolean[] markLeft, boolean[] markRight,
|
|
|
|
|
List<Integer> numPositions, String[] tokens) {
|
|
|
|
|
if (isOuterMostBracket(start, end, markLeft.length)) return false;
|
|
|
|
|
if (isInvalidOverlap(start, end, markLeft, markRight)) return false;
|
|
|
|
|
if (isOuterMostBracket(start, end, markLeft.length)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (isInvalidOverlap(start, end, markLeft, markRight)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return isValidBracketRange(tokens, numPositions, start, end);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//避免最外层括号
|
|
|
|
|
// 避免最外层括号
|
|
|
|
|
private boolean isOuterMostBracket(int start, int end, int numCount) {
|
|
|
|
|
return start == 0 && end == numCount - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//允许嵌套但禁止交叉
|
|
|
|
|
// 避免交叉括号,允许嵌套
|
|
|
|
|
private boolean isInvalidOverlap(int start, int end, boolean[] markLeft, boolean[] markRight) {
|
|
|
|
|
for (int i = start; i <= end; i++) {
|
|
|
|
|
// start/end可重合,内部禁止已有左右括号
|
|
|
|
|
if ((i != start && markLeft[i]) || (i != end && markRight[i])) return true;
|
|
|
|
|
if ((i != start && markLeft[i]) || (i != end && markRight[i])) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//检测括号是否有意义(优先级)
|
|
|
|
|
// 检查括号区间内部最小优先级是否小于相邻外部运算符优先级
|
|
|
|
|
private boolean isValidBracketRange(String[] tokens, List<Integer> numPositions, int start, int end) {
|
|
|
|
|
int opStart = numPositions.get(start) + 1;
|
|
|
|
|
int opEnd = numPositions.get(end) - 1;
|
|
|
|
|
String firstOp = null;
|
|
|
|
|
boolean hasHigherOuterOp = false;
|
|
|
|
|
|
|
|
|
|
for (int i = opStart; i <= opEnd; i += 2) {
|
|
|
|
|
if (!isNumber(tokens[i])) {
|
|
|
|
|
if (firstOp == null) firstOp = tokens[i];
|
|
|
|
|
else if (isDifferentPriority(firstOp, tokens[i])) {
|
|
|
|
|
hasHigherOuterOp = true;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// 找到括号内部最低优先级运算符
|
|
|
|
|
int minPriInside = Integer.MAX_VALUE;
|
|
|
|
|
for (int i = opStart; i <= opEnd; i++) {
|
|
|
|
|
if (i < 0 || i >= tokens.length) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
String t = tokens[i];
|
|
|
|
|
if (t.equals("(") || t.equals(")")) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!isNumber(t)) {
|
|
|
|
|
int p = getPrecedence(t);
|
|
|
|
|
if (p > 0) minPriInside = Math.min(minPriInside, p);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (minPriInside == Integer.MAX_VALUE) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int leftPri = getAdjacentOpPrecedence(tokens, numPositions.get(start) - 1);
|
|
|
|
|
int rightPri = getAdjacentOpPrecedence(tokens, numPositions.get(end) + 1);
|
|
|
|
|
|
|
|
|
|
if (leftPri == -1 && rightPri == -1) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return hasHigherOuterOp || firstOp == null;
|
|
|
|
|
return (leftPri != -1 && minPriInside < leftPri) || (rightPri != -1 && minPriInside < rightPri);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//比较运算符优先级
|
|
|
|
|
private boolean isDifferentPriority(String op1, String op2) {
|
|
|
|
|
int p1 = (op1.equals("+") || op1.equals("-")) ? 1 : 2;
|
|
|
|
|
int p2 = (op2.equals("+") || op2.equals("-")) ? 1 : 2;
|
|
|
|
|
return p1 != p2;
|
|
|
|
|
// 获取相邻运算符优先级
|
|
|
|
|
private int getAdjacentOpPrecedence(String[] tokens, int idx) {
|
|
|
|
|
if (idx < 0 || idx >= tokens.length) return -1;
|
|
|
|
|
String t = tokens[idx];
|
|
|
|
|
if (t.equals("(") || t.equals(")") || isNumber(t)) return -1;
|
|
|
|
|
return getPrecedence(t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//插入操作
|
|
|
|
|
// 运算符优先级
|
|
|
|
|
private int getPrecedence(String op) {
|
|
|
|
|
return switch (op) {
|
|
|
|
|
case "+", "-" -> 1;
|
|
|
|
|
case "*", "/" -> 2;
|
|
|
|
|
default -> 0;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 插入括号
|
|
|
|
|
private String insertBrackets(String[] tokens, boolean[] markLeft, boolean[] markRight) {
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
int numIndex = 0;
|
|
|
|
|
for (String token : tokens) {
|
|
|
|
|
if (isNumber(token)) {
|
|
|
|
|
if (markLeft[numIndex]) sb.append("( ");
|
|
|
|
|
if (markLeft[numIndex]) {
|
|
|
|
|
sb.append("(").append(" ");
|
|
|
|
|
}
|
|
|
|
|
sb.append(token);
|
|
|
|
|
if (markRight[numIndex]) sb.append(" )");
|
|
|
|
|
if (markRight[numIndex]) {
|
|
|
|
|
sb.append(" ").append(")");
|
|
|
|
|
}
|
|
|
|
|
numIndex++;
|
|
|
|
|
} else {
|
|
|
|
|
sb.append(" ").append(token).append(" ");
|
|
|
|
@ -130,19 +239,109 @@ public class BracketManager {
|
|
|
|
|
return sb.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//检查括号是否缺失
|
|
|
|
|
// 验证括号匹配
|
|
|
|
|
private boolean validateParentheses(String expr) {
|
|
|
|
|
int balance = 0;
|
|
|
|
|
for (char c : expr.toCharArray()) {
|
|
|
|
|
if (c == '(') balance++;
|
|
|
|
|
if (c == ')') balance--;
|
|
|
|
|
if (balance < 0) return false;
|
|
|
|
|
if (c == '(') {
|
|
|
|
|
balance++;
|
|
|
|
|
}
|
|
|
|
|
if (c == ')') {
|
|
|
|
|
balance--;
|
|
|
|
|
}
|
|
|
|
|
if (balance < 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return balance == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 判断是否为数字
|
|
|
|
|
private boolean isNumber(String token) {
|
|
|
|
|
try { Integer.parseInt(token); return true; }
|
|
|
|
|
catch (NumberFormatException e) { return false; }
|
|
|
|
|
try {
|
|
|
|
|
Integer.parseInt(token);
|
|
|
|
|
return true;
|
|
|
|
|
} catch (NumberFormatException e) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取表达式中数字位置
|
|
|
|
|
private List<Integer> getNumPositions(String expr) {
|
|
|
|
|
String[] tokens = expr.split(" ");
|
|
|
|
|
List<Integer> positions = new ArrayList<>();
|
|
|
|
|
for (int i = 0; i < tokens.length; i++) {
|
|
|
|
|
if (isNumber(tokens[i])) {
|
|
|
|
|
positions.add(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return positions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取第一层括号内部 token 范围
|
|
|
|
|
private List<int[]> getInnerBracketRanges(String expr) {
|
|
|
|
|
List<int[]> ranges = new ArrayList<>();
|
|
|
|
|
String[] tokens = expr.split(" ");
|
|
|
|
|
int balance = 0, start = -1;
|
|
|
|
|
for (int i = 0; i < tokens.length; i++) {
|
|
|
|
|
if (tokens[i].equals("(")) {
|
|
|
|
|
if (balance == 0) {
|
|
|
|
|
start = i;
|
|
|
|
|
}
|
|
|
|
|
balance++;
|
|
|
|
|
} else if (tokens[i].equals(")")) {
|
|
|
|
|
balance--;
|
|
|
|
|
if (balance == 0 && start != -1) {
|
|
|
|
|
ranges.add(new int[]{start + 1, i - 1});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ranges;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 提取指定范围 token
|
|
|
|
|
private String extractTokens(String expr, int start, int end) {
|
|
|
|
|
String[] tokens = expr.split(" ");
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
for (int i = start; i <= end; i++) {
|
|
|
|
|
if (i > start) sb.append(" ");
|
|
|
|
|
sb.append(tokens[i]);
|
|
|
|
|
}
|
|
|
|
|
return sb.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 替换指定范围 token
|
|
|
|
|
private String replaceTokens(String expr, int start, int end, String replacement) {
|
|
|
|
|
String[] tokens = expr.split(" ");
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
for (int i = 0; i < tokens.length; i++) {
|
|
|
|
|
if (i == start) {
|
|
|
|
|
sb.append(replacement);
|
|
|
|
|
i = end;
|
|
|
|
|
} else {
|
|
|
|
|
if (!sb.isEmpty()) {
|
|
|
|
|
sb.append(" ");
|
|
|
|
|
}
|
|
|
|
|
sb.append(tokens[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return sb.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//修正左括号后" "缺失
|
|
|
|
|
private String fixLeftParenthesisSpacing(String expr) {
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
char[] chars = expr.toCharArray();
|
|
|
|
|
for (int i = 0; i < chars.length; i++) {
|
|
|
|
|
char c = chars[i];
|
|
|
|
|
sb.append(c);
|
|
|
|
|
if (c == '(') {
|
|
|
|
|
if (i + 1 < chars.length && chars[i + 1] != ' ') {
|
|
|
|
|
sb.append(' ');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return sb.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|