Viết chương trình dòng lệnh tự động sinh bài tập toán tiểu học bốn phép tính

Mục tiêu của dự án là xây dựng một ứng dụng dòng lệnh trên nền tảng Java, có khả năng tự động tạo ngẫu nhiên các bài tập toán cơ bản dành cho học sinh tiểu học — bao gồm cộng, trừ, nhân, chia — với các ràng buộc phù hợp: kết quả không âm, số hạng nằm trong phạm vi cho trước, và đảm bảo tính duy nhất của từng biểu thức.

Chương trình được thiết kế theo hai chế độ chính:

  • Chế độ sinh đề: Nhận tham số số lượng bài tập (-n) và giới hạn giá trị (-r), sau đó xuất ra hai tệp văn bản riêng biệt: Exercises.txt (chứa các biểu thức dạng "7 + 3 = ?") và Answers.txt (chứa đáp án tương ứng).
  • Chế độ chấm điểm: Đọc hai tệp đầu vào, so sánh từng đáp án người dùng với kết quả đúng được tính toán lại từ biểu thức, rồi ghi báo cáo chi tiết vào Grade.txt dưới dạng danh sách câu đúng/sai kèm số thứ tự.

Để xử lý yêu cầu "không xuất hiện số âm trong kết quả", chương trình áp dụng chiến lược điều chỉnh trước khi thực hiện phép trừ: nếu số bị trừ nhỏ hơn số trừ, hai giá trị sẽ được hoán đổi. Với phép chia, mẫu số luôn được đảm bảo khác 0 bằng cách tái sinh ngẫu nhiên trong khoảng [1, range].

Để loại bỏ trùng lặp, hệ thống sử dụng cấu trúc Set<String> để lưu trữ các biểu thức đã sinh. Mỗi lần tạo mới, biểu thức chỉ được chấp nhận nếu chưa tồn tại trong tập hợp — đảm bảo tính đa dạng cho bộ đề.

Dưới đây là phiên bản cải tiến của hàm sinh đề, với logic rõ ràng hơn, tên biến mô tả chức năng và xử lý ngoại lệ toàn diện:

private static void generateExercises(int count, int maxValue) throws IOException {
    Set<String> uniqueExercises = new HashSet<>();
    try (FileWriter exerciseWriter = new FileWriter("Exercises.txt");
         FileWriter answerWriter = new FileWriter("Answers.txt")) {

        Random rng = new Random();
        while (uniqueExercises.size() < count) {
            int opIndex = rng.nextInt(4); // 0:+, 1:-, 2:*, 3:/
            int leftOperand = rng.nextInt(maxValue) + 1;
            int rightOperand = rng.nextInt(maxValue) + 1;

            String expression = "";
            String result = "";

            switch (opIndex) {
                case 0: // Cộng
                    expression = leftOperand + " + " + rightOperand + " = ?";
                    result = String.valueOf(leftOperand + rightOperand);
                    break;
                case 1: // Trừ (đảm bảo kết quả ≥ 0)
                    int larger = Math.max(leftOperand, rightOperand);
                    int smaller = Math.min(leftOperand, rightOperand);
                    expression = larger + " - " + smaller + " = ?";
                    result = String.valueOf(larger - smaller);
                    break;
                case 2: // Nhân
                    expression = leftOperand + " × " + rightOperand + " = ?";
                    result = String.valueOf(leftOperand * rightOperand);
                    break;
                case 3: // Chia nguyên (mẫu số ≠ 0)
                    if (rightOperand == 0) rightOperand = 1;
                    expression = leftOperand + " ÷ " + rightOperand + " = ?";
                    result = String.valueOf(leftOperand / rightOperand);
                    break;
            }

            if (!uniqueExercises.contains(expression)) {
                uniqueExercises.add(expression);
                exerciseWriter.write(expression + "\n");
                answerWriter.write(result + "\n");
            }
        }
    }
}

Hàm kiểm tra đáp án được viết lại dưới dạng hàm thuần túy, sử dụng biểu thức chính quy để trích xuất toán hạng và toán tử một cách linh hoạt, đồng thời xử lý tất cả các trường hợp ngoại lệ khi phân tích chuỗi:

private static boolean verifyAnswer(String expression, String userAnswer) {
    try {
        String cleanExpr = expression.split("=")[0].trim();
        int[] operands = extractOperands(cleanExpr);
        char operator = extractOperator(cleanExpr);

        int expected = computeResult(operands[0], operands[1], operator);
        return expected == Integer.parseInt(userAnswer.trim());
    } catch (Exception e) {
        return false;
    }
}

private static int[] extractOperands(String expr) {
    String[] parts = expr.split("[+\\-×÷]");
    return new int[]{
        Integer.parseInt(parts[0].trim()),
        Integer.parseInt(parts[1].trim())
    };
}

private static char extractOperator(String expr) {
    for (char c : expr.toCharArray()) {
        if (c == '+' || c == '-' || c == '×' || c == '÷') {
            return c;
        }
    }
    throw new IllegalArgumentException("Không tìm thấy toán tử hợp lệ");
}

private static int computeResult(int a, int b, char op) {
    return switch (op) {
        case '+' -> a + b;
        case '-' -> a - b;
        case '×' -> a * b;
        case '÷' -> b != 0 ? a / b : 0;
        default -> 0;
    };
}

Cơ chế đọc tệp đầu vào được tối ưu bằng khối try-with-resources, đảm bảo giải phóng tài nguyên tự động ngay cả khi xảy ra lỗi. Báo cáo chấm điểm được định dạng rõ ràng, liệt kê số thứ tự các câu đúng và sai trong ngoặc đơn, giúp giáo viên hoặc phụ huynh dễ dàng đối chiếu.

Thẻ: Java command-line math-generation algorithm education-software

Đăng vào ngày 28 tháng 5 lúc 03:42