Chương 3 Phương pháp xác định vị trí lỗi và gợi ý sửa lỗi
3.3. Gợi ý sửa lỗi chương trình
3.3.3. So sánh các chuỗi Tokens
Đầu vào của mô-đun gợi ý chương trình là chương trình lỗi của học sinh và rất nhiều chương trình mẫu khác được lưu trong bộ sưu tập. Vì vậy, việc so sánh được hiểu là so sánh chương trình lỗi với tất cả các chương trình mẫu trong bộ sưu tập. Để thực hiện việc này, chương trình lỗi sẽ so sánh lần lượt với từng chương trình mẫu (Hình 3.6). Với mỗi bộ chương trình lỗi – chương trình mẫu sẽ tính ra tỉ lệ tương đồng, kết quả này được hiển thị cho người dùng là căn cứ lựa chọn chương trình mẫu nào làm gợi ý.
Bước phân tích chương trình thành chuỗi các Tokens như mô tả ở Mục 3.3.2 cho kết quả mỗi chương trình được phân tách thành nhiều Tokens khác nhau, ta gọi là chuỗi các Tokens. So sánh hai chuỗi Tokens trong JPlag sử dụng thuật toán Greedy-String- Tiling (GST) [30] được phát triển bởi Michael Wise, sau đó các tác giả đã cải tiến bằng cách kết hợp với thuật toán Karp-Rabin để nâng cao hiệu quả.
So sánh hai chuỗi Tokens A và B, mục đích là tìm ra chuỗi con chung lớn nhất của hai mã nguồn từ đó tính ra tỉ lệ trùng lắp so với tổng thể chương trình. Chuỗi con này phải đảm bảo nguyên tắc:
i. Một Token trong A chỉ được so khớp với một Token trong B
ii. Các chuỗi con được tìm thấy phải độc lập với vị trí của chúng trong chuỗi. Hay việc thay đổi vị trí các thành phần trong mã nguồn chương trình không làm ảnh hưởng tới việc so sánh.
Chương trình lỗi Chương trình mẫu 1 . . . . Chương trình mẫu n Chương trình mẫu 2 So sánh So sánh So sánh
22
1 GSTiling (String A, String B) {
2 tiles { } do
3 maxmatch MinimumMatchLength
4 matches { }
5 for unmarkerd tokens Aa in A do 6 for unmarkerd tokens Bb in B do 7 j 0
8 while (Aa+j == Bb+j ) ∩ unmarkedAa+j ∩ unmarked Bb+j do
9 j++
10 end
11 if (j == maxmatch) then
12 matches matches match (a; b; j)
13 elseif j> maxmatch then
14 matches match (a; b; j) }
15 maxmatch j
16 end 17 end
18 for match(a; b; maxmatch) ∈ matches do
19 for j = 0 … (maxmatch -1) do 20 mark (Aa+j);
21 mark (Bb+j); 22 end
23 Tiles tiles ∪ match (a; b; maxmatch)
24 end
25 while (maxmatch > MinimumMatchLengt);
26 return tiles;
Thuật toán 3.1. Thuật toán Greedy-String-Tiling (GST) [30]
Thuật toán GST (Thuật toán 3.1) có đầu vào là hai chuỗi Tokens A, B. Thuật toán trả về kết quả là vị trí bắt đầu đoạn con lớn nhất trong chuỗi A (a), chuỗi B (b) và độ dài chuỗi con lớn nhất của A và B (maxmatch) – dòng 23. Bắt đầu thuật toán, biến maxmatch được khởi tạo giá trị bằng giá trị MinimumMatchLength (MML) hay độ dài tối thiểu chuỗi con. Thuật toán thực hiện hai việc, đầu tiên là tìm kiếm chuỗi con tương đồng dài nhất, tiếp theo là với chuỗi con dài nhất được tìm thấy tiến hành đánh dấu vị trí để đảm bảo nguyên tắc không lặp lại (nguyên tắc i ở trên). Nếu maxmatch được tìm thấy lớn hơn giới hạn MML thì kết quả mới được tính là hợp lệ.
Tìm kiếm chuỗi con dài nhất mô tả từ dòng 5 đến dòng 17 gồm ba vòng lặp. Vòng for đầu tiên thực hiện duyệt tất cả các Token trong chuỗi A. Với mỗi Token trong chuỗi
23
A, lần lượt duyệt tất cả các Token trong chuỗi B (vòng for thứ hai) để so sánh. Nếu Token trong A và B trùng nhau thì thêm Token đó vào chuỗi Token con (vòng lặp while) và tăng giá trị độ dài chuỗi con maxmatch lên. Trong trường hợp chuỗi con được tìm thấy đã nằm trong danh sách đánh dấu thì giữ nguyên độ dài match và không thêm chuỗi con đó vào chuỗi con đang có (dòng 13 đến dòng 15).
Đánh dấu vị trí chuỗi con dài nhất với vòng for (từ dòng 18 đến 24), trả về kết quả giá trị match, bao gồm vị trí bắt đầu chuỗi con lớn nhất trong A và B và độ dài chuỗi con. Việc đánh dấu vị trí lỗi này thực hiện lặp lại cùng với quá trình tìm kiếm chuỗi con lớn nhất ở trên, để đảm bảo rằng một vị trí chuỗi con đã được đánh dấu là chuỗi con dài nhất match sẽ không được lặp lại ở một vị trí khác trong chương trình.
Giả sử cần so sánh hai mã nguồn, Mã nguồn 3.2 (đóng vai trò như mã nguồn A) và Mã nguồn 3.3 (đóng vai trò như mã nguồn B) trong thuật toán GST. Mã nguồn A là mã nguồn đúng được xem xét đưa ra làm gợi ý, mã nguồn B là mã nguồn lỗi đang cần gợi ý sửa. Mã nguồn B được so sánh với mọi mã nguồn đúng khác, khi so sánh với mỗi mã nguồn đúng cho một tỉ lệ tương đồng, dựa trên tỉ lệ này để lựa chọn gợi ý nào.
Code Tokens
1 import java.util.Scanner; IMPORT
2 public class Problem55184 { BEGINCLASS
3 public static int UCLN(int x, inty){ VARDEEF,VARDEF,BEGINMENTHOD
4 if (x==0) BEGINIF
5 return x; RETURN, ENDIF
6 while (x == y) { BEGINWHILE
7 if(x>y) x = x - y; BEGINIF, ASSIGN
8 else y = y - x; ELSE, ASSIGN, ENDIF
9 } ENDWHILE
10 System.out.println(y); APPLY
11 return x; RETURN
12 } ENDMENTHOD
13 public static void main(String arg[]) VARDEF, BEGINMENTHOD
14 {
15 Scanner nhap = new Scanner(System.in); VARDEF,NEWCLASS,ASSIGN
16 int a,b; VARDEF, VARDEF
17 a = nhap.nextInt(); ASSIGN
18 b = nhap.nextInt(); ASSIGN
19 System.out.println(UCLN(a,b)); APPLY
20 } ENDMENTHOD
21 } ENDCLASS
24
Áp dụng thuật toán GST, với giới hạn MML là 3, so sánh và tìm kiếm các chuỗi con trong mã nguồn A và B, kết quả được mô tả như trong Bảng 3.3. Ta tìm được được bốn chuỗi con trùng nhau của A và B. Các chuỗi con có độ dài tương ứng 14, 5, 3, 6, tổng độ dài các chuỗi con trùng nhau (coverage) trong A và B là 28. Tổng số Tokens trong mã nguồn A là 31 Tokens, mã nguồn B là 30 Tokens. Áp dụng công thức tính độ tương đồng [24] (Công thức 3.2) cho độ tương đồng giữa mã nguồn A và B là 91.8%. Với độ tương đồng của mã nguồn A và B là 91.8%, xếp theo thứ tự là độ tương đồng mức độ cao, mã nguồn A có thể là gợi ý sửa lỗi cho mã nguồn B
𝑆𝑖𝑚(𝐴, 𝐵) = 2 ∗ 𝑐𝑜𝑣𝑒𝑟𝑎𝑔𝑒(𝑡𝑖𝑡𝑙𝑒𝑠)
|𝐴| + |𝐵|
Bảng 3.3. So sánh và tìm chuỗi con trong mã nguồn A và B
IMPORT IMPORT BEGINCLASS BEGINCLASS VARDEF VARDEF VARDEF VARDEF BEGINMENTHOD BEGINMENTHOD BEGINIF BEGINIF RETURN RETURN ENDIF ENDIF BEGINWHILE BEGINWHILE BEGINIF BEGINIF ASSIGN ASSIGN ELSE ELSE ASSIGN ASSIGN ENDIF ENDIF ENDWWHILE ENDWWHILE RETURN APPLY ENDMENTHOD RETURN VARDEF ENDMENTHOD BEGINMENTHOD VARDEF VARDEF BEGINMENTHOD VARDEF VARDEF NEWCLASS NEWCLASS ASSIGN ASSIGN VARDEF VARDEF ASSIGN VARDEF VARDEF ASSIGN ASSIGN ASSIGN ASSIGN APPLY APPLY ENDMENTHOD ENDMENTHOD ENDCLASS ENDCLASS
Mã nguồn 3.2 (A) Mã nguồn 3.3 (B) (3.2)
25
Trong ví dụ nêu trên, ở Mã nguồn 3.3 cách đặt tên biến, tên hàm, cách viết biểu thức gán cũng khác so với Mã nguồn 3.2. Ngoài ra Mã nguồn 3.3 cũng được viết ngắn gọn, không chứa những dòng trống. Nhưng khi tách thành Tokens hai mã nguồn vẫn có tỉ lệ chuỗi con giống nhau nhiều, do đó tỉ lệ tương đồng cao. Tỉ lệ tương đồng càng cao thì chương trình đưa ra gợi ý càng hiệu quả. Như vậy kể cả chương trình có độ tương đồng đạt 100% thì cũng không có nghĩa là hai chương trình giống hệt nhau. Điều này giúp phát triển tư duy của học sinh trong quá trình học tập, bởi chương trình được gợi ý có nhiều điểm tương đồng chứ không giống hệt chương trình lỗi buộc học sinh phải tư duy, phải căn cứ chỉ lỗi mà sửa bài chứ không đơn giản là sao chép kết quả sang.
Trong thuật toán này cần lưu ý giá trị MML. Nếu ta lấy MML bằng một kết quả trả về có thể không chính xác, vì hai chuỗi có độ dài bằng một có khả năng trùng nhau một cách ngẫu nhiên là rất cao, chúng có thể ở các vị trí khác nhau trong chương trình, và việc tìm chuỗi con dài nhất không khả thi. Trong bài toán này các chương trình của học sinh phần đa là các chương trình đơn giản, số lượng dòng lệnh không nhiều. Nếu lựa chọn giá trị MML quá cao thì khả năng không tìm được kết quả tương xứng hoặc độ phù hợp rất thấp. Nếu lựa chọn MML quá thấp, giả sử bằng một như mô tả bên trên sẽ không hiệu quả. Do đó trong quá trình cài đặt thuật toán, giá trị MML được đặt là ba, là giá trị phù hợp.
Để làm đầy thêm bộ sưu tập và tăng hiệu quả gợi ý do những chương trình học sinh viết sẽ gần gũi hơn với nhau, nên sau khi chương trình của học sinh được sửa và chạy chính xác, chương trình sẽ được gắn nhãn và đưa vào bộ sưu tập tại mục tương ứng với đề bài.