From c202292688ab9d8e4ae98427a1efbb7ffe95c75c Mon Sep 17 00:00:00 2001 From: relentless <2464869638@qq.com> Date: Sun, 28 Sep 2025 23:29:13 +0800 Subject: [PATCH 1/2] modify --- .idea/.gitignore | 10 ++ .idea/google-java-format.xml | 6 + .idea/misc.xml | 6 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + relentless1.iml | 11 ++ src/ElementaryQuestionStrategy.java | 241 +++++++++++++++++--------- src/FileUtils.java | 129 +++++++++----- src/HighSchoolQuestionStrategy.java | 24 ++- src/Main.java | 177 ++++++++++--------- src/MathQuestion.java | 43 +++-- src/MiddleSchoolQuestionStrategy.java | 79 +++++---- src/QuestionGenerator.java | 102 ++++++----- src/QuestionRepository.java | 37 ++-- src/QuestionStrategy.java | 10 +- src/QuestionStrategyFactory.java | 32 ++-- src/User.java | 99 ++++++----- 17 files changed, 639 insertions(+), 381 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/google-java-format.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 relentless1.iml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..7bc07ec --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Environment-dependent path to Maven home directory +/mavenHomeManager.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/google-java-format.xml b/.idea/google-java-format.xml new file mode 100644 index 0000000..8b57f45 --- /dev/null +++ b/.idea/google-java-format.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..6f29fee --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..ad0c14c --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/relentless1.iml b/relentless1.iml new file mode 100644 index 0000000..c90834f --- /dev/null +++ b/relentless1.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/ElementaryQuestionStrategy.java b/src/ElementaryQuestionStrategy.java index 120e106..ee95a4c 100644 --- a/src/ElementaryQuestionStrategy.java +++ b/src/ElementaryQuestionStrategy.java @@ -1,18 +1,23 @@ import java.util.Random; +/** + * 小学题目生成策略实现类。 + * + *

随机生成包含加、减、乘、除运算的表达式,保证符合题目要求。 + */ public class ElementaryQuestionStrategy implements QuestionStrategy { + private static final String[] OPERATORS = {"+", "-", "*", "/"}; private final Random random = new Random(); - private final String[] operators = {"+", "-", "*", "/"}; @Override public String generateQuestion() { - int operandsCount = random.nextInt(4) + 2; // 操作数 2~5 + int operandsCount = random.nextInt(4) + 2; // 操作数数量:2~5 StringBuilder sb = new StringBuilder(); // 生成运算符序列 String[] chosenOps = generateOperators(operandsCount); - // 随机决定括号 + // 随机决定括号位置 int[] parentheses = decideParentheses(operandsCount, chosenOps); // 拼接表达式 @@ -22,130 +27,204 @@ public class ElementaryQuestionStrategy implements QuestionStrategy { } /** - * 生成运算符序列 - * 根据操作数数量,随机生成对应数量的运算符数组 - * + * 根据操作数数量,随机生成运算符数组。 + * * @param operandsCount 操作数数量 - * @return 随机生成的运算符数组,长度为操作数数量-1 + * @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)]; + chosenOps[i] = OPERATORS[random.nextInt(OPERATORS.length)]; } return chosenOps; } /** - * 决定是否需要括号,并返回括号的起始和结束位置 - * 对于3个及以上操作数,随机决定是否添加括号,并检查括号是否有效 - * + * 构建数学表达式。 + * + * @param sb StringBuilder 对象 * @param operandsCount 操作数数量 * @param chosenOps 运算符数组 - * @return 包含开括号和闭括号位置的数组,格式为[openParenIndex, closeParenIndex],-1表示不添加括号 + * @param parentheses 括号位置数组 [openParenIndex, closeParenIndex] */ - 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; - } + private void buildExpression( + StringBuilder sb, int operandsCount, String[] chosenOps, int[] parentheses) { + int openParenIndex = parentheses[0]; + int closeParenIndex = parentheses[1]; + + // 生成第一个操作数 + int prevNum = random.nextInt(100) + 1; + + // 如果开括号在第一个操作数之前 + if (openParenIndex == 0) { + sb.append("("); + } + sb.append(prevNum); + + // 如果闭括号在第一个操作数之后(即只包含第一个操作数) + if (closeParenIndex == 0) { + 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; + } + + // 确保括号正确闭合(安全措施) + String expression = sb.toString(); + int openCount = countChar(expression, '('); + int closeCount = countChar(expression, ')'); + + // 如果括号不匹配,重新生成表达式 + if (openCount != closeCount) { + sb.setLength(0); // 清空 StringBuilder + // 递归调用生成新的表达式(或者去掉所有括号) + buildExpressionWithoutParentheses(sb, operandsCount, chosenOps); } - 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; + private void buildExpressionWithoutParentheses( + StringBuilder sb, int operandsCount, String[] chosenOps) { + int prevNum = random.nextInt(100) + 1; + sb.append(prevNum); + + for (int i = 1; i < operandsCount; i++) { + String op = chosenOps[i - 1]; + sb.append(" ").append(op).append(" "); + + int num = generateOperand(op, prevNum); + sb.append(num); + prevNum = num; + } + } + + /** + * 计算字符串中特定字符的数量。 + * + * @param str 输入字符串 + * @param ch 要统计的字符 + * @return 字符出现次数 + */ + private int countChar(String str, char ch) { + int count = 0; + for (int i = 0; i < str.length(); i++) { + if (str.charAt(i) == ch) { + count++; } } - return true; + return count; } /** - * 构建数学表达式 - * 根据操作数、运算符和括号位置,拼接完整的数学表达式 - * - * @param sb StringBuilder对象,用于构建表达式 + * 决定是否需要括号,并返回括号位置。 改进版本:确保括号位置有效。 + * * @param operandsCount 操作数数量 * @param chosenOps 运算符数组 - * @param parentheses 括号位置数组,格式为[openParenIndex, closeParenIndex] + * @return 括号位置数组 [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]; + private int[] decideParentheses(int operandsCount, String[] chosenOps) { + if (operandsCount <= 2) { + return new int[] {-1, -1}; // 操作数太少,不需要括号 + } - // 处理第一个操作数可能的开括号 - if (openParenIndex == 0) sb.append("("); - sb.append(prevNum); - - // 处理最后一个操作数可能的闭括号 - if (closeParenIndex == operandsCount - 1) sb.append(")"); + if (!random.nextBoolean()) { + return new int[] {-1, -1}; // 随机决定不加括号 + } - // 构建剩余的操作数和运算符 - for (int i = 1; i < operandsCount; i++) { - String op = chosenOps[i - 1]; - sb.append(" ").append(op).append(" "); + // 生成有效的括号位置 + int openParenIndex = random.nextInt(operandsCount - 1); + int closeParenIndex = + random.nextInt(operandsCount - openParenIndex - 1) + openParenIndex + 1; - // 根据运算符生成下一个操作数 - int num = generateOperand(op, prevNum); + // 确保至少包含 2 个操作数 + if (closeParenIndex - openParenIndex < 2) { + closeParenIndex = openParenIndex + 1; + } - // 添加括号(如果需要) - if (i == openParenIndex) sb.append("("); - sb.append(num); - if (i == closeParenIndex) sb.append(")"); + // 如果括号包裹整个表达式,则不需要括号 + if (openParenIndex == 0 && closeParenIndex == operandsCount - 1) { + return new int[] {-1, -1}; + } - prevNum = num; // 更新上一个数字,供下一轮计算使用 + // 检查括号内运算符是否同一优先级,如果是则不需要括号 + boolean samePrecedence = checkPrecedenceEquality(chosenOps, openParenIndex, closeParenIndex); + if (samePrecedence) { + return new int[] {-1, -1}; } + + return new int[] {openParenIndex, closeParenIndex}; + } + + /** + * 判断括号内的运算符是否具有相同优先级。 + * + * @param chosenOps 运算符数组 + * @param openParenIndex 开括号位置 + * @param closeParenIndex 闭括号位置 + * @return true 如果括号内运算符优先级相同 + */ + 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 op 当前运算符 - * @param prevNum 前一个操作数的值 + * @param prevNum 上一个操作数 * @return 生成的操作数 */ private int generateOperand(String op, int prevNum) { - int num = random.nextInt(100) + 1; // 默认生成1-100之间的随机数 - - // 如果是减法运算符,确保不会出现负数结果 + int num = random.nextInt(100) + 1; if (op.equals("-") && num > prevNum) { - num = random.nextInt(prevNum) + 1; // 保证 num <= prevNum,避免负数结果 + num = random.nextInt(prevNum) + 1; } return num; } + /** + * 获取运算符的优先级。 + * + * @param op 运算符 + * @return 优先级数值 + */ private int getPrecedence(String op) { - if (op.equals("+") || op.equals("-")) return 1; - if (op.equals("*") || op.equals("/")) return 2; + if (op.equals("+") || op.equals("-")) { + return 1; + } + if (op.equals("*") || op.equals("/")) { + return 2; + } return 0; } } diff --git a/src/FileUtils.java b/src/FileUtils.java index a24ab81..8c89b9b 100644 --- a/src/FileUtils.java +++ b/src/FileUtils.java @@ -1,64 +1,97 @@ -import java.io.*; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; import java.text.SimpleDateFormat; -import java.util.*; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; /** - * 文件工具类:生成文件名、保存题目、读取已有题目 + * 文件工具类:用于生成文件名、保存题目、读取已有题目。 */ public class FileUtils { - // 生成文件名 - public static String generateFileName() { - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); - return sdf.format(new Date()) + ".txt"; - } + /** + * 生成基于时间戳的文件名。 + * + * @return 文件名,格式为 yyyy-MM-dd-HH-mm-ss.txt + */ + public static String generateFileName() { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); + return sdf.format(new Date()) + ".txt"; + } - // 保存题目到用户文件夹 - public static void saveQuestionsToFile(String userId, String fileName, List questions) { - File userFolder = new File(userId); - if (!userFolder.exists()) { - userFolder.mkdirs(); - } + /** + * 将题目保存到用户目录下的指定文件中。 + * + * @param userId 用户 ID(作为文件夹名) + * @param fileName 文件名 + * @param questions 要保存的题目列表 + */ + public static void saveQuestionsToFile( + String userId, String fileName, List questions) { + File userFolder = new File(userId); + if (!userFolder.exists()) { + userFolder.mkdirs(); + } - File file = new File(userFolder, fileName); - try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { - for (MathQuestion question : questions) { - writer.write("题号 " + question.getQuestionNumber() + ": " + question.getQuestionText()); - writer.newLine(); - writer.newLine(); - } - writer.flush(); - } catch (IOException e) { - throw new RuntimeException("写文件失败: " + file.getAbsolutePath(), e); - } + File file = new File(userFolder, fileName); + try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { + for (MathQuestion question : questions) { + writer.write( + "题号 " + question.getQuestionNumber() + ": " + + question.getQuestionText()); + writer.newLine(); + writer.newLine(); + } + writer.flush(); + } catch (IOException e) { + throw new RuntimeException("写文件失败: " + file.getAbsolutePath(), e); } + } - // 读取用户文件夹下所有 txt 文件中已存在的题目 - public static Set loadExistingQuestions(String userId) { - Set existingQuestions = new HashSet<>(); - File userFolder = new File(userId); - if (!userFolder.exists() || !userFolder.isDirectory()) return existingQuestions; + /** + * 从用户目录下的所有 txt 文件中读取已有的题目。 + * + * @param userId 用户 ID(作为文件夹名) + * @return 已存在的题目集合(按题目内容去重) + */ + public static Set loadExistingQuestions(String userId) { + Set existingQuestions = new HashSet<>(); + File userFolder = new File(userId); + if (!userFolder.exists() || !userFolder.isDirectory()) { + return existingQuestions; + } - File[] files = userFolder.listFiles((dir, name) -> name != null && name.endsWith(".txt")); - if (files == null) return existingQuestions; + File[] files = userFolder.listFiles( + (dir, name) -> name != null && name.endsWith(".txt")); + if (files == null) { + return existingQuestions; + } - for (File f : files) { - try (BufferedReader reader = new BufferedReader(new FileReader(f))) { - String line; - while ((line = reader.readLine()) != null) { - line = line.trim(); - if (line.isEmpty()) continue; - if (line.startsWith("题号")) { - int idx = line.indexOf(":"); - if (idx != -1 && idx + 1 < line.length()) { - existingQuestions.add(line.substring(idx + 1).trim()); - } - } - } - } catch (IOException e) { - e.printStackTrace(); + for (File f : files) { + try (BufferedReader reader = new BufferedReader(new FileReader(f))) { + String line; + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (line.isEmpty()) { + continue; + } + if (line.startsWith("题号")) { + int idx = line.indexOf(":"); + if (idx != -1 && idx + 1 < line.length()) { + existingQuestions.add(line.substring(idx + 1).trim()); } + } } - return existingQuestions; + } catch (IOException e) { + e.printStackTrace(); + } } + return existingQuestions; + } } diff --git a/src/HighSchoolQuestionStrategy.java b/src/HighSchoolQuestionStrategy.java index 85812b9..6f3b128 100644 --- a/src/HighSchoolQuestionStrategy.java +++ b/src/HighSchoolQuestionStrategy.java @@ -1,13 +1,19 @@ import java.util.Random; +/** + * 高中题目生成策略实现类。 + *

+ * 随机生成包含基本运算和三角函数的数学表达式。 + */ public class HighSchoolQuestionStrategy implements QuestionStrategy { + private static final String[] BASIC_OPS = {"+", "-", "*", "/"}; + private static final String[] TRIG_FUNCS = {"sin", "cos", "tan"}; + private final Random random = new Random(); - private final String[] basicOps = {"+", "-", "*", "/"}; - private final String[] trigFuncs = {"sin", "cos", "tan"}; @Override public String generateQuestion() { - // 随机操作数个数 2-5 + // 随机操作数个数:2~5 int operandsCount = random.nextInt(4) + 2; StringBuilder sb = new StringBuilder(); boolean hasTrig = false; @@ -15,9 +21,9 @@ public class HighSchoolQuestionStrategy implements QuestionStrategy { for (int i = 0; i < operandsCount; i++) { int num = random.nextInt(100) + 1; - // 每个操作数有概率加三角函数 + // 每个操作数有概率带三角函数 if (random.nextBoolean()) { - String func = trigFuncs[random.nextInt(trigFuncs.length)]; + String func = TRIG_FUNCS[random.nextInt(TRIG_FUNCS.length)]; sb.append(func).append("(").append(num).append(")"); hasTrig = true; } else { @@ -25,16 +31,16 @@ public class HighSchoolQuestionStrategy implements QuestionStrategy { } if (i != operandsCount - 1) { - String op = basicOps[random.nextInt(basicOps.length)]; + String op = BASIC_OPS[random.nextInt(BASIC_OPS.length)]; sb.append(" ").append(op).append(" "); } } - // 确保至少一个三角函数 + // 确保至少包含一个三角函数 if (!hasTrig) { - String func = trigFuncs[random.nextInt(trigFuncs.length)]; + String func = TRIG_FUNCS[random.nextInt(TRIG_FUNCS.length)]; int num = random.nextInt(100) + 1; - return func + "(" + num + ") + " + sb.toString(); + return func + "(" + num + ") + " + sb; } return sb.toString(); diff --git a/src/Main.java b/src/Main.java index 3a5deeb..e54199e 100644 --- a/src/Main.java +++ b/src/Main.java @@ -2,99 +2,120 @@ import java.io.IOException; import java.util.Scanner; /** - * 程序入口类,负责处理登录和题目生成。 + * 程序入口类。负责用户登录和题目生成。 */ public class Main { - public static void main(String[] args) throws IOException { - Scanner scanner = new Scanner(System.in); - User currentUser = null; + /** + * 程序入口。 + * + * @param args 命令行参数 + * @throws IOException 文件写入失败时抛出 + */ + public static void main(String[] args) throws IOException { + Scanner scanner = new Scanner(System.in); + User currentUser = null; - // 登录流程 - while (true) { - while (currentUser == null) { - System.out.print("请输入用户名和密码,用空格分隔:"); - String username = scanner.next(); - String password = scanner.next(); + // 登录流程 + while (true) { + while (currentUser == null) { + System.out.print("请输入用户名和密码,用空格分隔:"); + String username = scanner.next(); + String password = scanner.next(); - currentUser = User.login(username, password); + currentUser = User.login(username, password); - if (currentUser == null) { - System.out.println("请输入正确的用户名、密码"); - } else { - clearScreen(); //登录成功清屏 - System.out.println("当前选择为 " + currentUser.getRole() + " 出题"); - } - } + if (currentUser == null) { + System.out.println("请输入正确的用户名、密码"); + } else { + clearScreen(); // 登录成功清屏 + System.out.println("当前选择为 " + currentUser.getRole() + " 出题"); + } + } - // 每次登录后初始化当前出题类型(账号默认类型) - String currentRole = currentUser.getRole(); + // 每次登录后初始化当前出题类型(账号默认类型) + String currentRole = currentUser.getRole(); - // 题目生成流程 - while (true) { - System.out.println("准备生成 " + currentRole - + " 数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):"); + // 题目生成流程 + while (true) { + System.out.println( + "准备生成 " + currentRole + + " 数学题目,请输入生成题目数量(输入 -1 将退出当前用户,重新登录):"); - String input = scanner.next(); + String input = scanner.next(); - // 退出登录 - if (input.equals("-1")) { - currentUser = null; - clearScreen(); - System.out.println("已退出当前用户,重新登录..."); - break; - } + // 退出登录 + if (input.equals("-1")) { + currentUser = null; + clearScreen(); + System.out.println("已退出当前用户,重新登录..."); + break; + } - // 检测切换命令 - if (input.startsWith("切换为")) { - String newRole = input.substring(3).trim(); - if (!newRole.equals("小学") && !newRole.equals("初中") && !newRole.equals("高中")) { - System.out.println("请输入小学、初中和高中三个选项中的一个"+"(当前类型为"+currentRole+")"); - continue; - } - currentRole = newRole; - clearScreen(); - System.out.println("系统提示:准备生成 " + currentRole + " 数学题目,请输入生成题目数量"); - continue; - } + // 检测切换命令 + if (input.startsWith("切换为")) { + String newRole = input.substring(3).trim(); + if (!newRole.equals("小学") + && !newRole.equals("初中") + && !newRole.equals("高中")) { + System.out.println( + "请输入小学、初中和高中三个选项中的一个" + + " (当前类型为 " + currentRole + ")"); + continue; + } + currentRole = newRole; + clearScreen(); + System.out.println( + "系统提示:准备生成 " + currentRole + " 数学题目,请输入生成题目数量"); + continue; + } - // 输入题目数量 - int questionCount; - try { - questionCount = Integer.parseInt(input); - } catch (NumberFormatException e) { - System.out.println("请输入有效的数字或使用“切换为小学/初中/高中”命令"+"(当前类型为"+currentRole+")"); - continue; - } + // 输入题目数量 + int questionCount; + try { + questionCount = Integer.parseInt(input); + } catch (NumberFormatException e) { + System.out.println( + "请输入有效的数字或使用“切换为小学/初中/高中”命令" + + " (当前类型为 " + currentRole + ")"); + continue; + } - if (questionCount < 10 || questionCount > 30) { - System.out.println("请输入有效的题目数量 (10-30) 或 -1 退出"); - continue; - } + if (questionCount < 10 || questionCount > 30) { + System.out.println("请输入有效的题目数量 (10-30) 或 -1 退出"); + continue; + } - clearScreen(); + clearScreen(); - // 把 currentRole 传给 QuestionGenerator - QuestionGenerator generator = new QuestionGenerator(currentUser, currentRole); - generator.generateQuestions(questionCount); - System.out.println("题目已生成并保存!\n"); - } - } + // 生成题目 + QuestionGenerator generator = + new QuestionGenerator(currentUser, currentRole); + generator.generateQuestions(questionCount); + System.out.println("题目已生成并保存!\n"); + } } + } - /** - * 清屏方法 - */ - public static void clearScreen() { - try { - if (System.getProperty("os.name").contains("Windows")) { - new ProcessBuilder("cmd", "/c", "cls").inheritIO().start().waitFor(); - } else { - new ProcessBuilder("clear").inheritIO().start().waitFor(); - } - } catch (Exception e) { - for (int i = 0; i < 50; i++) { - System.out.println(); - } - } + /** + * 清屏方法。 + */ + public static void clearScreen() { + try { + if (System.getProperty("os.name").contains("Windows")) { + new ProcessBuilder("cmd", "/c", "cls") + .inheritIO() + .start() + .waitFor(); + } else { + new ProcessBuilder("clear") + .inheritIO() + .start() + .waitFor(); + } + } catch (Exception e) { + for (int i = 0; i < 50; i++) { + System.out.println(); + } } + } } diff --git a/src/MathQuestion.java b/src/MathQuestion.java index c727b28..356bbce 100644 --- a/src/MathQuestion.java +++ b/src/MathQuestion.java @@ -1,21 +1,36 @@ /** - * 数学题目类,表示每一道数学题。 + * 数学题目类。表示每一道数学题。 */ public class MathQuestion { - private int questionNumber; - private String questionText; + private final int questionNumber; + private final String questionText; - // 构造方法 - public MathQuestion(int questionNumber, String questionText) { - this.questionNumber = questionNumber; - this.questionText = questionText; - } + /** + * 构造方法。 + * + * @param questionNumber 题号 + * @param questionText 题目内容 + */ + public MathQuestion(int questionNumber, String questionText) { + this.questionNumber = questionNumber; + this.questionText = questionText; + } - public int getQuestionNumber() { - return questionNumber; - } + /** + * 获取题号。 + * + * @return 题号 + */ + public int getQuestionNumber() { + return questionNumber; + } - public String getQuestionText() { - return questionText; - } + /** + * 获取题目内容。 + * + * @return 题目内容 + */ + public String getQuestionText() { + return questionText; + } } diff --git a/src/MiddleSchoolQuestionStrategy.java b/src/MiddleSchoolQuestionStrategy.java index aa4c595..629cabe 100644 --- a/src/MiddleSchoolQuestionStrategy.java +++ b/src/MiddleSchoolQuestionStrategy.java @@ -1,47 +1,50 @@ import java.util.Random; +/** + * 初中数学题策略实现类。用于生成包含平方和根号的数学题目。 + */ public class MiddleSchoolQuestionStrategy implements QuestionStrategy { - private final Random random = new Random(); - private final String[] basicOps = {"+", "-", "*", "/"}; // 基本运算符 + private final Random random = new Random(); + private final String[] basicOps = {"+", "-", "*", "/"}; - @Override - public String generateQuestion() { - // 随机操作数个数 2-5 - int operandsCount = random.nextInt(4) + 2; - StringBuilder sb = new StringBuilder(); - boolean hasSquareOrRoot = false; + @Override + public String generateQuestion() { + // 随机操作数个数 2-5。 + int operandsCount = random.nextInt(4) + 2; + StringBuilder sb = new StringBuilder(); + boolean hasSquareOrRoot = false; - // 生成运算符和操作数 - for (int i = 0; i < operandsCount; i++) { - int num = random.nextInt(100) + 1; // 生成1-100之间的数字 + // 生成运算符和操作数。 + for (int i = 0; i < operandsCount; i++) { + int num = random.nextInt(100) + 1; // 生成 1-100 之间的数字。 - // 每个操作数有概率平方或开根号 - if (random.nextBoolean()) { - if (random.nextBoolean()) { - sb.append("(").append(num).append(")^2"); - hasSquareOrRoot = true; // 标记是否已经使用了平方或根号 - } else { - // 确保根号下的数为正 - int rootNumber = random.nextInt(100) + 1; // 始终生成正整数 - sb.append("√(").append(rootNumber).append(")"); - hasSquareOrRoot = true; // 标记是否已经使用了平方或根号 - } - } else { - sb.append(num); // 普通数字 - } - - // 添加运算符(除最后一个操作数外) - if (i != operandsCount - 1) { - String op = basicOps[random.nextInt(basicOps.length)]; - sb.append(" ").append(op).append(" "); - } - } - // 如果没有平方或根号,强制添加一个 - if (!hasSquareOrRoot) { - // 确保根号下的数为正 - int rootNumber = random.nextInt(100) + 1; // 始终生成正整数 - sb.append(" + √(").append(rootNumber).append(")"); + // 每个操作数有概率平方或开根号。 + if (random.nextBoolean()) { + if (random.nextBoolean()) { + sb.append("(").append(num).append(")^2"); + hasSquareOrRoot = true; + } else { + // 确保根号下的数为正。 + int rootNumber = random.nextInt(100) + 1; + sb.append("√(").append(rootNumber).append(")"); + hasSquareOrRoot = true; } - return sb.toString(); + } else { + sb.append(num); + } + + // 添加运算符(除最后一个操作数外)。 + if (i != operandsCount - 1) { + String op = basicOps[random.nextInt(basicOps.length)]; + sb.append(" ").append(op).append(" "); + } + } + + // 如果没有平方或根号,强制添加一个。 + if (!hasSquareOrRoot) { + int rootNumber = random.nextInt(100) + 1; + sb.append(" + √(").append(rootNumber).append(")"); } + return sb.toString(); + } } diff --git a/src/QuestionGenerator.java b/src/QuestionGenerator.java index bd56c29..882b9ea 100644 --- a/src/QuestionGenerator.java +++ b/src/QuestionGenerator.java @@ -1,51 +1,73 @@ import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; /** - * 题目生成器类 + * 题目生成器类。根据用户角色使用不同策略生成数学题目。 */ public class QuestionGenerator { - private User user; - private String role; + private static final int MAX_TRY_MULTIPLIER = 100; - public QuestionGenerator(User user, String role) { - this.user = user; - this.role = role; - } + /** 当前用户对象。 */ + private final User user; + + /** 当前用户角色(小学/初中/高中)。 */ + private final String role; + + /** + * 构造方法。 + * + * @param user 用户对象 + * @param role 用户角色 + */ + public QuestionGenerator(User user, String role) { + this.user = user; + this.role = role; + } + + /** + * 生成指定数量的题目并保存到文件中。 + * + * @param questionCount 要生成的题目数量 + * @throws IOException 写文件失败时抛出 + */ + public void generateQuestions(int questionCount) throws IOException { + // 获取策略 + QuestionStrategy strategy = QuestionStrategyFactory.getStrategy(role); - public void generateQuestions(int questionCount) throws IOException { - // 获取策略 - QuestionStrategy strategy = QuestionStrategyFactory.getStrategy(role); - - // 读出该用户所有历史题目 - Set existingQuestions = FileUtils.loadExistingQuestions(user.getUsername()); - Set newQuestionsSet = new HashSet<>(); - List questions = new ArrayList<>(); - - System.out.println("以下为生成的题目列表:\n"); - - int i = 1; - int tryCount = 0; - while (questions.size() < questionCount) { - String questionText = strategy.generateQuestion(); - tryCount++; - if (existingQuestions.contains(questionText) || newQuestionsSet.contains(questionText)) { - // 如果题库太小可能死循环,这里设置最大尝试次数 - if (tryCount > questionCount * 100) { - System.out.println("题库不足,无法生成足够不重复的题目"); - break; - } - continue; - } - - newQuestionsSet.add(questionText); - MathQuestion question = new MathQuestion(i, questionText); - questions.add(question); - System.out.println("题号 " + i + ":" + questionText + "\n"); - i++; + // 读取该用户所有历史题目 + Set existingQuestions = + FileUtils.loadExistingQuestions(user.getUsername()); + Set newQuestionsSet = new HashSet<>(); + List questions = new ArrayList<>(); + + System.out.println("以下为生成的题目列表:\n"); + + int i = 1; + int tryCount = 0; + while (questions.size() < questionCount) { + String questionText = strategy.generateQuestion(); + tryCount++; + if (existingQuestions.contains(questionText) + || newQuestionsSet.contains(questionText)) { + // 如果题库太小可能死循环,这里设置最大尝试次数 + if (tryCount > questionCount * MAX_TRY_MULTIPLIER) { + System.out.println("题库不足,无法生成足够不重复的题目"); + break; } + continue; + } - String fileName = FileUtils.generateFileName(); - FileUtils.saveQuestionsToFile(user.getUsername(), fileName, questions); + newQuestionsSet.add(questionText); + MathQuestion question = new MathQuestion(i, questionText); + questions.add(question); + System.out.println("题号 " + i + ":" + questionText + "\n"); + i++; } + + String fileName = FileUtils.generateFileName(); + FileUtils.saveQuestionsToFile(user.getUsername(), fileName, questions); + } } diff --git a/src/QuestionRepository.java b/src/QuestionRepository.java index d4fbc17..df386b2 100644 --- a/src/QuestionRepository.java +++ b/src/QuestionRepository.java @@ -2,25 +2,28 @@ import java.util.HashSet; import java.util.Set; /** - * 题目仓库类,负责管理已生成的题目,避免重复。 + * 题目仓库类。负责管理已生成的题目,避免重复。 */ public class QuestionRepository { - private Set generatedQuestions = new HashSet<>(); + /** 已生成的题目集合。 */ + private final Set generatedQuestions = new HashSet<>(); - /** - * 检查题目是否重复 - * @param question 题目 - * @return 是否重复 - */ - public boolean isDuplicate(String question) { - return generatedQuestions.contains(question); - } + /** + * 检查题目是否重复。 + * + * @param question 题目 + * @return 如果题目已存在返回 true,否则返回 false + */ + public boolean isDuplicate(String question) { + return generatedQuestions.contains(question); + } - /** - * 将题目添加到仓库 - * @param question 题目 - */ - public void addQuestion(String question) { - generatedQuestions.add(question); - } + /** + * 将题目添加到仓库。 + * + * @param question 题目 + */ + public void addQuestion(String question) { + generatedQuestions.add(question); + } } diff --git a/src/QuestionStrategy.java b/src/QuestionStrategy.java index 079889b..4ed868c 100644 --- a/src/QuestionStrategy.java +++ b/src/QuestionStrategy.java @@ -1,6 +1,12 @@ /** - * 出题策略接口 + * 出题策略接口。 */ public interface QuestionStrategy { - String generateQuestion(); + + /** + * 生成一道数学题目。 + * + * @return 返回生成的数学题目表达式 + */ + String generateQuestion(); } diff --git a/src/QuestionStrategyFactory.java b/src/QuestionStrategyFactory.java index e565ff5..fbae552 100644 --- a/src/QuestionStrategyFactory.java +++ b/src/QuestionStrategyFactory.java @@ -1,17 +1,25 @@ /** - * 出题策略工厂类,根据role返回对应策略 + * 出题策略工厂类。根据角色返回对应的策略。 */ public class QuestionStrategyFactory { - public static QuestionStrategy getStrategy(String role) { - switch (role) { - case "小学": - return new ElementaryQuestionStrategy(); - case "初中": - return new MiddleSchoolQuestionStrategy(); - case "高中": - return new HighSchoolQuestionStrategy(); - default: - throw new IllegalArgumentException("未知的角色: " + role); - } + + /** + * 根据用户角色获取对应的出题策略。 + * + * @param role 用户角色(小学、初中、高中) + * @return 对应的出题策略实现 + * @throws IllegalArgumentException 如果角色未知 + */ + public static QuestionStrategy getStrategy(String role) { + switch (role) { + case "小学": + return new ElementaryQuestionStrategy(); + case "初中": + return new MiddleSchoolQuestionStrategy(); + case "高中": + return new HighSchoolQuestionStrategy(); + default: + throw new IllegalArgumentException("未知的角色: " + role); } + } } diff --git a/src/User.java b/src/User.java index 56f2691..dcfbfc2 100644 --- a/src/User.java +++ b/src/User.java @@ -1,52 +1,67 @@ /** - * 用户类,包含登录验证和角色管理。 + * 用户类。包含登录验证和角色管理。 */ public class User { - private String username; - private String password; - private String role; + private final String username; + private final String password; + private final String role; - // 构造方法 - public User(String username, String password, String role) { - this.username = username; - this.password = password; - this.role = role; - } + /** + * 构造方法。 + * + * @param username 用户名 + * @param password 密码 + * @param role 用户角色 + */ + public User(String username, String password, String role) { + this.username = username; + this.password = password; + this.role = role; + } - /** - * 登录验证 - * @param username 用户名 - * @param password 密码 - * @return 返回用户对象或null - */ - public static User login(String username, String password) { - String[][] users = { - {"张三1", "123", "小学"}, {"张三2", "123", "小学"}, {"张三3", "123", "小学"}, - {"李四1", "123", "初中"}, {"李四2", "123", "初中"}, {"李四3", "123", "初中"}, - {"王五1", "123", "高中"}, {"王五2", "123", "高中"}, {"王五3", "123", "高中"} - }; + /** + * 登录验证。 + * + * @param username 用户名 + * @param password 密码 + * @return 如果用户名和密码正确,返回对应的用户对象;否则返回 {@code null} + */ + public static User login(String username, String password) { + String[][] users = { + {"张三1", "123", "小学"}, + {"张三2", "123", "小学"}, + {"张三3", "123", "小学"}, + {"李四1", "123", "初中"}, + {"李四2", "123", "初中"}, + {"李四3", "123", "初中"}, + {"王五1", "123", "高中"}, + {"王五2", "123", "高中"}, + {"王五3", "123", "高中"} + }; - for (String[] user : users) { - if (user[0].equals(username) && user[1].equals(password)) { - return new User(username, password, user[2]); - } - } - return null; + for (String[] user : users) { + if (user[0].equals(username) && user[1].equals(password)) { + return new User(username, password, user[2]); + } } + return null; + } - /** - * 获取用户角色 - * @return 用户角色 - */ - public String getRole() { - return this.role; - } + /** + * 获取用户角色。 + * + * @return 用户角色 + */ + public String getRole() { + return this.role; + } - /** - * 获取用户名 - * @return 用户名 - */ - public String getUsername() { - return this.username; - } + /** + * 获取用户名。 + * + * @return 用户名 + */ + public String getUsername() { + return this.username; + } } -- 2.34.1 From dc9bbb6816c4a71264eaeeb70c2d2183f5bb0a1d Mon Sep 17 00:00:00 2001 From: relentless <2464869638@qq.com> Date: Mon, 29 Sep 2025 08:45:10 +0800 Subject: [PATCH 2/2] modify9.29 --- src/ElementaryQuestionStrategy.java | 440 ++++++++++++++-------------- src/HighSchoolQuestionStrategy.java | 76 ++--- src/Main.java | 1 + src/QuestionGenerator.java | 1 + src/QuestionRepository.java | 29 -- 5 files changed, 260 insertions(+), 287 deletions(-) delete mode 100644 src/QuestionRepository.java diff --git a/src/ElementaryQuestionStrategy.java b/src/ElementaryQuestionStrategy.java index ee95a4c..e26193e 100644 --- a/src/ElementaryQuestionStrategy.java +++ b/src/ElementaryQuestionStrategy.java @@ -6,225 +6,225 @@ import java.util.Random; *

随机生成包含加、减、乘、除运算的表达式,保证符合题目要求。 */ public class ElementaryQuestionStrategy implements QuestionStrategy { - private static final String[] OPERATORS = {"+", "-", "*", "/"}; - private final Random random = new Random(); - - @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; - } - - /** - * 构建数学表达式。 - * - * @param sb StringBuilder 对象 - * @param operandsCount 操作数数量 - * @param chosenOps 运算符数组 - * @param parentheses 括号位置数组 [openParenIndex, closeParenIndex] - */ - private void buildExpression( - StringBuilder sb, int operandsCount, String[] chosenOps, int[] parentheses) { - int openParenIndex = parentheses[0]; - int closeParenIndex = parentheses[1]; - - // 生成第一个操作数 - int prevNum = random.nextInt(100) + 1; - - // 如果开括号在第一个操作数之前 - if (openParenIndex == 0) { - sb.append("("); - } - sb.append(prevNum); - - // 如果闭括号在第一个操作数之后(即只包含第一个操作数) - if (closeParenIndex == 0) { - 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; - } - - // 确保括号正确闭合(安全措施) - String expression = sb.toString(); - int openCount = countChar(expression, '('); - int closeCount = countChar(expression, ')'); - - // 如果括号不匹配,重新生成表达式 - if (openCount != closeCount) { - sb.setLength(0); // 清空 StringBuilder - // 递归调用生成新的表达式(或者去掉所有括号) - buildExpressionWithoutParentheses(sb, operandsCount, chosenOps); - } - } - - /** - * 构建没有括号的表达式(备用方法)。 - */ - private void buildExpressionWithoutParentheses( - StringBuilder sb, int operandsCount, String[] chosenOps) { - int prevNum = random.nextInt(100) + 1; - sb.append(prevNum); - - for (int i = 1; i < operandsCount; i++) { - String op = chosenOps[i - 1]; - sb.append(" ").append(op).append(" "); - - int num = generateOperand(op, prevNum); - sb.append(num); - prevNum = num; - } - } - - /** - * 计算字符串中特定字符的数量。 - * - * @param str 输入字符串 - * @param ch 要统计的字符 - * @return 字符出现次数 - */ - private int countChar(String str, char ch) { - int count = 0; - for (int i = 0; i < str.length(); i++) { - if (str.charAt(i) == ch) { - count++; - } - } - return count; - } - - /** - * 决定是否需要括号,并返回括号位置。 改进版本:确保括号位置有效。 - * - * @param operandsCount 操作数数量 - * @param chosenOps 运算符数组 - * @return 括号位置数组 [openParenIndex, closeParenIndex] - */ - private int[] decideParentheses(int operandsCount, String[] chosenOps) { - if (operandsCount <= 2) { - return new int[] {-1, -1}; // 操作数太少,不需要括号 - } - - if (!random.nextBoolean()) { - return new int[] {-1, -1}; // 随机决定不加括号 - } - - // 生成有效的括号位置 - int openParenIndex = random.nextInt(operandsCount - 1); - int closeParenIndex = - random.nextInt(operandsCount - openParenIndex - 1) + openParenIndex + 1; - - // 确保至少包含 2 个操作数 - if (closeParenIndex - openParenIndex < 2) { - closeParenIndex = openParenIndex + 1; - } - - // 如果括号包裹整个表达式,则不需要括号 - if (openParenIndex == 0 && closeParenIndex == operandsCount - 1) { - return new int[] {-1, -1}; - } - - // 检查括号内运算符是否同一优先级,如果是则不需要括号 - boolean samePrecedence = checkPrecedenceEquality(chosenOps, openParenIndex, closeParenIndex); - if (samePrecedence) { - return new int[] {-1, -1}; - } - - return new int[] {openParenIndex, closeParenIndex}; - } - - /** - * 判断括号内的运算符是否具有相同优先级。 - * - * @param chosenOps 运算符数组 - * @param openParenIndex 开括号位置 - * @param closeParenIndex 闭括号位置 - * @return true 如果括号内运算符优先级相同 - */ - 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 op 当前运算符 - * @param prevNum 上一个操作数 - * @return 生成的操作数 - */ - private int generateOperand(String op, int prevNum) { - int num = random.nextInt(100) + 1; - if (op.equals("-") && num > prevNum) { - num = random.nextInt(prevNum) + 1; - } - return num; - } - - /** - * 获取运算符的优先级。 - * - * @param op 运算符 - * @return 优先级数值 - */ - private int getPrecedence(String op) { - if (op.equals("+") || op.equals("-")) { - return 1; - } - if (op.equals("*") || op.equals("/")) { - return 2; - } - return 0; + private static final String[] OPERATORS = {"+", "-", "*", "/"}; + private final Random random = new Random(); + + @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; + } + + /** + * 构建数学表达式。 + * + * @param sb StringBuilder 对象 + * @param operandsCount 操作数数量 + * @param chosenOps 运算符数组 + * @param parentheses 括号位置数组 [openParenIndex, closeParenIndex] + */ + private void buildExpression( + StringBuilder sb, int operandsCount, String[] chosenOps, int[] parentheses) { + int openParenIndex = parentheses[0]; + int closeParenIndex = parentheses[1]; + + // 生成第一个操作数 + int prevNum = random.nextInt(100) + 1; + + // 如果开括号在第一个操作数之前 + if (openParenIndex == 0) { + sb.append("("); + } + sb.append(prevNum); + + // 如果闭括号在第一个操作数之后(即只包含第一个操作数) + if (closeParenIndex == 0) { + 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; + } + + // 确保括号正确闭合(安全措施) + String expression = sb.toString(); + int openCount = countChar(expression, '('); + int closeCount = countChar(expression, ')'); + + // 如果括号不匹配,重新生成表达式 + if (openCount != closeCount) { + sb.setLength(0); // 清空 StringBuilder + // 递归调用生成新的表达式(或者去掉所有括号) + buildExpressionWithoutParentheses(sb, operandsCount, chosenOps); + } + } + + /** + * 构建没有括号的表达式(备用方法)。 + */ + private void buildExpressionWithoutParentheses( + StringBuilder sb, int operandsCount, String[] chosenOps) { + int prevNum = random.nextInt(100) + 1; + sb.append(prevNum); + + for (int i = 1; i < operandsCount; i++) { + String op = chosenOps[i - 1]; + sb.append(" ").append(op).append(" "); + + int num = generateOperand(op, prevNum); + sb.append(num); + prevNum = num; + } + } + + /** + * 计算字符串中特定字符的数量。 + * + * @param str 输入字符串 + * @param ch 要统计的字符 + * @return 字符出现次数 + */ + private int countChar(String str, char ch) { + int count = 0; + for (int i = 0; i < str.length(); i++) { + if (str.charAt(i) == ch) { + count++; + } + } + return count; + } + + /** + * 决定是否需要括号,并返回括号位置。 改进版本:确保括号位置有效。 + * + * @param operandsCount 操作数数量 + * @param chosenOps 运算符数组 + * @return 括号位置数组 [openParenIndex, closeParenIndex] + */ + private int[] decideParentheses(int operandsCount, String[] chosenOps) { + if (operandsCount <= 2) { + return new int[] {-1, -1}; // 操作数太少,不需要括号 + } + + if (!random.nextBoolean()) { + return new int[] {-1, -1}; // 随机决定不加括号 + } + + // 生成有效的括号位置 + int openParenIndex = random.nextInt(operandsCount - 1); + int closeParenIndex = + random.nextInt(operandsCount - openParenIndex - 1) + openParenIndex + 1; + + // 确保至少包含 2 个操作数 + if (closeParenIndex - openParenIndex < 2) { + closeParenIndex = openParenIndex + 1; + } + + // 如果括号包裹整个表达式,则不需要括号 + if (openParenIndex == 0 && closeParenIndex == operandsCount - 1) { + return new int[] {-1, -1}; + } + + // 检查括号内运算符是否同一优先级,如果是则不需要括号 + boolean samePrecedence = checkPrecedenceEquality(chosenOps, openParenIndex, closeParenIndex); + if (samePrecedence) { + return new int[] {-1, -1}; + } + + return new int[] {openParenIndex, closeParenIndex}; + } + + /** + * 判断括号内的运算符是否具有相同优先级。 + * + * @param chosenOps 运算符数组 + * @param openParenIndex 开括号位置 + * @param closeParenIndex 闭括号位置 + * @return true 如果括号内运算符优先级相同 + */ + 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 op 当前运算符 + * @param prevNum 上一个操作数 + * @return 生成的操作数 + */ + private int generateOperand(String op, int prevNum) { + int num = random.nextInt(100) + 1; + if (op.equals("-") && num > prevNum) { + num = random.nextInt(prevNum) + 1; + } + return num; + } + + /** + * 获取运算符的优先级。 + * + * @param op 运算符 + * @return 优先级数值 + */ + private int getPrecedence(String op) { + if (op.equals("+") || op.equals("-")) { + return 1; + } + if (op.equals("*") || op.equals("/")) { + return 2; + } + return 0; + } } diff --git a/src/HighSchoolQuestionStrategy.java b/src/HighSchoolQuestionStrategy.java index 6f3b128..7709e08 100644 --- a/src/HighSchoolQuestionStrategy.java +++ b/src/HighSchoolQuestionStrategy.java @@ -6,43 +6,43 @@ import java.util.Random; * 随机生成包含基本运算和三角函数的数学表达式。 */ public class HighSchoolQuestionStrategy implements QuestionStrategy { - private static final String[] BASIC_OPS = {"+", "-", "*", "/"}; - private static final String[] TRIG_FUNCS = {"sin", "cos", "tan"}; - - private final Random random = new Random(); - - @Override - public String generateQuestion() { - // 随机操作数个数:2~5 - int operandsCount = random.nextInt(4) + 2; - StringBuilder sb = new StringBuilder(); - boolean hasTrig = false; - - for (int i = 0; i < operandsCount; i++) { - int num = random.nextInt(100) + 1; - - // 每个操作数有概率带三角函数 - if (random.nextBoolean()) { - String func = TRIG_FUNCS[random.nextInt(TRIG_FUNCS.length)]; - sb.append(func).append("(").append(num).append(")"); - hasTrig = true; - } else { - sb.append(num); - } - - if (i != operandsCount - 1) { - String op = BASIC_OPS[random.nextInt(BASIC_OPS.length)]; - sb.append(" ").append(op).append(" "); - } - } - - // 确保至少包含一个三角函数 - if (!hasTrig) { - String func = TRIG_FUNCS[random.nextInt(TRIG_FUNCS.length)]; - int num = random.nextInt(100) + 1; - return func + "(" + num + ") + " + sb; - } - - return sb.toString(); + private static final String[] BASIC_OPS = {"+", "-", "*", "/"}; + private static final String[] TRIG_FUNCS = {"sin", "cos", "tan"}; + + private final Random random = new Random(); + + @Override + public String generateQuestion() { + // 随机操作数个数:2~5 + int operandsCount = random.nextInt(4) + 2; + StringBuilder sb = new StringBuilder(); + boolean hasTrig = false; + + for (int i = 0; i < operandsCount; i++) { + int num = random.nextInt(100) + 1; + + // 每个操作数有概率带三角函数 + if (random.nextBoolean()) { + String func = TRIG_FUNCS[random.nextInt(TRIG_FUNCS.length)]; + sb.append(func).append("(").append(num).append(")"); + hasTrig = true; + } else { + sb.append(num); + } + + if (i != operandsCount - 1) { + String op = BASIC_OPS[random.nextInt(BASIC_OPS.length)]; + sb.append(" ").append(op).append(" "); + } } + + // 确保至少包含一个三角函数 + if (!hasTrig) { + String func = TRIG_FUNCS[random.nextInt(TRIG_FUNCS.length)]; + int num = random.nextInt(100) + 1; + return func + "(" + num + ") + " + sb; + } + + return sb.toString(); + } } diff --git a/src/Main.java b/src/Main.java index e54199e..57b714e 100644 --- a/src/Main.java +++ b/src/Main.java @@ -14,6 +14,7 @@ public class Main { public static void main(String[] args) throws IOException { Scanner scanner = new Scanner(System.in); User currentUser = null; + System.out.println("-----------------欢迎来到中小学数学卷子自动生成程序-----------------"); // 登录流程 while (true) { diff --git a/src/QuestionGenerator.java b/src/QuestionGenerator.java index 882b9ea..a9c70a6 100644 --- a/src/QuestionGenerator.java +++ b/src/QuestionGenerator.java @@ -50,6 +50,7 @@ public class QuestionGenerator { while (questions.size() < questionCount) { String questionText = strategy.generateQuestion(); tryCount++; + //这里是为了进行题目的查重 if (existingQuestions.contains(questionText) || newQuestionsSet.contains(questionText)) { // 如果题库太小可能死循环,这里设置最大尝试次数 diff --git a/src/QuestionRepository.java b/src/QuestionRepository.java deleted file mode 100644 index df386b2..0000000 --- a/src/QuestionRepository.java +++ /dev/null @@ -1,29 +0,0 @@ -import java.util.HashSet; -import java.util.Set; - -/** - * 题目仓库类。负责管理已生成的题目,避免重复。 - */ -public class QuestionRepository { - /** 已生成的题目集合。 */ - private final Set generatedQuestions = new HashSet<>(); - - /** - * 检查题目是否重复。 - * - * @param question 题目 - * @return 如果题目已存在返回 true,否则返回 false - */ - public boolean isDuplicate(String question) { - return generatedQuestions.contains(question); - } - - /** - * 将题目添加到仓库。 - * - * @param question 题目 - */ - public void addQuestion(String question) { - generatedQuestions.add(question); - } -} -- 2.34.1