Ứng dụng thuật toán xâu con chung dài nhất trong so sánh mã nguồn chương trình
Bước 1: Loại bỏ phần không liên quan, sau
đó tách riêng các từ khóa, câu lệnh và biến
trong code.
Bước 2: Áp dụng thuật toán tìm dãy các từ
khóa, câu lệnh dài nhất xuất hiện ở cả 2 code
giữ thứ tự trước sau. Nếu có nhiều dãy như vậy
ưu tiên các dãy có từ xuất hiện gần nhau nhất.
Bước 3: Dựa vào dãy các từ chung nhau tìm
mối liên quan giữa các biến của code1 và
code2, từ đó đưa ra kết luận biến code1 tương
ứng biến code2 hay không.
Bước 4: Truy vết đánh dấu dãy từ chung rồi
đưa ra output.
Bạn đang xem tài liệu "Ứng dụng thuật toán xâu con chung dài nhất trong so sánh mã nguồn chương trình", để tải tài liệu gốc về máy hãy click vào nút Download ở trên
Tóm tắt nội dung tài liệu: Ứng dụng thuật toán xâu con chung dài nhất trong so sánh mã nguồn chương trình
Trần Ngọc Hà và đtg Tạp chí KHOA HỌC & CÔNG NGHỆ 80(04): 109 - 113 109 ỨNG DỤNG THUẬT TOÁN XÂU CON CHUNG DÀI NHẤT TRONG SO SÁNH MÃ NGUỒN CHƯƠNG TRÌNH Trần Ngọc Hà1*, Triệu Hải Long1, Nguyễn Ngọc Tuấn2 1Trường Đại học Sư phạm, Đại học Thái Nguyên, 2 K17 Khoa học máy tính, Đại học Công Nghệ, Đại học Quốc gia Hà Nội TÓM TẮT Trong xử lý văn bản nói chung và mã nguồn chương trình nói riêng, việc tìm ra những phần giống nhau trong các văn bản hay mã nguồn chương trình có vai trò khá quan trọng, nhất là khi cần phát hiện sự sao chép code. Trong bài viết này, chúng tôi muốn giới thiệu thuật toán và các phương pháp xử lí liên quan nhằm tìm ra những phần giống nhau trong các code với độ phức tạp về mặt thời gian và chi phí bộ nhớ là tuyến tính, độ chính xác cao kể cả trong một số trường hợp đặc biệt. Từ khóa: So sánh văn bản, xâu con chung dài nhất, mã nguồn, danh sách kề, sắp xếp đếm phân phối MỞ ĐẦU* Mã nguồn là một dãy các câu lệnh được viết bằng một ngôn ngữ lập trình. Tìm phần chung giữa các code được hiểu là tìm các đoạn code có sự trùng lặp hoàn toàn hoặc một phần, có thể về nội dung hoặc cấu trúc đoạn code. Khi làm việc với code, nhất là code do người khác viết ra (cùng nhóm, học sinh...), việc tìm ra những phần giống nhau giúp giảm bớt thời gian, chi phí thực hiện, phát hiện sự sao chép... Tuy nhiên việc này gặp nhiều khó khăn như sự phức tạp của các ngôn ngữ lập trình, về mặt cú pháp, cách sử dụng câu lệnh... ví dụ ngôn ngữ lập trình Pascal không phân biệt kí tự thường và hoa, cản trở việc nhận dạng từ. Ở mức cao hơn, việc so sánh đoạn code gần giống nhau, như về câu lệnh, cấu trúc (ví dụ đoạn code chỉ khác nhau ở tên biến) hay có sự thêm bớt đoạn code khác thì chỉ cần đưa ra đoạn giống nhau. Hiện nay đã có nhiều nghiên cứu về so sánh văn bản[2][3][6][7] nhưng các phương pháp đề xuất chưa xử lí được với code. Để giải quyết các vấn đề trên chúng tôi đề xuất thuật toán xử lý được mô tả qua các bước như sau: Ta tạm coi code là chuỗi kí tự đã loại bỏ kí tự điều khiển, chú thích chỉ bao gồm các từ khóa (ví dụ var, if, for), câu lệnh (=, >, <), giá trị số coi là “từ”, và các “biến”. Giả sử code mẫu là code1, code đem so sánh là code2. *Tel: 0983.168.400; Email: hatn84@gmail.com Bước 1: Loại bỏ phần không liên quan, sau đó tách riêng các từ khóa, câu lệnh và biến trong code. Bước 2: Áp dụng thuật toán tìm dãy các từ khóa, câu lệnh dài nhất xuất hiện ở cả 2 code giữ thứ tự trước sau. Nếu có nhiều dãy như vậy ưu tiên các dãy có từ xuất hiện gần nhau nhất. Bước 3: Dựa vào dãy các từ chung nhau tìm mối liên quan giữa các biến của code1 và code2, từ đó đưa ra kết luận biến code1 tương ứng biến code2 hay không. Bước 4: Truy vết đánh dấu dãy từ chung rồi đưa ra output. Hình 1: Sơ đồ minh họa thuật toán Tách từ: Phân lớp các từ trong code thành 2 lớp riêng biệt dựa vào từ điển từ khóa và đặc trưng câu lệnh của ngôn ngữ lập trình. Tách từ code Biến Từ Code1 & Code2 Tìm dãy từ chung Xử lý biến Truy vết Dãy con chung dài nhất Trần Ngọc Hà và đtg Tạp chí KHOA HỌC & CÔNG NGHỆ 80(04): 109 - 113 110 Các biến có thể nhận biết dựa trên từ khóa khai báo, tùy vào ngôn ngữ lập trình và công cụ lập trình (IDE), có thể đi trước (C++)[4] hoặc đi sau (Pascal)... Các phần không liên quan cần loại bỏ để không đưa vào xử lí, ví dụ như phần chú thích, kí tự điều khiển, dẫn biên dịch, tiền xử lý... Tìm dãy chung: Việc sử dụng liên tiếp các câu lệnh giống nhau cho thấy có sự trùng lặp trong code, ta cần tìm ra các đoạn này với độ dài lớn nhất. Thuật toán trong mục II sẽ giúp tìm ra dãy từ chung dài nhất, và các từ này ưu tiên xuất hiện liền kề hoặc khoảng cách xuất hiện gần nhau nhất trong code. Trong ví dụ dưới đây với code1 và code2 cụ thể sẽ cho kết quả như sau: For i:=2 to n do For j:=n downto i do If A[j-1]>A[j] then Begin tg:=A[j-1]; A[j-1]:=A[j]; A[j]:=tg; End; a. Code1 For x:=2 to n do For y:=n downto x1 do If A[y-1]>A[y] then Begin tg:=A[y-1]; A[y-1]:=A[y]; A[y]:=tg; End; b. Code2 For i:=2 to n do For j:=n downto i do If A[j-1]>A[j] then Begin tg:=A[j-1]; A[j-]:=A[j]; A[j]:=tg; End; c. Dãy từ chung code1 và code2 (phần chung được đánh dấu bằng phần gạch chân). Trong ví dụ trên cả hai đoạn mã nguồn trên chỉ khác nhau ở tên biến nhưng cấu trúc câu lệnh giống nhau, code1 sử dụng “i” và “j” còn code2 dùng “x” và “y”, tuy nhiên cùng mô tả thuật toán sắp xếp nổi bọt, ta cần nhận dạng, đánh dấu cho bước xử lý sau đó. Xử lý biến: Quá trình này tìm ra mối liên quan giữa các biến trong code1 và code2, Trong so sánh code nếu code2 sử dụng 1 phần mã code1 thì các biến trong phần code đó giống hệt nhau, ta chỉ việc đánh dấu các biến đó trong code1 và code2. Tuy nhiên nếu code2 chỉ sử dụng một phần, hay thay chỉ thay đổi tên biến khác đi so với code1, thì cần nhận dạng sự tương ứng giữa các biến dựa trên dãy từ chung tìm được trước đó. Nếu vai trò 2 biến như nhau ta coi 2 biến đó là tương đương. Trong ví dụ 1 thì “i” và “x” là tương đương, và chúng ta có thể nhận biết điều này vì liền kề với “i” trong code1 và “x” trong code2 là dãy từ chung của 2 code và từ chung này giống nhau, ngoài ra vị trí, số lượng xuất hiện cũng trùng lặp, những cơ sở đó giúp ta đưa ra kết luận tương đương các biến , còn biến “i” và “y” không tương đương vì số lượng, vị trí xuất hiện không giống nhau. Truy vết & output: các dãy từ dài nhất không lưu trực tiếp trong bộ nhớ mà lưu dưới dạng dãy chỉ số từ trong code2, từ dãy chỉ số này ta khôi phục dãy từ chung và đưa ra output. CẢI TIẾN THUẬT TOÁN TÌM DÃY CHUNG DÀI NHẤT Sau khi tách được các từ trong code1 và code2, từ 2 dãy từ (input) nhiệm vụ của chúng ta là tìm dãy chung dài nhất giữa 2 dãy, sau đó đánh dấu và đưa ra output của bài toán. Để tiện theo dõi ta coi code1 là một dãy các từ đặt trong mảng A[n] (n là số từ code1),tương tự code2 là B[m] với m là số từ trong code2. Thuật toán tìm dãy từ trên thuật toán Hunt [5] có nhiệm vụ tìm ra dãy từ chung dài nhất từ A[n] và B[m] sao cho dãy chung có các từ xuất hiện trong code1 và code2 là gần nhất. Tư tưởng chung của thuật toán như sau: A[i]=B[j]với mỗi A[i] ta định vị một từ B[j] tương ứng sao cho: Trần Ngọc Hà và đtg Tạp chí KHOA HỌC & CÔNG NGHỆ 80(04): 109 - 113 111 [ ] [ ] min A i B j j = Để làm việc này ta sử dụng một mảng THRESH[O:n] để lưu vị trí j min trong B sao cho A[i]=B[j] và có thể chèn vào dãy từ chung. Với mỗi A[i] nếu được định vị trong B làm tăng kích thước THRESH lên 1 đơn vị, cuối cùng kích thước THRESH chính là độ dài dãy từ lớn nhất. Tuy nhiên dãy THRESH chứa vị trí các từ trong B chưa chắc đã là vị trí các từ thuộc dãy chung lớn nhất, mà chúng được lưu lại trong danh sách LINK, gồm thông tin nối từ i sang j và từ chung trước đó LINK[k-1]. Cuối cùng sử dụng danh sách LINK để khôi phục dãy chung dài nhất. Để tăng tốc thuật toán và giảm bớt bộ nhớ, với mỗi A[i] thay vì lần lượt tìm B[j] ta tìm B[j] qua mảng MATCHLIST chứa chỉ số các từ trùng lặp B[j] được xây dựng trước đó. Ví dụ A=”a b c b d d a” B=”b a d b a b d” MATCHLIST[1] = (5, 2) MATCHLIST[2] = (6, 4, 1) MATCHLIST[3] = ( ) MATCHLIST[4] = MATCHLIST[2] MATCHLIST[5] = (7, 3) MATCHLIST[6] = MATCHLIST[5] MATCHLIST[7] = MATCHLIST[I] Hay để giảm chi phí tìm kiếm và bộ nhớ hơn nữa có thể tổ chức MATCHLIST dưới dạng danh sách kề (adjacency list)[1]. Chi tiết thuật toán như sau: integer array THRESH[O:n]; list array MATCHLIST[1 :n]; pointer array LINK[1 :n]; pointer PTR; Step 1: Xây dựng MATCHLIST; for i := 1 step 1 until n do set MATCHLIST[i] : (j1 ,j2, ... ,jp) such that j1 >j2 > ... >jp and code1[i] = code2[jq] for i <= q < =p; Step 2: Khởi tạo THRESH; THRESH[O] := 0; for i := 1 step 1 until n do THRESH[i] := n + 1; LINK[O] := null; Step 3: Tìm dãy chung lớn nhất; for i := 1 step 1 until n do for j on MATCHLIST[i] do begin find k such that THRESH[k-1] < j < THRESH[k]; if j < THRESH[k] then begin THRESH[k] := j; LINK[k] := newnode ( i, j, LINK[k-l]); end; end; Step 4: Khôi phục dãy chung; k := largest k such that THRESH[k] ≠ n + 1; PTR := LINK[k]; while PTR ≠ null do begin print (i,j) pair pointed to by PTR; advance PT end. PHÂN TÍCH VÀ ĐÁNH GIÁ Do làm việc với A[n] lần lượt từ đầu tới cuối đúng 1 lần nên không cần lưu trữ code1 mà có thể làm việc trực tiếp từ file lưu trữ code1. Các mảng sử dụng trong thuật toán đều 1 chiều và độ lớn nhỏ hơn max(n, m), do vậy chi phí bộ nhớ là tuyến tính. Chi phí thời gian của thuật toán chủ yếu cho Step 3, tuy nhiên việc tìm vị trí k trong THRESH có thể sử dụng phương pháp tìm kiếm nhị phân, do dễ dàng nhận thấy các giá trị trong THRESH luôn tăng. Trong Step 1, để xây dựng MATCHLIST, có thể đưa về sắp xếp B[m] kèm lưu chỉ số, tuy nhiên vì số lượng các từ trong ngôn ngữ lập trình không nhiều, ta có thể dùng phương pháp sắp xếp đếm phân phối (distribution sort)[1] để giảm chi phí thời gian xuống O(m). Dễ thấy độ phức tạp thời gian Trần Ngọc Hà và đtg Tạp chí KHOA HỌC & CÔNG NGHỆ 80(04): 109 - 113 112 của thuật toán là O((r + n) log n) với r<max(n, m); còn chi phí về mặt bộ nhớ làO(max(n,m). Đây là những chi phí chấp nhận được và có thể áp dụng với code rất dài. THỰC NGHIỆM Với những cơ sở lý thuyết nêu trên chúng tôi xây dựng một chương trình minh họa xử lý văn bản, và so sánh với một số chương trình sẵn có, tuy nhiên do hạn chế thời gian nên chương trình minh họa chưa trang bị đầy đủ chức năng trong bài viết mà tập trung tìm dãy từ chung dài nhất giữa hai code. Hình 2 minh họa giao diện chương trình, sau khi xử lý các code1 và code2, chương trình sẽ đưa ra phần code chung bằng cách đánh dấu và tỉ lệ khớp (match). Cũng với đoạn code1 và code2 như trên, thì phần mềm của nhóm tác giả Đại học Bách khoa Hà Nội chưa nhận biết những đoạn code trùng nhau. Kết quả minh họa như trong hình 3. Hình 2: Chương trình minh họa Hình 3. Phần mềm so sánh văn bản của ĐHBK Hà Nội Trần Ngọc Hà và đtg Tạp chí KHOA HỌC & CÔNG NGHỆ 80(04): 109 - 113 113 Hình 4. So sánh code với CodeCompare Với phần mềm CodeCompare, một sản phẩm thương mại của công ty phát triển phần mềm Devart (đối tác cung cấp phần mềm của các tập đoàn lớn trên thế giới như Hitachi, Siemens, Intel...), hầu hết các đoạn code giống nhau được phát hiện, tuy nhiên chưa chỉ ra được các biến có vai trò tương tự nhau. Các phần khác nhau được đánh dấu đậm hơn, các phần giống nhau hoàn toàn được giữ nguyên. TỔNG KẾT Trên đây chúng tôi đã giới thiệu phương pháp tìm phần chung của code, thuật toán tìm dãy từ chung dài nhất và xây dựng chương trình minh họa tính năng so sánh mã nguồn, đây là những ứng dụng thực tế giúp ích cho người lập trình hoặc giáo viên... những người làm việc với code. Trong tương lai có thể áp dụng cơ sở lý thuyết để phát triển ứng dụng thông minh và tự động hơn, như tự động so sánh code với các cơ sở dữ liệu sẵn có như Internet, cơ sở dữ liệu bài thi có sẵn... Cuối cùng chúng tôi xin gửi lời cảm ơn thạc sỹ Nguyễn Văn Trường, khoa Toán, Đại học Sư phạm - Đại học Thái Nguyên, đã có nhiều đóng góp giúp chúng tôi thực hiện bài báo này. TÀI LIỆU THAM KHẢO [1]. Alfred V.Aho, Jeffrey D.Ullman, John E.Hopcroft, (January 11, 1983), Data Structures and Algorithms, Addison Wesley. [2]. Beate Commentz-Walter, 1979, A string matching algorithm fast on the average Extended abstract, Springerlink: Volume 71/1979, 118-132, DOI: 10.1007/3-540-09510-1_10. [3]. Bilenko; Mooney; Cohen; Ravikumar, P.Fienberg, Sep/Oct 2003, Adaptive name matching in information integration, IEEE. [4]. Herbert Schildt (1998-08-01). C++ The Complete Reference Third Edition. Osborne McGraw-Hill. ISBN 978-0078824760. [5]. James W.Hunt, Thomas G.Szymanski, May 1977, A fast algorithm for computing longest common subsequences, Association for Computing Machinery: Volume 20 Issue 5. [6]. Michael Steinbach George Karypis Vipin Kumar, 2000, A Comparison of Document Clustering Techniques, Citeseerx. [7]. William W. Cohen, Pradeep Ravikumar Stephen, E. Fienberg, 2003, A Comparison of String Metrics for Matching Names and Records, the Proceedings of the IJCAI. SUMMARY COMPARE SOURCE CODE OF PROGRAMS USING FAST LONGEST COMMON SUBSEQUENCES ALGORITHM Tran Ngoc Ha1*, Trieu Hai Long1, Nguyen Ngoc Tuan2 1 College of Education – Thai Nguyen University, 2 K17 Computer Science – College of Technology-Vietnam National University, Hanoi Finding the similar part of text of source code plays an important role in detecting forged code. In this paper, we present our algorithm and involved methods to find out similar codes. That can reduce time and space complexities to linear ones, increase the rate of accuracy. Keywords: Compare documents, Longest Common Subsequences, Source code, adjacency list, distribution sort. * Tel: 0983.168.400; Email: hatn84@gmail.com
File đính kèm:
- ung_dung_thuat_toan_xau_con_chung_dai_nhat_trong_so_sanh_ma.pdf