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.

9.2 KiB

数学题目生成器

这是潘俊晖在《软件工程导论》课程中的个人项目。
该项目是一个基于 Java 的命令行应用程序,支持用户登录并生成小学、初中、高中的数学题目。
项目采用策略模式设计,便于扩展新的题目生成器。


环境要求

  • JDK 版本JDK 8
  • 依赖库:纯 Java 项目,无需额外依赖
  • IDEIntelliJ IDEA也可通过命令行运行

运行

1. 通过 JRE 运行 jar 包

执行:

java -jar new-math-cli.jar

2. 克隆仓库并通过 IDEA 运行

代码说明

登陆检查

private static void handleLogin(Scanner scanner) {
    System.out.println("**********欢迎使用数学题目生成 Client **********");
    System.out.println("请输入用户名和密码,用空格隔开 (输入 'exit' 退出程序):");
    String line = scanner.nextLine();

    if ("exit".equalsIgnoreCase(line)) {
        System.out.println("感谢使用,程序已退出。");
        System.exit(0);
    }

    String[] input = line.split(" ");
    if (input.length == 2) {
        authService.login(input[0], input[1]).ifPresentOrElse(
                user -> {
                    currentUser = user;
                    currentLevel = user.getLevel();
                    System.out.printf("当前选择为%s出题%n", currentLevel.getName());},
                () -> System.out.println("请输入正确的用户名、密码")
        );
    } else {
        System.out.println("输入格式不正确,请重新输入。");
    }
}

生成题目时检查输入

private static void handleQuestionGeneration(Scanner scanner) {
    System.out.printf("准备生成%s数学题目, 请输入生成题目数量(输入-1将退出当前用户, 重新登录):%n", currentLevel.getName()); // 生成提示
    System.out.println("可输入“切换为(小学/初中/高中)”");
    String input = scanner.nextLine();

    if (input.startsWith("切换为")) {
        handleLevelSwitch(input);
        return;
    }

    try {
        int count = Integer.parseInt(input);
        if (count == -1) {
            currentUser = null;
            currentLevel = null;
            System.out.println("已退出,请重新登录。");
        } else if (count >= 10 && count <= 30) {
            testPaperService.generateAndSave(currentUser, currentLevel, count);
        } else {
            System.out.println("输入无效请输入10-30之间的数字或-1。");
        }
    } catch (NumberFormatException e) {
        System.out.println("输入无效,请输入一个数字。");
    }
}

题目查重和生成部分

public void generateAndSave(User user, Level level, int count) {
    Set<String> existingQuestions = loadExistingQuestions(user.getUsername());
    System.out.println("检测到历史题目 " + existingQuestions.size() + " 道。");

    IQuestionGenerator generator = createGenerator(level);
    Set<String> newQuestions = new HashSet<>();

    // 生成新的、与历史和本次不重复的题目
    for (int i = 0; i < count; i++) {
        Set<String> used = new HashSet<>(existingQuestions);
        used.addAll(newQuestions);

        String question = generator.generate(used);
        newQuestions.add(question);
    }

    saveToFile(user.getUsername(), newQuestions);
}

private Set<String> loadExistingQuestions(String username) {
    Set<String> existingQuestions = new HashSet<>();
    File userDir = new File(username);
    if (!userDir.exists() || !userDir.isDirectory()) {
        return existingQuestions;
    }

    File[] files = userDir.listFiles((dir, name) -> name.toLowerCase().endsWith(".txt"));
    if (files == null) {
        return existingQuestions;
    }

    for (File file : files) {
        try {
            Files.lines(file.toPath()).forEach(line -> {
                if (line.matches("^\\d+\\.\\s.*")) {
                    String question = line.substring(line.indexOf(' ') + 1).trim();
                    existingQuestions.add(question);
                }
            });
        } catch (IOException e) {
            System.err.println("错误:读取历史文件失败: " + file.getName());
        }
    }
    return existingQuestions;
}

题目生成接口和抽象类

// generator/IQuestionGenerator.java
package generator;

import java.util.Set;

public interface IQuestionGenerator {
    String generate(Set<String> existingQuestions);
}

// generator/BaseGenerator.java
package generator;

import java.util.Random;
import java.util.Set;

public abstract class BaseGenerator implements IQuestionGenerator {
    protected final Random random = new Random();

    protected int getRandomOperand() {
        return random.nextInt(100) + 1;
    }

    protected int getNumberOfOperands() {
        return random.nextInt(5) + 1;
    }

    @Override
    public String generate(Set<String> existingQuestions) {
        String question;
        do {
            question = createUniqueQuestion();
        } while (existingQuestions.contains(question));
        return question;
    }

    protected abstract String createUniqueQuestion();
}

小学题目生成器

public class ElementarySchoolGenerator extends BaseGenerator {
    private final String[] OPERATORS = {"+", "-", "*", "/"};

    @Override
    protected String createUniqueQuestion() {
        int numOperands = random.nextInt(4) + 2;
        StringBuilder sb = new StringBuilder();

        int runningTotal = getRandomOperand();
        sb.append(runningTotal);

        for (int i = 0; i < numOperands - 1; i++) {
            String operator = pickOperator();
            int operand = getRandomOperand();

            operator = adjustOperator(operator, operand, runningTotal);
            runningTotal = applyOperation(runningTotal, operator, operand);

            sb.append(" ").append(operator).append(" ").append(operand);
        }

        return sb.append(" = ?").toString();
    }

    private String pickOperator() {
        return OPERATORS[random.nextInt(OPERATORS.length)];
    }

    private String adjustOperator(String op, int operand, int current) {
        if (op.equals("-") && operand > current) return "+";
        if (op.equals("/") && (operand == 0 || current % operand != 0)) return "*";
        return op;
    }

    private int applyOperation(int current, String op, int operand) {
        switch (op) {
            case "+": return current + operand;
            case "-": return current - operand;
            case "*": return current * operand;
            case "/": return current / operand;
            default: throw new IllegalArgumentException("Unknown operator: " + op);
        }
    }
}

初中题目生成器

public class JuniorHighGenerator extends BaseGenerator {
    @Override
    protected String createUniqueQuestion() {
        int numOperands = random.nextInt(4) + 2;

        List<String> operands = new ArrayList<>();
        for (int i = 0; i < numOperands; i++) {
            operands.add(String.valueOf(getRandomOperand()));
        }

        int specialIndex = random.nextInt(numOperands);
        int originalValue = Integer.parseInt(operands.get(specialIndex));

        if (random.nextBoolean()) {
            operands.set(specialIndex, "sqrt(" + (originalValue * originalValue) + ")");
        } else {
            operands.set(specialIndex, originalValue + "^2");
        }

        StringBuilder questionBuilder = new StringBuilder();
        String[] basicOperators = {"+", "-", "*"};

        questionBuilder.append(operands.get(0));
        for (int i = 1; i < numOperands; i++) {
            String operator = basicOperators[random.nextInt(basicOperators.length)];
            questionBuilder.append(" ").append(operator).append(" ").append(operands.get(i));
        }
        questionBuilder.append(" = ?");

        return questionBuilder.toString();
    }
}

高中题目生成器

public class SeniorHighGenerator extends BaseGenerator {
    @Override
    protected String createUniqueQuestion() {
        int numOperands = random.nextInt(4) + 2;

        List<String> operands = new ArrayList<>();
        for (int i = 0; i < numOperands; i++) {
            operands.add(String.valueOf(getRandomOperand()));
        }

        int specialIndex = random.nextInt(numOperands);
        String[] trigFunctions = {"sin", "cos", "tan"};
        String func = trigFunctions[random.nextInt(trigFunctions.length)];
        int angle = random.nextInt(91);
        operands.set(specialIndex, String.format("%s(%d)", func, angle));

        StringBuilder questionBuilder = new StringBuilder();
        String[] basicOperators = {"+", "-", "*"};

        questionBuilder.append(operands.get(0));

        for (int i = 1; i < numOperands; i++) {
            String operator = basicOperators[random.nextInt(basicOperators.length)];
            questionBuilder.append(" ").append(operator).append(" ").append(operands.get(i));
        }
        questionBuilder.append(" = ?");

        return questionBuilder.toString();
    }
}

后续改进方向

  • 增加题目难度参数(如:数字范围、运算符种类)。

  • 增加 GUI 界面,提升交互体验。

  • 增加单元测试和持续集成,确保题目生成正确性。

  • 题目结果可导出为 PDF/Word 试卷格式。