Mục Tiêu Dự Án
Hoạt động này tập trung vào việc xây dựng một giải pháp tự động hóa để xác định tỷ lệ trùng lặp nội dung giữa hai file văn bản. Cụ thể, chương trình cần nhận diện sự giống nhau giữa một tài liệu gốc và phiên bản đã bị chỉnh sửa (thêm/xóa/sửa đổi) để đưa ra chỉ số định lượng.
Yêu Cầu Đầu Vào Và Đầu Ra
- Đầu vào: Nhận đường dẫn tuyệt đối của ba file thông qua tham số dòng lệnh:
- File văn bản gốc.
- File văn bản nghi ngờ sao chép.
- File đích để ghi kết quả phân tích.
- Kết quả: Giá trị số thực hiển thị tỷ lệ tương đồng, được làm tròn đến hai chữ số thập phân.
- Công cụ mẫu: Sử dụng dữ liệu mẫu đã cung cấp trong lớp học để kiểm tra khả năng xử lý các trường hợp chèn hoặc xóa ký tự.
Tổng Quan Kiến Trúc Phần Mềm
Cấu trúc mã nguồn được thiết kế theo mô hình phân tầng rõ ràng nhằm đảm bảo tính mở rộng và dễ bảo trì:
| Tầng (Layer) | Thành Phần Chính | Nhiệm Vụ Chức Năng |
|---|---|---|
| Lớp Ứng Dụng (Application) | Main |
Xử lý giao tiếp với người dùng, đọc biến môi trường, gọi API lõi và xuất báo cáo. |
| Lớp Nghiệp Vụ (Business Logic) | Class ContentAnalyzer |
Thực thi thuật toán so khớp, quản lý các phương thức tính toán độ tương tự khác nhau. |
| Lớp Tiện Ích (Utility) | Module xử lý chuỗi | Mã hóa, chuẩn hóa ký tự và loại bỏ khoảng trắng thừa trước khi so sánh. |
Chi Tiết Thuật Toán Tính Tương Đồng
Hai đoạn văn bản được so sánh dựa trên độ dài của Dãy Con Chung Dài Nhất (Longest Common Subsequence - LCS). Tỷ lệ trùng lặp được tính bằng công thức:
$$ \text{Tỷ Lệ} = \frac{\text{Độ Dài LCS}}{\text{Độ Dài Văn Bản Gốc}} $$
Các Biến Thể Của Hàm Tính LCS
- Phân tích tiêu chuẩn (
getLCSScore): Dùng ma trận 2 chiều. Phù hợp cho văn bản ngắn vì độ phức tạp không gian là $O(N \times M)$. - Tối ưu bộ nhớ (
optimizeMemoryLCS): Chỉ giữ lại cột trước đó để cập nhật trạng thái hiện tại. Độ phức tạp không gian giảm xuống $O(\min(N, M))$. - Tối ưu khối (
blockChunkLCS): Chia nhỏ văn bản thành các đoạn (chunk) để tính toán cục bộ. Giải quyết vấn đề tràn bộ nhớ trên file lớn.
Đánh Giá Hiệu Năng
Các thử nghiệm đo lường hiệu suất cho thấy sự cải thiện đáng kể về tài nguyên hệ thống khi áp dụng các biến thể tối ưu:
- Độ phức tạp thời gian: Giữ nguyên $O(N \times M)$ cho tất cả các biến thể, nhưng hằng số thực thi nhỏ hơn nhờ tận dụng CPU cache.
- Độ phức tạp không gian: Giảm thiểu từ vài MB (đối với file lớn 20k ký tự ở bản tiêu chuẩn) xuống còn vài KB (ở bản tối ưu).
- Khả năng chịu tải: Phiên bản tối ưu thứ 3 có thể xử lý hàng chục nghìn ký tự mà không gây lỗi tràn bộ nhớ stack.
Mã Nguồn Kiểm Thử Đơn Vị (Unit Test)
Dưới đây là các kịch bản kiểm thử đã được viết lại để xác minh tính đúng đắn của hàm tính toán, sử dụng khung thử nghiệm MSTest.
1. Xử Lý Trường Hợp Rỗng
TEST_CASE(Test_Null_Input_Handling) {
// Thử nghiệm với cả hai đầu vào đều rỗng
double result = Analyzer.calculate("", "");
Assert::IsTrue(result == 0.0);
// Thử nghiệm với một bên rỗng
double ratio2 = Analyzer.calculate("Nội dung duy nhất", "");
Assert::IsTrue(ratio2 == 0.0);
}
2. Đối Chiếu Đoạn Văn Giống Nhau
TEST_CASE(Test_Identical_Text_Scores) {
string txtA = "Hệ thống hoạt động tốt";
string txtB = "Hệ thống hoạt động tốt";
double score = Analyzer.compare(txtA, txtB);
Assert::AreEqual(1.0, score, 1e-5, "Văn bản hoàn toàn giống nhau phải có điểm 1.0");
}
3. Xử Lý Sự Thay Đổi Từ Ngữ (Paraphrasing)
TEST_CASE(Test_Paraphrased_Content) {
// Thay đổi một số từ nhưng ý nghĩa giữ nguyên
string orig = "Tôi đi xe buýt đến trường";
string mod = "Tôi dùng xe buýt đến trường";
double similarity = Analyzer.compare(orig, mod);
// Kết quả phải nằm trong khoảng 0.4 đến 0.9 do có thay đổi ký tự
Assert::IsTrue(similarity >= 0.4 && similarity <= 0.9);
}
4. Xử Lý File Lớn Không Bị Crashes
TEST_CASE(Test_Long_File_Stability) {
// Tạo chuỗi giả kích thước lớn
string bigText(50000, 'X');
string sameText(50000, 'X');
auto start = high_resolution_clock::now();
double val = Analyzer.compare(bigText, sameText);
auto stop = high_resolution_clock::now();
Assert::IsTrue(val > 0.9); // Phải rất cao vì giống hệt
// Không được gây lỗi OutOfMemoryException
}
Quy Trình Quản Lý Mã Nguồn Với Git
Toàn bộ vòng đời phát triển được giám sát qua nền tảng GitHub để đảm bảo tính đồng bộ và ổn định:
- Tạo Branch: Mỗi tính năng mới bắt buộc tạo nhánh riêng (ví dụ:
feature/lcs-optimization) từ nhánh chínhmain. - Commit & Push: Cập nhật thông tin commit tuân thủ chuẩn mực, ngắn gọn và chứa hướng dẫn thay đổi.
- Pull Request: Sau khi hoàn thành và test trên máy chủ, nộp yêu cầu gộp mã (PR) để đội ngũ rà soát lỗi.
- Merge: Khi xung đột được giải quyết, mã sẽ được hợp nhất vào nhánh gốc.
- Release: Các tệp tin nhị phân và mã nguồn cuối cùng được đóng gói và tải lên mục Releases.