Merge pull request '3' (#3) from develop into main

main
hnu202326010305 6 days ago
commit b6de5ff1b8

@ -0,0 +1,148 @@
import java.util.List;
import java.util.Random;
/**
*
*/
public class BracketManager {
private static final Random RANDOM = new Random();
/**
*
* 3 -> 80% 1 20%0
* 4 -> 90% 1 10%2
* 5 -> 50% 1 50%2
*/
public int decideParenthesesPairs(int numCount) {
return switch (numCount) {
case 3 -> RANDOM.nextDouble() < 0.8 ? 1 : 0;
case 4 -> RANDOM.nextDouble() < 0.9 ? 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(" ");
int numCount = numPositions.size();
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;
}
//计算生成括号的位置
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;
// 20%概率尝试嵌套在已有括号内部
if (RANDOM.nextDouble() < 0.2) {
for (int i = 0; i < numCount; i++) {
if (markLeft[i]) {
start = i;
end = i + RANDOM.nextInt(numCount - i);
break;
}
}
}
if (canPlaceBracket(start, end, markLeft, markRight, numPositions, tokens)) {
markLeft[start] = true;
markRight[end] = true;
placed = true;
}
}
}
}
//检测添加括号的合法性
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;
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;
}
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;
}
}
}
return hasHigherOuterOp || firstOp == null;
}
//比较运算符优先级
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 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("( ");
sb.append(token);
if (markRight[numIndex]) sb.append(" )");
numIndex++;
} else {
sb.append(" ").append(token).append(" ");
}
}
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;
}
return balance == 0;
}
private boolean isNumber(String token) {
try { Integer.parseInt(token); return true; }
catch (NumberFormatException e) { return false; }
}
}

@ -1,7 +1,11 @@
import java.util.Scanner;
/**
*
*/
public class ExamSystem {
private final Scanner scanner = new Scanner(System.in);
public Users login() {
Users users = null;
while (users == null) {
@ -22,29 +26,44 @@ public class ExamSystem {
public void generateProblems(Users users) {
while (true) {
String input = scanner.nextLine().trim();
if (input.equals("-1")) {
break;
if (input.equals("-1")) break;
Users newUsers = handleSwitchType(input, users);
if (newUsers != null) {
users = newUsers;
continue;
}
ProblemsGenerator generator = getGenerator(users.getType());
handleGenerate(input, users, generator);
}
}
ProblemsGenerator generator = switch (users.getType()) {
private ProblemsGenerator getGenerator(String type) {
return switch (type) {
case "小学" -> new PrimaryProblem();
case "初中" -> new JuniorProblem();
case "高中" -> new SeniorProblem();
default -> null;
};
}
private Users handleSwitchType(String input, Users users) {
if (!input.startsWith("切换为")) return null;
if (input.startsWith("切换为")) {
String target = input.replace("切换为", "");
if (target.equals("小学") || target.equals("初中") || target.equals("高中")) {
users = new Users(users.getName(), users.getPassword(), target);
System.out.print("系统提示:准备生成" + users.getType()
Users newUsers = new Users(users.getName(), users.getPassword(), target);
System.out.print("系统提示:准备生成" + newUsers.getType()
+ "数学题目,请输入生成题目数量(输入-1将退出当前用户重新登录");
return newUsers;
} else {
System.out.println("请输入小学、初中和高中三个选项中的一个");
return null;
}
continue;
}
private void handleGenerate(String input, Users users, ProblemsGenerator generator) {
try {
int num = Integer.parseInt(input);
if (num >= 10 && num <= 30 && generator != null) {
@ -59,5 +78,5 @@ public class ExamSystem {
System.out.print("准备生成" + users.getType()
+ "数学题目,请输入生成题目数量(输入-1将退出当前用户重新登录");
}
}
}

@ -0,0 +1,51 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class ExpressionBuilder {
private static final Random RANDOM = new Random();
private static final String[] OPERATORS = {"+", "-", "*", "/"};
// 生成基础四则运算表达式
public String generateBasicExpression(int count) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; i++) {
sb.append(RANDOM.nextInt(100) + 1);
if (i < count - 1) {
sb.append(" ").append(OPERATORS[RANDOM.nextInt(OPERATORS.length)]).append(" ");
}
}
return sb.toString();
}
// 获取表达式中数字的位置
public List<Integer> getNumberPositions(String expression) {
String[] tokens = expression.split(" ");
List<Integer> positions = new ArrayList<>();
for (int i = 0; i < tokens.length; i++) {
if (isNumber(tokens[i])) positions.add(i);
}
return positions;
}
// 检查是否存在除零
public boolean hasDivideByZero(String expression) {
String[] tokens = expression.split(" ");
for (int i = 0; i < tokens.length - 1; i++) {
if (tokens[i].equals("/")) {
try {
if (Integer.parseInt(tokens[i + 1]) == 0) return true;
} catch (NumberFormatException e) {
//
}
}
}
return false;
}
//判断是否为数字位
private boolean isNumber(String token) {
try { Integer.parseInt(token); return true; }
catch (NumberFormatException e) { return false; }
}
}

@ -1,31 +1,96 @@
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
/**
*
*/
public class Generator {
//生成题目并保存到用户文件夹
public static void generate(Users users, int count, ProblemsGenerator problemsGenerator) {
Set<String> history = loadHistory(users);
StringBuilder builder = new StringBuilder();
for (int i = 1; i <= count; i++) {
builder.append(i).append(". ").append(problemsGenerator.generate()).append("\n\n");
String problem;
int tries = 0;
do {
problem = problemsGenerator.generate();
tries++;
if (tries >= 20) {
System.out.println("警告:尝试 20 次仍未生成新题目,可能题目库有限。");
break;
}
} while (history.contains(problem));
history.add(problem);
builder.append(i).append(". ").append(problem).append("\n\n");
}
save(users, builder.toString());
System.out.println("题目生成完成。");
}
/**
*
*/
private static Set<String> loadHistory(Users users) {
Set<String> history = new HashSet<>();
String safeName = users.getName().replaceAll("[^\\u4e00-\\u9fa5a-zA-Z0-9_]", "_");
String folder = "doc/" + safeName;
File dir = new File(folder);
if (!dir.exists()) return history;
File[] files = dir.listFiles((d, name) -> name.endsWith(".txt"));
if (files != null) {
for (File f : files) {
try (Scanner sc = new Scanner(f)) {
while (sc.hasNextLine()) {
String line = sc.nextLine().trim();
if (!line.isEmpty() && Character.isDigit(line.charAt(0))) {
int dot = line.indexOf('.');
if (dot != -1) line = line.substring(dot + 1).trim();
history.add(line);
}
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("读取历史题目失败:" + e.getMessage());
}
}
}
return history;
}
/**
*
*/
private static void save(Users users, String content) {
try {
String folder = "doc/" + users.getName();
java.io.File dir = new java.io.File(folder);
if (!dir.exists()) {
dir.mkdirs();
String safeName = users.getName().replaceAll("[^\\u4e00-\\u9fa5a-zA-Z0-9_]", "_");
String folder = "doc/" + safeName;
File dir = new File(folder);
if (!dir.exists() && !dir.mkdirs()) {
System.out.println("无法创建目录: " + folder);
return;
}
String filename = LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss")) + ".txt";
FileWriter writer = new FileWriter(folder + "/" + filename);
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss"))
+ "-" + (int) (Math.random() * 1000) + ".txt";
try (FileWriter writer = new FileWriter(folder + "/" + filename)) {
writer.write(content);
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("保存失败:" + e.getMessage());
}
}

@ -1,51 +1,36 @@
import java.util.List;
import java.util.Random;
/**
*
*/
public class JuniorProblem implements ProblemsGenerator {
private static final Random RANDOM = new Random();
private static final String[] OPERATORS = {"+", "-", "*", "/"};
private static final String[] POW_OPERATORS = {"√", "^2"};
private final BracketManager bracketManager = new BracketManager();
private final PowerManager powerManager = new PowerManager();
private final ExpressionBuilder expressionBuilder = new ExpressionBuilder();
@Override
public String generate() {
String primaryExpr = generatePrimaryExpression();
return addPowerOperators(primaryExpr);
}
private String generatePrimaryExpression() {
int count = RANDOM.nextInt(4) + 2;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; i++) {
sb.append(RANDOM.nextInt(100) + 1);
if (i < count - 1) {
sb.append(" ").append(OPERATORS[RANDOM.nextInt(OPERATORS.length)]).append(" ");
}
}
return sb.toString();
}
private String addPowerOperators(String expr) {
String[] parts = expr.split(" ");
boolean hasPower = false;
String expression;
boolean hasPower;
do {
int numCount = RANDOM.nextInt(5) + 1;
expression = expressionBuilder.generateBasicExpression(numCount);
List<Integer> numPositions = expressionBuilder.getNumberPositions(expression);
for (int i = 0; i < parts.length; i++) {
if (parts[i].matches("\\d+") && (!hasPower || RANDOM.nextBoolean())) {
String op = POW_OPERATORS[RANDOM.nextInt(POW_OPERATORS.length)];
parts[i] = op.equals("√") ? "√" + parts[i] : parts[i] + "^2";
hasPower = true;
if (RANDOM.nextDouble() < 0.8) {
int pairs = bracketManager.decideParenthesesPairs(numCount);
if (pairs > 0) {
expression = bracketManager.addParentheses(expression, pairs, numPositions);
}
}
if (!hasPower) {
for (int i = 0; i < parts.length; i++) {
if (parts[i].matches("\\d+")) {
String op = POW_OPERATORS[RANDOM.nextInt(POW_OPERATORS.length)];
parts[i] = op.equals("√") ? "√" + parts[i] : parts[i] + "^2";
break;
}
}
}
// 添加幂运算并检查添加是否成功
expression = powerManager.addPowerOperations(expression, numPositions);
hasPower = expression.contains("^2") || expression.contains("√");
} while (expressionBuilder.hasDivideByZero(expression) || !hasPower);
return String.join(" ", parts);
return expression;
}
}

@ -1,3 +1,6 @@
/**
*
*/
public class Main {
public static void main(String[] args) {
ExamSystem system = new ExamSystem();

@ -0,0 +1,95 @@
import java.util.List;
import java.util.Random;
/**
*
*/
public class PowerManager {
private static final Random RANDOM = new Random();
// 普通幂运算
public String addPowerOperations(String expression, List<Integer> numPositions) {
if (numPositions.isEmpty()) return expression;
String[] tokens = expression.split(" ");
int targetIndex = chooseTargetIndex(tokens, numPositions, null);
if (targetIndex != -1) {
tokens[targetIndex] = applyPower(tokens[targetIndex], tokens, targetIndex);
}
return String.join(" ", tokens);
}
// 避免加在 trig 已占用的位置,同时避免加在运算符右边
public String addPowerOperationsAvoid(String expression, List<Integer> numPositions, List<Integer> trigPositions) {
if (numPositions.isEmpty()) return expression;
String[] tokens = expression.split(" ");
int targetIndex = chooseTargetIndex(tokens, numPositions, trigPositions);
if (targetIndex != -1) {
tokens[targetIndex] = applyPower(tokens[targetIndex], tokens, targetIndex);
}
return String.join(" ", tokens);
}
// 选择合适的位置加幂运算
private int chooseTargetIndex(String[] tokens, List<Integer> numPositions, List<Integer> avoidPositions) {
for (int tries = 0; tries < 20; tries++) {
int idx = numPositions.get(RANDOM.nextInt(numPositions.size()));
if ((avoidPositions == null || !avoidPositions.contains(idx)) && canApplyPower(tokens, idx)) {
return idx;
}
}
return -1;
}
// 检查该位置是否可以加幂运算
private boolean canApplyPower(String[] tokens, int index) {
if (!isNumber(tokens[index])) return false;
if (tokens[index].contains("^") || tokens[index].contains("√")) return false; // 避免重复幂运算
// 不能加在运算符右边
if (index > 0) {
String prev = tokens[index - 1];
if (prev.equals("/") || prev.equals("*") || prev.equals("+") || prev.equals("-")) {
return false;
}
}
return true;
}
// 应用平方或开方(根号放左括号左侧,平方放右括号右侧)
private String applyPower(String token, String[] tokens, int index) {
boolean useSqrt = RANDOM.nextBoolean();
if (useSqrt) {
// 根号放在左括号左侧,如果前面是左括号,则放在括号左边
if (index > 0 && tokens[index - 1].equals(")")) {
return "√" + token; // 特殊情况,仍然放数字前
}
if (index > 0 && tokens[index - 1].equals("(")) {
return "√" + token;
}
return "√" + token;
} else {
// 平方放在右括号右侧,如果后面是右括号,则放在括号右边
if (index < tokens.length - 1 && tokens[index + 1].equals(")")) {
return token + "^2";
}
return token + "^2";
}
}
private boolean isNumber(String token) {
try {
Integer.parseInt(token);
return true;
} catch (NumberFormatException e) {
return false;
}
}
}

@ -1,44 +1,29 @@
import java.util.List;
import java.util.Random;
/**
*
*/
public class PrimaryProblem implements ProblemsGenerator {
private static final Random RANDOM = new Random();
private static final String[] OPERATORS = {"+", "-", "*", "/"};
private final BracketManager bracketManager = new BracketManager();
private final ExpressionBuilder expressionBuilder = new ExpressionBuilder();
@Override
public String generate() {
int count = RANDOM.nextInt(4) + 2;
StringBuilder sb = new StringBuilder();
String expression;
do {
int numCount = 2 + RANDOM.nextInt(4);
expression = expressionBuilder.generateBasicExpression(numCount);
boolean addParentheses = count >= 3 && RANDOM.nextDouble() < 0.5;
if (addParentheses) {
int parenCount = RANDOM.nextInt(count - 2) + 2;
int startPos = RANDOM.nextInt(count - parenCount + 1);
for (int i = 0; i < count; i++) {
if (i == startPos) {
sb.append("( ");
List<Integer> numPositions = expressionBuilder.getNumberPositions(expression);
int pairs = bracketManager.decideParenthesesPairs(numCount);
if (pairs > 0) {
expression = bracketManager.addParentheses(expression, pairs, numPositions);
}
int num = RANDOM.nextInt(100) + 1;
sb.append(num);
if (i == startPos + parenCount - 1) {
sb.append(" )");
}
} while (expressionBuilder.hasDivideByZero(expression));
if (i < count - 1) {
sb.append(" ").append(OPERATORS[RANDOM.nextInt(OPERATORS.length)]).append(" ");
}
}
} else {
for (int i = 0; i < count; i++) {
int num = RANDOM.nextInt(100) + 1;
sb.append(num);
if (i < count - 1) {
sb.append(" ").append(OPERATORS[RANDOM.nextInt(OPERATORS.length)]).append(" ");
}
}
}
return sb.toString();
return expression;
}
}

@ -1,107 +1,43 @@
import java.util.List;
import java.util.Random;
/**
*
*/
public class SeniorProblem implements ProblemsGenerator {
private static final Random RANDOM = new Random();
private static final String[] OPERATORS = {"+", "-", "*", "/"};
private static final String[] POW_OPERATORS = {"√", "^2"};
private static final String[] TRIG_OPERATORS = {"sin", "cos", "tan"};
private final BracketManager bracketManager = new BracketManager();
private final PowerManager powerManager = new PowerManager();
private final TrigManager trigManager = new TrigManager();
private final ExpressionBuilder expressionBuilder = new ExpressionBuilder();
@Override
public String generate() {
String primaryExpression = generatePrimaryExpression();
String expression;
boolean hasTrig;
do {
int numCount = RANDOM.nextInt(5) + 1;
expression = expressionBuilder.generateBasicExpression(numCount);
List<Integer> numPositions = expressionBuilder.getNumberPositions(expression);
return addHighSchoolOperators(primaryExpression);
if (RANDOM.nextDouble() < 0.8) {
int pairs = bracketManager.decideParenthesesPairs(numCount);
if (pairs > 0) {
expression = bracketManager.addParentheses(expression, pairs, numPositions);
}
private String generatePrimaryExpression() {
int count = RANDOM.nextInt(4) + 2;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; i++) {
int num = RANDOM.nextInt(100) + 1;
sb.append(num);
if (i < count - 1) {
sb.append(" ").append(OPERATORS[RANDOM.nextInt(OPERATORS.length)]).append(" ");
}
}
return sb.toString();
}
private String addHighSchoolOperators(String primaryExpression) {
String[] parts = primaryExpression.split(" ");
StringBuilder result = new StringBuilder();
boolean hasTrigonometry = false;
boolean hasAlgebra = false;
// 添加三角函数并检查是否存在
expression = trigManager.addTrigOperations(expression, numPositions);
List<Integer> trigPositions = expressionBuilder.getNumberPositions(expression);
hasTrig = expression.contains("sin") || expression.contains("cos") || expression.contains("tan");
for (int i = 0; i < parts.length; i++) {
String part = parts[i];
if (isNumber(part)) {
if (!hasTrigonometry || RANDOM.nextDouble() < 0.6) {
part = applyTrigOperator(part);
hasTrigonometry = true;
}
else if (RANDOM.nextDouble() < 0.3) {
part = applyAlgebraOperator(part);
hasAlgebra = true;
}
}
result.append(part);
if (i < parts.length - 1) {
result.append(" ");
}
}
if (!hasTrigonometry) {
return addTrigonometryForced(result.toString());
}
return result.toString();
}
private boolean isNumber(String str) {
try {
Integer.parseInt(str);
return true;
} catch (NumberFormatException e) {
return false;
}
}
private String applyTrigOperator(String number) {
String operator = TRIG_OPERATORS[RANDOM.nextInt(TRIG_OPERATORS.length)];
int num = Integer.parseInt(number);
int angle = generateValidAngle(operator);
return operator + angle + "°";
}
private String applyAlgebraOperator(String number) {
String operator = POW_OPERATORS[RANDOM.nextInt(POW_OPERATORS.length)];
int num = Integer.parseInt(number);
return operator.equals("√") ? "√" + num : num + "^2";
}
private int generateValidAngle(String trigOp) {
int angle = RANDOM.nextInt(100) + 1;
if (trigOp.equals("tan")) {
while (angle == 90) {
angle = RANDOM.nextInt(100) + 1;
}
}
return angle;
}
private String addTrigonometryForced(String expression) {
String[] parts = expression.split(" ");
for (int i = 0; i < parts.length; i++) {
if (isNumber(parts[i])) {
parts[i] = applyTrigOperator(parts[i]);
break;
}
// 40% 概率加幂运算,避免与 trig 重叠
if (RANDOM.nextDouble() < 0.4) {
expression = powerManager.addPowerOperationsAvoid(expression, numPositions, trigPositions);
}
} while (expressionBuilder.hasDivideByZero(expression) || !hasTrig);
return String.join(" ", parts);
return expression;
}
}

@ -0,0 +1,70 @@
import java.util.List;
import java.util.Random;
/**
*
*/
public class TrigManager {
private static final Random RANDOM = new Random();
private static final String[] TRIG_FUNCTIONS = {"sin", "cos", "tan"};
//添加三角函数
public String addTrigOperations(String expression, List<Integer> numPositions) {
if (numPositions.isEmpty()) {
return expression;
}
String[] tokens = expression.split(" ");
int targetIndex = chooseTargetIndex(numPositions, tokens);
if (targetIndex == -1) {
return expression;
}
tokens[targetIndex] = applyTrig(tokens[targetIndex]);
return String.join(" ", tokens);
}
//随机函数种类
private int chooseTargetIndex(List<Integer> numPositions, String[] tokens) {
for (int tries = 0; tries < 50; tries++) {
int idx = numPositions.get(RANDOM.nextInt(numPositions.size()));
if (canApplyTrig(tokens, idx)) {
return idx;
}
}
return -1;
}
//检测三角函数是否合法
private boolean canApplyTrig(String[] tokens, int index) {
if (!isNumber(tokens[index])) {
return false;
}
int value = Integer.parseInt(tokens[index]);
// 防止 tan(90)
if (value == 90 && RANDOM.nextInt(3) == 2) {
return false;
}
// cos(90) 不加在除法右边
if (index > 0 && tokens[index - 1].equals("/") && value == 90) {
return false;
}
return true;
}
//添加操作
private String applyTrig(String token) {
String func = TRIG_FUNCTIONS[RANDOM.nextInt(TRIG_FUNCTIONS.length)];
return func + "(" + token + ")";
}
private boolean isNumber(String token) {
try {
Integer.parseInt(token);
return true;
} catch (NumberFormatException e) {
return false;
}
}
}

@ -1,3 +1,6 @@
/**
*
*/
public class Users {
private final String name;
private final String password;

Loading…
Cancel
Save