Luận văn Nghiên cứu các phương pháp biểu diễn tri thức trong lập trình logic

MỤC LỤC MỞ ĐẦU 3 Chương 1 CHƯƠNG TRÌNH LOGIC TỔNG QUÁT 5 1.1 Mở đầu .5 1.2 Biểu diễn tri thức trong chương trình logic tổng quát .12 1.3 Câu trả lời cho truy vấn .17 1.4 Một số ngữ nghĩa khác của chương trình logic tổng quát 19 Chương 2 LẬP TRÌNH LOGIC MỞ RỘNG 22 2.1 Biểu diễn tri thức sử dụng các chương trình logic mở rộng .26 2.2 Ngữ nghĩa khác của chương trình logic mở rộng .37 2.3 Các chương trình logic phân biệt (Disjunctive Logic Programs) 38 2.3.1 Giới thiệu .38 2.3.2 Biểu diễn tri thức sử dụng chương trình logic phân biệt 42 2.3.3 Tìm câu trả lời cho truy vấn .46 Chương 3 MÔI TRƯỜNG LẬP TRÌNH LOGIC 50 3.1 Giới thiệu 50 3.2 Hệ thống DLV 53 3.2.1 Ngôn ngữ của môi trường DLV 54 3.2.2 Cấu trúc một chương trình .57 a. Cơ sở dữ liệu mở rộng – EDB 57 b. Cơ sở dữ liệu cơ bản – IDB .58 (i) Luật .58 (i.1) Luật ngầm định 59 2 (i.2) Luật phân biệt 61 (i.3) Luật phủ định 62 (ii) Ràng buộc 65 Chi Ha(ii.1) Ràng buộc toàn vẹn 65 (ii.2) Ràng buộc yếu 67 3.3 Gói DLV trong Java .70 3.3.2 Kiến trúc gói DLV: lớp DlvHandler 72 Chương 4 CÁC BÀI TOÁN MINH HỌA 77 4.1 Bài toán N quân hậu .78 4.1.1 Phân tích bài toán .78 4.1.2 Cài đặt 82 4.2 Bài toán Cây khung nhỏ nhất .84 4.2.1 Mô tả bài toán 84 4.2.2 Phân tích và cài đặt 85 a. Chương trình logic DLV .85 b. Cài đặt trên Java 87 KẾT LUẬN 93 TÀI LIỆU THAM KHẢO 95 PHỤ LỤC 97

pdf114 trang | Chia sẻ: maiphuongtl | Lượt xem: 1824 | Lượt tải: 0download
Bạn đang xem trước 20 trang tài liệu Luận văn Nghiên cứu các phương pháp biểu diễn tri thức trong lập trình logic, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
ilter=member Best model: {member(a,p2),member(b,p1),member(c,p1),member(d,p2), member(e,p2)} Cost ([Weight:Level]): Best model: {member(a,p1),member(b,p2),member(c,p2),member(d,p1), member(e,p1)} Cost ([Weight:Level]): □ Nếu dòng lệnh chứa lựa chọn -costbound=weight[,weight], tất cả các mô hình có giá trị nhỏ hơn hoặc bằng costbound sẽ được tính. Chú ý rằng không phải tất cả các mô hình được tính sẽ là mô hình tốt nhất. Ta có thể kết hợp một số nguyên dương với mức ưu tiên của costbound. Nếu thay số nguyên dương bằng dấu gạch dưới _ thì có nghĩa là mức đánh giá tương ứng là không giới hạn. Ví dụ, chạy DLV với sự lựa chọn sau: $ DLV take2.dl -costbound=5,10,_ sẽ thiết lập giới hạn trên cho các giá trị của mô hình tính toán, trong đó hai giá trị đầu tiên phải có giá trị nhỏ hơn hoặc bằng 5 và 10, trong khi đó, giá trị cuối cùng là không có giới hạn. Và mức đánh giá cuối cùng này được coi là mức ưu tiên cao nhất. Nếu một vài mức ưu tiên đã có trong dữ liệu vào, chỉ những giá trị tương ứng với mức ưu tiên thấp hơn sẽ được gán lại giá trị cao hơn, còn các mức khác sẽ là không giới hạn. Nói cách khác, các mức ưu tiên cao hơn sẽ bị loại bỏ. Nếu giới hạn trên nhỏ hơn giá trị của các mô hình tốt nhất, kết quả nhận được sẽ là không có mô hình nào cả. Chú ý rằng số lượng các mô hình được tính toán có thể được giới hạn bằng cách sử dụng lựa chọn -n. Ví dụ: $ DLV take2.dl -costbound=10 -n=1 sẽ yêu cầu tính toán một mô hình có giá trị cao nhất nhỏ hơn hoặc bằng 10. 70 3.3 Gói DLV trong Java Phần này sẽ mô tả thư viện lập trình giúp các ứng dụng Java có thể sử dụng được DLV. Gói DLV là một thư viện hướng đối tượng thực hiện trong Java, cho phép nhúng một chương trình logic phân biệt vào trong một chương trình Java. Toàn bộ thư viện này được mô tả trong hình 3.1. Phần này cũng mô tả chi tiết các lớp cho phép mô hình hóa dữ liệu vào ra của DLV, và lớp nhân DlvHandler chứa các đặc tính quan trọng nhất. Hình 3-1 Mô hình lớp UML cho gói DLV 3.3.1 Biểu diễn dữ liệu: các lớp Predicate, Literal, Model và Program Phần này mô tả cú pháp và ngữ nghĩa của DLV. Chương trình logic phân biệt (DLV input) là một tập hữu hạn các luật; các mô hình biểu diễn cho lời giải của chương trình (DLV output) là các tập phần tử nền. Ta sẽ xem xét cách liên kết vào ra của DLV với thư viện hướng đối tượng. Đầu tiên là các thư viện Literal và Predicate, mô hình hóa các vị từ nền và các dạng cơ bản của nó (các phần tử nền). Sau đó là các lớp Model và Program tương ứng với mô hình hóa vào ra của DLV. Cần chú ý rằng, các phần tử nền có thể là một phần của một chương trình logic. Ta sử dụng các 71 lớp Literal và Predicate để điều khiển dễ dàng hơn cả dữ liệu vào và ra của DLV. Lớp Literal: Lớp Literal mô hình hóa các phần tử nền. Nó cung cấp các phương pháp truy nhập và thay đổi toán hạng và xác nhận liệu một phần tử là khẳng định hay không và tạo ra dạng bù của nó. Lớp Literal là lớp con của lớp Predicate. Do đó, các đối tượng Literal có quan hệ gần với các đối tượng của Predicate. Lớp Predicate: Lớp Predicate mô hình hóa các vị từ nền. Lớp Predicate là một công cụ mạnh, cho phép người lập trình Java quản lý dữ liệu được sử dụng làm dữ liệu vào của DLV và xử lý dữ liệu ra của DLV. Nó cung cấp việc truy nhập đầy đủ để xác định các đặc điểm như là tên của vị từ, bậc và kích thước. Hơn thế nữa, nó cung cấp hai giao diện hiệu quả để truy nhập đến các phần tử nền. Giao diện thứ nhất bắt nguồn từ giao diện nổi tiếng java.util.Enumeration và giao diện thứ hai có cơ sở từ giao diện java.sql.ResultSet thuộc JDBC. Các giao diện này đưa ra các điều khiển đầy đủ cho việc tính toán với các vị từ nền. Enumeration-like hữu ích nếu ta muốn truy nhập trực tiếp vào các đối tượng Literal. ResultSet-like có thể giúp người lập trình Java chuyên lập trình cơ sở dữ liệu và JDBC có thể quản lý các vị từ nền theo cách tốt nhất. Mặc dù các cột trong bảng cơ sở dữ liệu có tên và kiểu, nhưng các thông tin kiểu này không tồn tại trong các tham số của vị từ. Để thực hiện đầy đủ giao diện ResultSet-like, ta có thêm lớp PredicateMetaData. Lớp PredicateMetaData ánh xạ tên và kiểu dữ liệu đến mỗi tham số trong một vị từ. Bằng cách sử dụng kiểu thông tin này, ta thực hiện các phương thức giống như int getInt(String name) để khôi phục thông tin, từ hàng hiện tại (phần tử hiện tại) trên cột (tham số) đặt tên là “name”, và tự động truyền nó dưới dạng 72 một giá trị nguyên. Một ánh xạ PredicateMetaData chỉ được yêu cầu thực hiện một số phương thức đặc biệt trong giao diện ResultSet-like, và về mặt tổng quát nó không có tính chất bắt buộc. The Model class Lớp Model biểu diễn các mô hình là kết quả DLV (tức là các tập trả lời). Do các mô hình là các tập vị từ nền, lớp Model thực hiện một tập các đối tượng Predicate. Ta có thể khôi phục các đối tượng Predicate bên trong Model, có thể xác định tên của nó (sử dụng phương thức getPredicate) hoặc theo một cách tuần tự, ta có thể lựa chọn một trong hai phương thức, một thuộc giao diện java.util.Enumeration, một thuộc giao diện java.sql.ResultSet. Một hằng số tĩnh “NO_Model” cho phép biểu diễn một chương trình không có mô hình nào. Ta có thể kiểm tra một đối tượng Model có tên là m có mô hình hay không bằng cách gọi phương thức m.isNoModel() hoặc kiểm tra m == Model.NO_ Model. The Program class Lớp Program mô hình hóa các chương trình logic. DLV có một cơ cấu mềm dẻo để biểu diễn chương trình đầu vào. Nó cho phép chia một chương trình logic thành nhiều file văn bản. Lớp Program đã mở rộng cơ cấu này. Trên thực tế, nó cũng cho phép biểu diễn dữ liệu vào bằng cách sử dụng các đối tượng String và Predicate. Sự mở rộng này nhúng hoàn toàn các chương trình logic vào trong các chương trình Java. Với cách này, DLV có thể được quản lý trực tiếp bởi các đối tượng Java. Hơn thế nữa, ta có thể lấy dữ liệu từ bảng cơ sở dữ liệu quan hệ được hỗ trợ bởi JDBC bằng cách sử dụng lớp JDBCPredicate. 3.3.2 Kiến trúc gói DLV: lớp DlvHandler Trong phần này, ta sẽ phác thảo kiến trúc toàn diện của gói DLV và lớp DlvHandler chứa các đặc tính cơ bản của gói DLV. 73 Ở phần trước, ta đã biết đến cách viết và thực hiện một chương trình trong DLV. Ta có thể chạy nó từ một dòng lệnh mô tả các thông số cần thiết và chương trình đầu vào. Các tập trả lời DLV sẽ được đưa ra dưới dạng văn bản. Gói DLV thực hiện DLV trong một cách xử lý mở rộng tự nhiên, thực hiện như một dòng lệnh gọi cơ bản. Mỗi ứng dụng Java có một lớp đơn java.lang.Runtime cho phép ứng dụng tự thực hiện giao diện của nó với môi trường mà ứng dụng đang chạy trong đó. Phương thức Runtime.exec() tạo ra một tiến trình tự nhiên và đưa ra một lớp con của java.lang.Process có thể được sử dụng để điều khiển một tiến trình và thu nhận thông tin về nó. Lớp java.lang.Process cung cấp các phương thức để thực hiện đầu vào và đầu ra như một tiến trình, chờ cho tiến trình này thực hiện xong, kiểm tra trạng thái kết thúc và phá hủy tiến trình này. Lớp DlvHandler quản lý một đối tượng DLV là một tiến trình tự nhiên, bằng cách sử dụng phương thức Runtime.exec() và lớp java.lang.Process. Tất cả các điều khiển I/O DLV chuẩn sẽ được định hướng đến đối tượng DlvHandler (bên trong máy ảo Java). Với cách này, lớp DlvHandler có thể đưa dữ liệu vào cho DLV và lấy kết quả từ DLV. Chú ý rằng các thông số cần thiết cho DLV và các file văn bản chứa chương trình vào được xác định là các thông số dòng lệnh là một mảng xâu. Dữ liệu được lưu trong bộ nhớ (bằng cách sử dụng đối tượng Predicate) hoặc trong các cơ sở dữ liệu được định hướng đến DLV. Đối tượng DlvHandler sẽ tập trung các kết quả DLV và phân tích chúng, xây dựng một tập các đối tượng Model. Toàn bộ quá trình xử lý này được mô tả trong hình 3.2. 74 Hình 3-2 Quá trình xử lý DLV Tất cả các lớp của gói DLV trong Java được đặt trong một gói có tên là DLV. Để nhúng các chương trình logic phân biệt vào bên trong các câu lệnh Java, ta phải gọi đến gói DLV và thực hiện các bước sau: 1. Thiết lập các thông số cần thiết và dữ liệu vào 2. Chạy DLV 3. Quản lý kết quả DLV Ta thiết lập dữ liệu vào bằng cách sử dụng đối tượng Program. Ta sử dụng một đối tượng DlvHandler để thiết lập các thông số cần thiết, chạy các đối tượng tiến trình DLV và quản lý kết quả DLV. Lớp DlvHandler cung cấp các điều khiển đầy đủ trên môi trường DLV. Đối tượng DlvHandler gọi DLV, đưa dữ liệu vào và nhận kết quả ra. Ngay khi DLV đưa ra một kết quả, đối tượng DlvHandler phân tích nó và tạo ra một đối tượng Model. Đối tượng DlvHandler lưu trữ mỗi đối tượng Model trong một tập. Ta có thể quả lý tập các đối tượng Model bằng các phương thức thích hợp. Lớp DlvHandler thực hiện một giao diện tương ứng với giao diện java.util.Enumeration để truy nhập đến các đối tượng Model. Hơn thế nữa, mỗi đối tượng DlvHandler có một đối tượng OutputDescriptor (xem trong hình 3.1). Lớp OutputDescriptor mô tả cách 75 phân tích kết quả ra DLV. Đặc biệt là OutputDescriptor mô tả cách xây dựng các đối tượng Predicate được thêm vào với các đối tượng Model. Gói DLV cung cấp ba kiểu của đối tượng Predicate (xem trong hình 3.1): - lớp Predicate - lớp FilePredicate - lớp JDBCPredicate Lớp đầu tiên lưu trữ dữ liệu trong bộ nhớ chính; lớp thứ hai lưu trữ dữ liệu trong một file văn bản (theo định dạng datalog); lớp cuối cùng lưu trữ dữ liệu trong một bảng cơ sở dữ liệu quan hệ. Bằng cách sử dụng lớp OutputDescriptor, ta sẽ định rõ lớp vị từ được sử dụng cho mỗi vị từ nền. Với cách này, ta có thể chọ một thiết bị lưu trữ (bộ nhớ chính, đĩa cứng, v.v...) cho mỗi vị từ nền. Như đã nói ở trên, DLV có thể sử dụng rất nhiều thời gian để tính toán các tập trả lời bởi vì các chương trình logic phân biệt có thể là những bài toán rất khó. Nhưng khi một mô hình mới được tính xong, DLV sẽ đưa ra ngay lập tức. Để có thể nắm giữ được đặc tính này, ta có ba trạng thái cho chương trình Java làm việc: đồng bộ (synchronous), đồng bộ mô hình (model synchronous) và không đồng bộ (asynchronous). Nếu ta chạy DLV trong chế độ đồng bộ, luồng Java đang gọi DLV sẽ bị khóa cho đến khi DLV kết thúc việc tính toán. Luồng Java đang gọi DLV chỉ được truy nhập vào kết quả DLV khi việc thực hiện DLV kết thúc. Nếu ta chạy DLV trong chế độ đồng bộ mô hình hoặc trong chế độ không đồng bộ, luồng Java gọi DLV có thể truy nhập đến các mô hình vừa được tính xong. Gói DLV cung cấp một phương thức kiểm tra liệu đã có một mô hình mới hay chưa. Nếu ta chạy DLV trong chế độ đồng bộ mô hình, phương thức này sẽ khóa luồng Java gọi DLV cho đến khi có một mô hình mới hoặc DLV 76 kết thúc. Nếu ta chạy DLV trong chế độ không đồng bộ, phương thức này sẽ không bao giờ khóa luồng Java gọi DLV. 77 Chương 4 CÁC BÀI TOÁN MINH HỌA Một phương pháp nổi tiếng cho cách giải các bài toán logic là phương pháp sinh lời giải và kiểm tra, trong đó các lời giải có thể của bài toán được tạo ra và những lời giải không tương ứng sẽ bị loại bỏ khi kiểm tra. Cũng tương tự như cách thông thường khi chỉ ra một bài toán thuộc lớp NP, sau khi lựa chọn ngẫu nhiên, việc kiểm tra sẽ được thực hiện trong thời gian đa trị. Phần sinh lời giải trong cách biểu diễn của bài toán có được nhờ việc liệt kê các khả năng có thể và phần kiểm tra có được nhờ đưa ra các ràng buộc để loại bỏ các khả năng vi phạm điều kiện của bài toán. Do đó tập trả lời của chương trình kết quả sẽ phù hợp với bài toán đưa ra. Ta còn gọi cách thực hiện theo phương pháp sinh lời giải và kiểm tra là liệt kê và loại bỏ. Việc giải quyết các bài toán theo cách biểu diễn này phụ thuộc vào cách liệt kê các khả năng có thể, tức là những biến nào được sử dụng và các giá trị mà biến nhận được. Thông thường các tri thức ẩn và hiện về miền này được sử dụng để làm giảm kích thước của không gian các khả năng có thể (hoặc không gian trạng thái). Và có thể thêm một số điều kiện kiểm tra bên trong phần sinh lời giải. Một ví dụ cho cách này là bài toán N quân hậu. Trong cách biểu diễn các ràng buộc, cần phải lựa chọn cẩn thận với cách biểu diễn dưới dạng là thực tế hay là luật. Ví dụ, xem xét chương trình Π : . . . . p a q b a not b b not a ← ← ← ← 78 Giả thiết ràng buộc của chương trình: “p phải nhận giá trị đúng”. Do đó ta có thể loại bỏ các khả năng để p là không đúng. Nếu ta biểu diễn ràng buộc này dưới dạng thực tế trong chương trình Π : p← . thì kết quả chương trình có hai tập trả lời {a, p} và {b, q, p} là tập không mong muốn. Cái ta cần là loại bỏ b, q ra khỏi không gian trạng thái và tập trả lời là {a, p}. Vậy cách đúng đắn để biểu diễn ràng buộc này trong chương trình Π là: ←not p. Ta sẽ xét 2 bài toán N quân hậu và Cây bao trùm nhỏ nhất để minh họa cho cách suy diễn và tìm kiếm lời giải cho bài toán logic. Hai bài toán N quân hậu và Cây khung nhỏ nhất sẽ minh họa cách biểu diễn tri thức trong lập trình logic bằng DLV. Với mỗi bài toán, có nhiều cách để biểu diễn tri thức, phần này sẽ phân tích tiết từng cách biểu diễn và cài đặt cụ thể một cách biểu diễn trong môi trường lập trình DLV. 4.1 Bài toán N quân hậu 4.1.1 Phân tích bài toán Với một bảng kích thước n n× , ta cần phải đặt n quân hậu sao cho không quân hậu nào có thể ăn lẫn nhau. Có nghĩa là chỉ có chính xác một quân hậu trên một hàng và một cột, không có hai quân hậu nào ở cùng trên một đường chéo. Theo cách tiếp cận liệt kê và loại bỏ, đầu tiên ta cần phải liệt kê các cách đặt n quân hậu trên bảng n n× và sau đó loại bỏ các trường hợp các quân hậu có thể ăn lẫn nhau. Dưới đây sẽ xem xét một số cách biểu diễn phương pháp tiếp cận này. Sự khác nhau giữa các cách này nằm trong cả phần liệt kê và phần loại bỏ. Trong một số phần liệt kê, bản thân nó chứa cách liệt kê kém hơn và kèm theo loại bỏ, trong khi đó, trong phần loại bỏ, điều kiện của nó lại chứa cả liệt kê. (1) Đặt từng quân hậu vào trong các ô vuông có thể: Ta sẽ đặt tên cho các quân hậu từ 1 đến n và sử dụng vị từ at(I, X, Y) để biểu diễn quân hậu I ở 79 vị trí (X, Y). Phần liệt kê bao gồm phép liệt kê kém hơn trong không gian có thể được định nghĩa là với mỗi một ô sẽ là có quân hậu hoặc không và chứa phép loại bỏ sao cho mỗi quân hậu ở một ô và không có hai quân hậu ở cùng một vị trí. Cách biểu diễn bài toán được viết như sau: (a) Khai báo: Ta có miền ứng dụng như sau: ( ) ( ) ( ) ( ) ( ) ( ) 1 . ... . 1 . ... . 1 . ... . queen queen n row row n col col n ← ← ← ← ← ← (b) Liệt kê: Các luật liệt kê tạo ra các không gian có thể sao cho n quân hậu khác nhau được đặt trong bảng n n× ở những vị trí khác nhau. Các luật đó như sau: i. Với mỗi vị trí (X, Y) và mỗi quân hậu I, I có thể đặt ở vị trí (X, Y) hoặc không. ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) , , , , , _ , , . _ , , , , , , , . at I X Y queen I row X col Y not not at I X Y not at I X Y queen I row X col Y not at I X Y ← ← ii. Với mỗi quân hậu I, nó được đặt ở nhiều nhất 1 vị trí. ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) , , , , , , , , , , , . , , , , , , , , , , , . queen I row X col Y row U col Z at I X Y at I U Z Y Z queen I row X col Y row Z col V at I X Y at I Z V X Z ← ≠ ← ≠ iii. Với mỗi quân hậu I, nó được đặt ở ít nhất 1 vị trí. ( ) ( ) ( ) ( ) ( ) ( ) ( ) , , , , , . , . placed I queen I row X col Y at I X Y queen I not placed I ← ← iv. Không có hai quân hậu nào đặt ở cùng một vị trí. ( ) ( ) ( ) ( ) ( ) ( ), , , , , , , , , , .queen I row X col Y queen J at I X Y at J X Y I J← ≠ (c) Loại trừ i. Không có hai quân hậu khác nhau trên cùng một hàng 80 ii. Không có hai quân hậu khác nhau trên cùng một cột iii. Không có hai quân hậu khác nhau trên cùng một đường chéo Chú ý rằng (1)(b)(iv) là một dạng con của cả hai luật (1)(c)(i) và (1)(c)(ii). Nói cách khác, khi đã có (1)(c)(i) và (1)(c)(ii), ta không cần đến luật (1)(b)(iv) nữa. Luật này được nhắc đến ở đây khi ta muốn liệt kê các trường hợp có thể. (2) Đặt từng quân hậu vào các vị trí nhất định: Ta biểu diễn một cách trình bày trong khi liệt kê các trạng thái có thể, vấn đề quan tâm ở đây, các quân hậu cần được đặt trong những vị trí nhất định. (a) Khai báo: giống như (1)(a) (b) Liệt kê: i. Kết nối các hiệu quả của (1)(b)(i) – (iii) bằng các luật sau, trong đó đảm bảo rằng mỗi quân hậu được đặt ở một vị trí duy nhất. Hai luật đầu tiên định nghĩa ( )_ , ,other at I X Y có nghĩa quân hậu I được đặt ở vị trí khác với ( ),X Y . Luật thứ ba đảm bảo điều kiện rằng nếu quân hậu I không được đặt ở vị trí khác với ( ),X Y thì nó phải được đặt ở ( ),X Y . ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) _ , , , , , , , , , , . _ , , , , , , , , , , . , , , , , _ , , . other at I X Y queen I row X col Y row U col Z at I U Z Y Z other at I X Y queen I row X col Y row Z col V at I Z V X Z at I X Y queen I row X col Y not other at I X Y ← ≠ ← ≠ ← ii. Luật sau giống như (1)(b)(iv), nó đảm bảo hai quân hậu được đặt ở những vị trí khác nhau. ( ) ( ) ( ) ( ) ( ) ( ), , , , , , , , , , .queen I row X col Y queen J at I X Y at J X Y I J← ≠ (c) Loại bỏ: giống như (1)(c) 81 (3) Đặt từng quân hậu vào các vị trí nhất định sao cho chúng không thể ăn lẫn nhau theo hàng dọc hoặc hàng ngang. Với cách trình bày này, ta đặt hai ràng buộc liệt kê vào phần liệt kê. Do đó, mỗi khi liệt kê trạng thái có thể, ta đảm bảo rằng không có hai quân hậu nào ở cùng một hàng hoặc một cột. (a) Khai báo: giống (1)(a) (b) Liệt kê: i. Giống như (2)(b)(i): ii. Hai ràng buộc đầu trong (2)(c) giống như (1)(c)(i) và (1)(c)(ii) được thay thế như sau và ta sẽ không sử dụng (2)(b)(ii) nữa, vì nó đã được kết hợp trong các luật sau: ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) _ , , , , , , , , , , . _ , , , , , , , , , , . other at I X Y queen I row X col Y col V queen J at J X V I J other at I X Y queen I row X col Y row U queen J at J U Y I J ← ≠ ← ≠ (c) Loại bỏ: ta cần một luật loại bỏ giống như luật (1)(c)(iii) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) , , , , , , , , , , , , , . row X col Y row U col V queen I queen J at I X Y at J U V I J abs X U abs Y V ← ≠ − = − (4) Đặt các quân hậu với các trạng thái có thể trên bàn cờ: với ba cách trình bày trên, ta gán tên cho các quân hậu và lần lượt đặt chúng vào các vị trí. Ta có thể nhận được hiệu quả tính toán và cách lập trình ngắn gọn hơn bằng cách bỏ qua việc gán tên cho các quân hậu. Dưới đây, ta có thể xác định cách biểu diễn (1) bằng cách không phân biệt các quân hậu. Ta sẽ sử dụng vị từ ( ),in X Y có ý nghĩa là một quân hậu được đặt ở vị trí ( ),X Y . (a) Khai báo: ( ) ( ) ( ) ( ) 1 . ... . 1 . ... . row row n col col n ← ← ← ← 82 (b) Liệt kê: bao gồm hai phần. Vì ta không cần phân biệt các quân hậu, nên sẽ không cần sử dụng đến (1)(b)(ii) – (iv). i. Xác định mỗi ô có quân hậu hay không có. ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) _ , , , , . , , , _ , . not in X Y row X col Y not in X Y in X Y row X col Y not not in X Y ← ← ii. Để chắc chắn đã đặt tất cả n quân hậu, thay vì phải đếm, ta sử dụng tri thức các quân hậu không ăn lẫn nhau, chúng phải ở khác hàng nhau, và khi đó, mỗi quân hậu ở một hàng, biểu diễn bằng luật sau: ( ) ( ) ( ) ( ) ( ) ( ) _ , , , . , _ . has queen X row X col Y in X Y row X not has queen X ← ← (c) Loại bỏ: sử dụng phiên bản (1)(c). i. Hai quân hậu không thể ở trên cùng một cột. ( ) ( ) ( ) ( ) ( ), , , , , , , .row X col Y row XX X XX in X Y in XX Y← ≠ ii. Hai quân hậu không thể ở trên cùng một hàng. ( ) ( ) ( ) ( ) ( ), , , , , , , .row X col Y col YY Y YY in X Y in X YY← ≠ iii. Hai quân hậu không thể ở trên cùng một đường chéo. ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) , , , , , , , , , , . row X col Y row XX col YY X XX Y YY in X Y in XX YY abs X XX abs Y YY ← ≠ ≠ − = − 4.1.2 Cài đặt Bài toán N quân hậu được biểu diễn bằng chương trình logic tổng quát trong môi trường lập trình DLV. Chương trình được lưu trong file có tên NQueens4.dl và được cài đặt theo thuật toán (4) trình bày trong phần 4.1.1 ở trên. Chương trình biểu diễn bài toán như sau: row(X) :- #int(X), X > 0. 83 col(X) :- #int(X), X > 0. out(X, Y) :- row(X), col(Y), not in(X, Y). in(X, Y) :- row(X), col(Y), not out(X, Y). has_queen(X) :- row(X), col(Y), in(X, Y). :- row(X), not has_queen(X). :- Y YY, in(X, Y), in(X, YY). :- X XX, in(X, Y), in(XX, Y). :- in(X1, Y1), in(X2, Y2), X2=X1+N, Y2 = Y1 + N, N > 0. :- in(X1, Y1), in(X2, Y2), X2=X1+N, Y1 = Y2 + N, N > 0. Trong đó, #int(X) là các hàm của ngôn ngữ lập trình Datalog phân biệt, cho biết X nhận giá trị trong khoảng từ 0 đến N, với N được nhận từ dòng lệnh. Dòng lệnh để chạy chương trình trong DLV như sau: $ DLV -silent –N=4 NQueens4.dl Kết quả nhận được như sau: Trường hợp N = 4, ta có hai tập trả lời: Hình 4-1 Hai tập trả lời của NQueens với N = 4 Trường hợp N = 8, ta có 92 tập trả lời, hình dưới đây chỉ liệt kê một số lời giải của bài toán. 84 Hình 4-2 Các tập trả lời của NQueens với N = 8 4.2 Bài toán Cây khung nhỏ nhất 4.2.1 Mô tả bài toán Trong thiết kế mạch điện, cần thiết phải có một số các phích cắm cho các thiết bị điện tử tương ứng. Để kết nối một tập n phích cắm, ta có thể sử dụng n-1 dây điện, mỗi dây nối hai phích cắm với nhau. Có nhiều cách nối các phích điện với nhau, và cách tối ưu nhất là cách sử dụng ít dây điện nhất. Ta có thể mô hình hóa bài toán này bằng một đồ thị liên thông vô hướng G=(V,E), trong đó V là tập các phích điện, E là tập các khả năng có thể kết nối giữa các phích điện với nhau, và mỗi cạnh (u,v) thuộc E, ta có một trọng số w(u,v) là lượng dây cần thiết để nối u và v với nhau. Yêu cầu của bài toán là tìm T là một tập con của E, nối tất cả các đỉnh và có tổng trọng số các 85 cạnh trong T là nhỏ nhất. Do T là không chứa chu trình và kết nối tất cả các đỉnh với nhau, nên T phải có dạng cây, và được gọi là cây bao trùm của đồ thị G. Bài toán cần tìm T được gọi là bài toán cây bao trùm nhỏ nhất. Hình 4.3 đưa ra một ví dụ về đồ thị liên thông vô hướng và cây bao trùm nhỏ nhất của nó. Hình 4-3 Cây bao trùm nhỏ nhất của một đồ thị vô hướng. Trên mỗi cạnh của đồ thị đều có trọng số, và các cạnh thuộc cây bao trùm nhỏ nhất được tô đậm. Tổng trọng số của cây là 37. Cây bao trùm nhỏ nhất không phải là tồn tại duy nhất, nếu ta thay thế cạnh (b,c) bằng cạnh (a,h), ta nhận được một cây bao trùm nhỏ nhất có cùng tổng trọng số là 37. 4.2.2 Phân tích và cài đặt a. Chương trình logic DLV Chia dữ liệu vào DLV thành nhiều nguồn khác nhau. Cơ sở dữ liệu mở rộng EDB của chương trình được lưu trong file MST.inp, mô tả một đồ thị liên thông vô hướng. Cơ sở dữ liệu cơ bản IDB của chương trình được lưu trong file MST.dl, là các tập luật và ràng buộc để mô tả bài toán. Khai báo: - quyết định một đỉnh là gốc của cây, đây cũng đồng thời là đỉnh xuất phát trong quá trình liệt kê các trường hợp có thể là cây bao trùm của đồ thị. - xác định (X,Y, C) là cạnh của đồ thị khi (X, Y, C) hoặc (Y, X, C) thuộc cơ sở dữ liệu mở rộng của chương trình, tức là thuộc file MST.inp. root(a). 86 is_edge(X, Y, C) :- edge(X, Y, C). is_edge(X, Y, C) :- edge(Y, X, C). Liệt kê: - Liệt kê tất cả các trường hợp là cây bao trùm của đồ thị, với mỗi trường hợp, xác định một cạnh của đồ thị có thuộc cây hay không thuộc cây bằng luật phân biệt sau: in_tree(X, Y, C) v out_tree(X, Y) :- is_edge(X, Y, C), reached(X). - Vì cần xây dựng một cây bao trùm của đồ thị cho trước, nên ta cần phải biểu diễn tính chất không chứa chu trình của cây bao trùm bằng ràng buộc sau: :- in_tree(X, Y, _), has_path(X, Y). has_path(X, Y) :- in_tree(X, Z, _), in_tree(Z, Y, _), X != Z, Z != Y. has_path(X, Y) :- in_tree(X, Z, _), have_path(Z, Y), X != Z, Z != Y. Tức là sẽ không thể tồn tại đồng thời một cạnh (X,Y) và một đường đi khác từ X đến Y. - Đảm bảo tất cả các đỉnh phải được duyệt tới trong mọi trường hợp của cây bao trùm: reached(X) :- root(X). reached(Y) :- 87 reached(X), in_tree(X, Y, C). :- node(X), not reached(X). Loại bỏ: Tối ưu hóa cây bao trùm bằng ràng buộc yếu để nhận được kết quả là cây bao trùm nhỏ nhất: :~ in_tree(X, Y, C). [C:1] Kết quả chạy của chương trình trong DLV: Hình 4-4 Tập trả lời là các cạnh thuộc cây bao trùm nhỏ nhất có tổng trọng số là 12 Và cuối cùng, ta có thể chạy thử chương trình này với các thông số về mức độ đánh giá và ưu tiên được truyền từ dòng Hình 4-5 Các cây bao trùm có trọng số nhỏ hơn hoặc bằng 13 b. Cài đặt trên Java Chạy chương trình DLV trên trong Java, ta phải thực hiện việc gọi DLV bằng mã nguồn Java như sau: Bước 1: Xây dựng đối tượng Program và thiết lập dữ liệu vào // build a Program object and setup input 88 Program pr=new Program(); // set input pr.addProgramFile(this.dlvFileName); pr.addProgramFile(this.inputFileName); Bước 2: Xây dựng đối tượng DlvHandler // build a DlvHandler object DlvHandler dlv=new DlvHandler(this.dlvExeFile); Bước 3: Tạo chương trình đầu vào và các thông số cần thiết. // set input program dlv.setProgram(pr); // set invocation parameters dlv.setNumberOfModels(1); //computes no more than 1 solutions dlv.setIncludeFacts(false); dlv.setFilter(new String[]{"in_tree"}); Bước 4: Chạy DLV // run DLV by using model synchronous method of invocation dlv.run(DlvHandler.MODEL_SYNCHRONOUS); Bước 5: Quản lý kết quả DLV bằng cách sử dụng các lớp Model, Predicate, Literal. // DLV output handling // for each model, wait until DLV find a new model while(dlv.hasMoreModels()) { Model m=dlv.nextModel(); // gets next model if(!m.isNoModel()) { // for each predicate in m while(m.hasMorePredicates()){ // gets next predicate Predicate p=m.nextPredicate(); System.out.println(p.toString()); } System.out.println("--- END Model"); } else System.out.println("I cannot find a model"); } 89 Phần mềm MST_DLV bao gồm bốn file: - Node.java : định kiểu cho một nút của đồ thị, bao gồm tên nút, tọa độ x và y của nút đó trên khung kết quả. - Edge.java : mô tả một cạnh của đồ thị, bao gồm có đỉnh đầu, đỉnh cuối và trọng số của cạnh này. - MST.java : thực hiện các bước gọi DLV trong Java và vẽ đồ thị và cây khung tương ứng. - MSTGUI.java : mô tả giao diện làm việc giữa người sử dụng và chương trình. Giao diện làm việc của MST như sau: Hình 4-6 Giao diện làm việc của chương trình MST Ta cần nhập các tên file tương ứng, đó là file dữ liệu vào chứa các sự kiện mô tả một đồ thị với các vị từ node và edge; file chương trình là file bao gồm các ràng buộc và các luật; và cuối cùng là file kết quả ra chứa kết quả vừa tính được với vị từ in_tree. Với file MST6.inp, ta có được kết quả cây khung nhỏ nhất có tổng trọng số là 16. 90 Hình 4-7 Danh sách cạnh thuộc cây khung nhỏ nhất với đồ thị 6 đỉnh chứa trong file MST6.inp Hình 4-8 Đồ thị và cây khung nhỏ nhất của MST6.inp Với file MST7.inp, ta nhận được kết quả cây khung nhỏ nhất có tổng trọng số là 130. Hình 4-9 Danh sách cạnh thuộc cây khung nhỏ nhất với đồ thị 7 đỉnh chứa trong file MST7.inp 91 Hình 4-10 Đồ thị và cây khung nhỏ nhất của MST7.inp Với file MST8.inp, Ta nhận được kết quả cây khung nhỏ nhất có tổng trọng số là 103. Hình 4-11 Danh sách cạnh thuộc cây khung nhỏ nhất với đồ thị 8 đỉnh chứa trong file MST8.inp 92 Hình 4-12 Đồ thị và cây khung nhỏ nhất của MST8.inp 93 KẾT LUẬN Bản luận văn đã trình bày các kết quả nghiên cứu về cách biểu diễn tri thức trong lập trình logic. Các chương trình logic khác nhau sẽ có những cách biểu diễn tri thức khác nhau. Một chương trình logic được coi là một đặc thù để xây dựng các lý thuyết có thể cho một thế giới quan và các luật trong chương trình là những ràng buộc mà các lý thuyết này cần phải thỏa mãn. Ngữ nghĩa của các chương trình logic khác nhau về cách định nghĩa tính thỏa mãn các luật. Chương trình logic tổng quát sử dụng ngữ nghĩa mô hình ổn định và các dạng mở rộng của nó. Với ngữ nghĩa này, các lý thuyết tương ứng là các tập nguyên tố nền, được gọi là các mô hình ổn định của một chương trình. Chương trình logic mở rộng xuất hiện thêm một cách biểu diễn phủ định, đó là phủ định hiện. Trong ngôn ngữ của chương trình mở rộng, ta có thể phân biệt một truy vấn với ý nghĩa “nó không thành công” với một truy vấn với ý nghĩa mạnh hơn “phủ định của nó thành công”. Ngữ nghĩa của một chương trình logic mở rộng là một tập các tập trả lời của chương trình, tập trả lời của một chương trình là một tập các phần tử được coi là đúng dựa vào sự suy diễn trong chương trình. Với các nghiên cứu về cách biểu diễn tri thức trong các chương trình logic, bản luận văn đã trình bày một môi trường lập trình logic hiệu quả, DLV (datalog với phép hoặc) là một hệ thống cơ sở dữ liệu tường thuật khá mạnh. Nó được tạo ra trên cơ sở của ngôn ngữ lập trình tường thuật datalog, thích hợp với các loại suy diễn không đơn điệu, bao gồm cả chuẩn đoán và lập kế hoạch. Và ngoài ra, DLV còn được nhúng vào trong mã nguồn hướng đối 94 tượng Java thông qua gói DLV. Bằng cách sử dụng một cách thứ tự thích hợp cho các lớp Java, gói DLV cho phép kết nối mã nguồn Java với các chương trình logic phân biệt. Cuối cùng, bản luận văn đã đưa ra hai bài toán minh họa: bài toán N quân hậu và bài toán Cây khung nhỏ nhất. Các bài toán này được cài đặt trong môi trường lập trình logic DLV và chương trình mô tả bài toán Cây khung nhỏ nhất đã được nhúng vào mã nguồn Java và được chạy dưới dạng một chương trình hướng đối tượng Java. Bản luận văn đã nghiên cứu các phương pháp biểu diễn tri thức trong lập trình logic. Nếu có thêm điều kiện, bản luận văn có thể mở rộng nghiên cứu thêm độ phức tạp tính toán của chương trình, cải thiện tốc độ tính toán cũng phát triển các phương pháp biểu diễn tri thức khác đối với các bài toán NP khó. 95 TÀI LIỆU THAM KHẢO Tiếng Việt [1] Nguyễn Xuân Thái (1999), “Lập trình logic và nguyên lý giải”, Luận văn Thạc sỹ khoa học, trường Đại học Khoa học Tự nhiên, Đại học Quốc Gia Hà Nội. Tiếng Anh [2] Chitta Baral (2004), “Knowledge Representation, Reasoning and Declarative Problem Solving”, Arizona State University, U.S.A. [3] Chitta Baral, Michael Gelfond (1994), “Logic Programming and Knowledge Representation”, Computer Science Department, University of Texas at El Paso, El Paso, Texaz, U.S.A. [4] Michael Gelfond (1994), “The Stable Model Semantics for Logic Programming”, University of Texas at El Paso, El Paso, Texaz, U.S.A. [5] Steffen Hölldobler (2004), “Computational Logic - Working material”, Artificial Intelligence Institute, Technische Universität Dresden, Germany. [6] Matthias Knorr (2006), “A Comparative Study of Disjunctive Well- founded Sematics”, Master thesis in Computational Logic, Universidade Nova de Lisboa, Portugal. [7] Vladimir Lifschitz, “Foundations of Logic Programming”, Department of Computer Sciences, University of Texas, U.S.A. 96 [8] Vivek Nigam (2006), “Dynamic Logic Programming and 3APL”, Master thesis in Computational Logic, Universidade Nova de Lisboa, Portugal. [9] Luís Moniz Pereira, José Júlio Alferes (1996), “Reasoning with Logic Programming”, Universidade Nova de Lisboa, Portugal. [10] Francesco Ricca (2004), “Java Wrapper for DLV”, Department of Mathematics, University of Calabria, Italy. [11] Wolfgang Faber, Gerald Pfeifer (since 1996), DLV homepage, 97 PHỤ LỤC Bài toán N quân hậu trên DLV: row(X) :- #int(X), X > 0. col(X) :- #int(X), X > 0. out(X, Y) :- row(X), col(Y), not in(X, Y). in(X, Y) :- row(X), col(Y), not out(X, Y). has_queen(X) :- row(X), col(Y), in(X, Y). :- row(X), not has_queen(X). :- Y YY, in(X, Y), in(X, YY). :- X XX, in(X, Y), in(XX, Y). :- in(X1, Y1), in(X2, Y2), X2=X1+N, Y2 = Y1 + N, N > 0. :- in(X1, Y1), in(X2, Y2), X2=X1+N, Y1 = Y2 + N, N > 0. 98 Bài toán Cây khung nhỏ nhất trong DLV: root(a). node(a). node(b). node(c). node(d). node(e). edge(a, b, 4). edge(a, c, 3). edge(c, b, 2). edge(c, d, 3). edge(b, e, 4). edge(d, e, 5). in_tree(X, Y, C) v out_tree(X, Y) :- edge(X, Y, C), reached(X). :- root(X), in_tree(_, X, C). :- in_tree(X, Y, C), in_tree(Z, Y, C), X != Z. reached(X) :- root(X). reached(Y) :- reached(X), in_tree(X, Y, C). :- node(X), not reached(X). :~ in_tree(X, Y, C). [C:1] PHỤ LỤC Bài toán N quân hậu trên DLV: row(X) :- #int(X), X > 0. col(X) :- #int(X), X > 0. out(X, Y) :- row(X), col(Y), not in(X, Y). in(X, Y) :- row(X), col(Y), not out(X, Y). has_queen(X) :- row(X), col(Y), in(X, Y). :- row(X), not has_queen(X). :- Y YY, in(X, Y), in(X, YY). :- X XX, in(X, Y), in(XX, Y). :- in(X1, Y1), in(X2, Y2), X2=X1+N, Y2 = Y1 + N, N > 0. :- in(X1, Y1), in(X2, Y2), X2=X1+N, Y1 = Y2 + N, N > 0. Bài toán Cây khung nhỏ nhất trong DLV: root(a). node(a). node(b). node(c). node(d). node(e). edge(a, b, 4). edge(a, c, 3). edge(c, b, 2). edge(c, d, 3). edge(b, e, 4). edge(d, e, 5). in_tree(X, Y, C) v out_tree(X, Y) :- edge(X, Y, C), reached(X). :- root(X), in_tree(_, X, C). :- in_tree(X, Y, C), in_tree(Z, Y, C), X != Z. reached(X) :- root(X). reached(Y) :- reached(X), in_tree(X, Y, C). :- node(X), not reached(X). :~ in_tree(X, Y, C). [C:1] Chương trình MSTGUI.java package com.studyMST; import java.awt.Container; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.WindowEvent; import java.io.File; import java.io.IOException; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; import org.jgraph.JGraph; import org.jgraph.graph.DefaultGraphModel; import org.jgraph.graph.GraphModel; import DLV.DLVInvocationException; public class MSTGUI extends JFrame { public Container contentPane; private JTextField textNameInputFile = new JTextField("MST.inp"); private JButton buttonOpenInputFile = new JButton("Open Input File"); private JTextField textNameProgramFile = new JTextField("MST.dl"); private JButton buttonOpenProgramFile = new JButton("Open Program File"); private JTextField textNameOutputFile = new JTextField("MST.out"); private JButton buttonOpenOutputFile = new JButton("Open Output File"); private JButton buttonPress = new JButton("Solve!"); private JFileChooser fileChooserInput = new JFileChooser(); private JPanel inputPanel = new JPanel(); private static final long serialVersionUID = 1L; public static void main(String[] args) throws Exception, DLVInvocationException { MSTGUI studyGUIObject = new MSTGUI(); studyGUIObject.setSize(800, 175); studyGUIObject.contentPane = studyGUIObject.getContentPane(); studyGUIObject.contentPane.setLayout(new BoxLayout(studyGUIObject.contentPane, BoxLayout.Y_AXIS)); studyGUIObject.initPanels(); studyGUIObject.initContainer(); studyGUIObject.setVisible(true); studyGUIObject.addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosing(WindowEvent winEvt) { System.exit(0); } }); } public void initContainer() { this.contentPane = this.getContentPane(); this.contentPane.add(this.inputPanel); } public void initPanels() throws IOException, DLVInvocationException { //set the layout to be 4 rows and 3 columns GridLayout inputPanelLayout = new GridLayout(4,3); this.inputPanel.setLayout(inputPanelLayout); //first row this.inputPanel.add(new JLabel("Enter MST Input File")); textNameInputFile.setColumns(25); this.inputPanel.add(textNameInputFile); this.buttonOpenInputFile.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { buttonOpenInputFileActionPerformed(evt); } }); this.inputPanel.add(buttonOpenInputFile); //second row this.inputPanel.add(new JLabel("Enter MST Program File")); textNameProgramFile.setColumns(25); this.inputPanel.add(textNameProgramFile); this.buttonOpenProgramFile.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { buttonOpenProgramFileActionPerformed(evt); } }); this.inputPanel.add(buttonOpenProgramFile); //third row this.inputPanel.add(new JLabel("Enter MST Output File")); textNameOutputFile.setColumns(25); this.inputPanel.add(textNameOutputFile); this.buttonOpenOutputFile.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { buttonOpenOutputFileActionPerformed(evt); } }); this.inputPanel.add(buttonOpenOutputFile); //fourth row this.buttonPress.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { try { buttonPressActionPerformed(evt); } catch (IOException e) { e.printStackTrace(); } catch (DLVInvocationException e) { e.printStackTrace(); } } }); this.inputPanel.add(new JLabel("")); this.inputPanel.add(new JLabel("")); this.inputPanel.add(buttonPress); } private void buttonOpenInputFileActionPerformed(ActionEvent evt) { int returnVal = fileChooserInput.showOpenDialog(MSTGUI.this); if (returnVal == JFileChooser.APPROVE_OPTION) { File file = fileChooserInput.getSelectedFile(); //This is where a real application would open the file. textNameInputFile.setText(file.getAbsolutePath()); } } private void buttonOpenProgramFileActionPerformed(ActionEvent evt) { int returnVal = fileChooserInput.showOpenDialog(MSTGUI.this); if (returnVal == JFileChooser.APPROVE_OPTION) { File file = fileChooserInput.getSelectedFile(); //This is where a real application would open the file. textNameProgramFile.setText(file.getAbsolutePath()); } } private void buttonOpenOutputFileActionPerformed(ActionEvent evt) { int returnVal = fileChooserInput.showOpenDialog(MSTGUI.this); if (returnVal == JFileChooser.APPROVE_OPTION) { File file = fileChooserInput.getSelectedFile(); //This is where a real application would open the file. textNameOutputFile.setText(file.getAbsolutePath()); } } private void buttonPressActionPerformed(java.awt.event.ActionEvent evt)throws IOException, DLVInvocationException { String inputFileName = textNameInputFile.getText(); String outputFileName = textNameOutputFile.getText(); String dlvFileName = textNameProgramFile.getText(); MST mstObject = new MST(); GraphModel model = new DefaultGraphModel(); mstObject.dlvFileName = dlvFileName; mstObject.inputFileName = inputFileName; mstObject.outputFileName = outputFileName; mstObject.graph = new JGraph(model); mstObject.drawGraphFromInput(); mstObject.calculateMST(); mstObject.drawMSTFromOutput(); //create a new frame for the result JFrame resultFrame = new JFrame(); //add the result to the new frame just created resultFrame.getContentPane().add(new JScrollPane(mstObject.graph)); resultFrame.setSize(800, 600); resultFrame.setVisible(true); resultFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); } } Chương trình MST.java: package com.studyMST; import java.awt.Color; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import java.util.Random; import java.util.StringTokenizer; import javax.swing.BorderFactory; import org.jgraph.JGraph; import org.jgraph.graph.DefaultEdge; import org.jgraph.graph.DefaultGraphCell; import org.jgraph.graph.GraphConstants; import DLV.DLVException; import DLV.DLVExceptionUncheked; import DLV.DLVInvocationException; import DLV.DlvHandler; import DLV.Model; import DLV.Predicate; import DLV.Program; public class MST { private static final int NODE = 1; private static final int EDGE = 2; private HashMap mapOfNodes = new HashMap(); private ArrayList listOfEdges = new ArrayList(); public ArrayList listOfInTree = new ArrayList(); private HashMap mapOfEdges = new HashMap(); public String dlvFileName = "MST.dl"; public String inputFileName = "MST.inp"; public String outputFileName = "MST.out"; private String dlvExeFile = "dl.exe"; public JGraph graph; /** * @param args * @throws IOException */ public void calculateMST () throws IOException, DLVInvocationException{ File outputFile = new File(this.outputFileName); FileWriter out = new FileWriter(outputFile); // build a Program object and setup input Program pr=new Program(); // set input pr.addProgramFile(this.dlvFileName); pr.addProgramFile(this.inputFileName); // build a DlvHandler object DlvHandler dlv=new DlvHandler(this.dlvExeFile); // set input program dlv.setProgram(pr); // set invocation parameters dlv.setNumberOfModels(1); // computes no more than 1 solutions dlv.setIncludeFacts(false); dlv.setFilter(new String[]{"in_tree"}); try { // run DLV by using model synchronous method of invocation dlv.run(DlvHandler.MODEL_SYNCHRONOUS); // DLV output handling while(dlv.hasMoreModels()) // for each model, wait until DLV find a new model { Model m=dlv.nextModel(); // gets next model if(!m.isNoModel()) { while(m.hasMorePredicates()) // for each predicate in m { Predicate p=m.nextPredicate(); // gets next predicate System.out.println(p.toString()); // print out p out.write(p.toString()); } System.out.println("--- END Model"); } else System.out.println("I cannot find a model"); } } catch(DLVException d) { d.printStackTrace(); } catch(DLVExceptionUncheked du) { du.printStackTrace(); } finally { System.err.println(dlv.getWarnings()); // print out errors } out.close(); } public void drawGraphFromInput() throws IOException { BufferedReader d = new BufferedReader(new InputStreamReader(new FileInputStream(new File(this.inputFileName)))); String line = ""; while((line = d.readLine()) != null) { String verticeOrEdge = line.substring(line.indexOf("(") + 1, line.indexOf(")")); //System.out.println(verticeOrEdge); int type = this.getType(verticeOrEdge); if(type == MST.NODE) { Node theNode = new Node(); theNode.label = verticeOrEdge; mapOfNodes.put(theNode.label, theNode); } else if(type == MST.EDGE) { StringTokenizer st = new StringTokenizer(verticeOrEdge, ","); String labelNode1 = st.nextToken().trim(); String labelNode2 = st.nextToken().trim(); int edgeWeight = Integer.parseInt(st.nextToken().trim()); Node node1 = (Node) mapOfNodes.get(labelNode1); Node node2 = (Node) mapOfNodes.get(labelNode2); Edge theEdge = new Edge(); theEdge.node1 = node1; theEdge.node2 = node2; theEdge.weight = edgeWeight; listOfEdges.add(theEdge); } else { } } System.out.println("no of nodes = " + mapOfNodes.size()); buildNodesCoordinate(); buildGraph(); } public void drawMSTFromOutput() throws IOException { BufferedReader d = new BufferedReader(new InputStreamReader(new FileInputStream(new File(this.outputFileName)))); String line = ""; while((line = d.readLine()) != null) { String edge = line.substring(line.indexOf("(") + 1, line.indexOf(")")); StringTokenizer st = new StringTokenizer(edge, ","); String labelNode1 = st.nextToken().trim(); String labelNode2 = st.nextToken().trim(); int edgeWeight = Integer.parseInt(st.nextToken().trim()); Node node1 = (Node) mapOfNodes.get(labelNode1); Node node2 = (Node) mapOfNodes.get(labelNode2); Edge theEdge = new Edge(); theEdge.node1 = node1; theEdge.node2 = node2; theEdge.weight = edgeWeight; listOfInTree.add(theEdge); } modifyGraph(); } public void modifyGraph() { for(int i=0; i<this.listOfInTree.size(); i++) { Edge theEdge = (Edge) this.listOfInTree.get(i); String node1Label = theEdge.node1.label; String node2Label = theEdge.node2.label; DefaultGraphCell modifiedEdge = (DefaultGraphCell) mapOfEdges.get(node1Label + "-" + node2Label); modifyEdgeColor(modifiedEdge); } } public int getType(String line) { if(line.indexOf(",") == -1 ) { return MST.NODE; } else { return MST.EDGE; } } public void buildNodesCoordinate() { int screenWidth = 1000; int screenHeight = 1000; int maxHorizonalCells = 10; int maxVerticalCells = 10; int cellWidth = screenWidth/maxHorizonalCells; int cellHeight = screenHeight/maxVerticalCells; boolean[][] board = new boolean[maxHorizonalCells][maxVerticalCells]; Object[] mapOfNodesLabel = this.mapOfNodes.keySet().toArray(); Random random = new Random(); for(int i=0; i< mapOfNodesLabel.length; i++) { boolean flag = false; while (flag == false) { int randomHorizontal = random.nextInt(maxHorizonalCells); int randomVertical = random.nextInt(maxVerticalCells); if(board[randomHorizontal][randomVertical] == false) { Node theNode = (Node) this.mapOfNodes.get(mapOfNodesLabel[i]); theNode.x = randomHorizontal * cellWidth; theNode.y = randomVertical * cellHeight; flag = true; //System.out.println(theNode); } } } } public DefaultGraphCell createVertex(String name, double x, double y, double w, double h, Color bg, boolean raised) { // Create vertex with the given name DefaultGraphCell cell = new DefaultGraphCell(name); // Set bounds GraphConstants.setBounds(cell.getAttributes(), new Rectangle2D.Double(x, y, w, h)); // Set fill color if (bg != null) { GraphConstants.setGradientColor(cell.getAttributes(), bg); GraphConstants.setOpaque(cell.getAttributes(), true); } // Set raised border if (raised) GraphConstants.setBorder(cell.getAttributes(), BorderFactory.createRaisedBevelBorder()); else // Set black border GraphConstants.setBorderColor(cell.getAttributes(), Color.black); // Add a Floating Port cell.addPort(); return cell; } public void modifyEdgeColor(DefaultGraphCell modifiedEdge ) { Map nested = new Hashtable(); GraphConstants.setLineColor(modifiedEdge.getAttributes(), Color.BLUE); nested.put(modifiedEdge, modifiedEdge.getAttributes()); this.graph.getGraphLayoutCache().edit(nested, null, null, null); } public DefaultEdge createEdge(DefaultGraphCell cell1, DefaultGraphCell cell2, int weight ) { // Create Edge DefaultEdge edge = new DefaultEdge(); // Fetch the ports from the new vertices, and connect them with the edge edge.setSource(cell1.getChildAt(0)); edge.setTarget(cell2.getChildAt(0)); Point2D[] point = {new Point2D.Double(GraphConstants.PERMILLE/2, 10)}; GraphConstants.setExtraLabelPositions(edge.getAttributes(), point); Object[] labels = {weight + ""}; GraphConstants.setExtraLabels(edge.getAttributes(), labels); GraphConstants.setLineColor(edge.getAttributes(), Color.red); return edge; } public void buildGraph()throws IOException { // Control-drag should clone selection this.graph.setCloneable(true); // Enable edit without final RETURN keystroke this.graph.setInvokesStopCellEditing(true); // When over a cell, jump to its default port (we only have one, anyway) this.graph.setJumpToDefaultPort(true); int mapOfNodesSize = this.mapOfNodes.size(); int listOfEdgesSize = this.listOfEdges.size(); // Insert all three cells in one call, so we need an array to store them DefaultGraphCell[] cells = new DefaultGraphCell[mapOfNodesSize + listOfEdgesSize]; Object[] mapOfNodesLabel = this.mapOfNodes.keySet().toArray(); HashMap mapOfCells = new HashMap(); for(int i=0; i<mapOfNodesLabel.length; i++) { Node theNode = (Node) this.mapOfNodes.get(mapOfNodesLabel[i]); cells[i] = createVertex(theNode.label, theNode.x, theNode.y, theNode.width, theNode.height, null, false); mapOfCells.put(theNode.label, cells[i]); } for(int i=0; i<listOfEdgesSize; i++) { Edge theEdge = (Edge) this.listOfEdges.get(i); String node1Label = theEdge.node1.label; String node2Label = theEdge.node2.label; cells[mapOfNodes.size() + i] = createEdge( (DefaultGraphCell) mapOfCells.get(node1Label), (DefaultGraphCell) mapOfCells.get(node2Label), theEdge.weight); mapOfEdges.put(node1Label + "-" + node2Label, cells[mapOfNodes.size() + i]); mapOfEdges.put(node2Label + "-" + node1Label, cells[mapOfNodes.size() + i]); } // Insert the cells via the cache, so they get selected graph.getGraphLayoutCache().insert(cells); } } Lớp Node.java package com.studyMST; public class Node { public String label; public int x; public int y; public final int width = 20; public final int height = 20; args */ public static void main(String[] args) { // TODO Auto-generated method stub } public String toString() { // TODO Auto-generated method stub return ("label="+label+ ",x="+x + ",y="+y); } } Lớp Edge.java package com.studyMST; public class Edge { public Node node1; public Node node2; public int weight; public static void main(String[] args) { // TODO Auto-generated method stub } }

Các file đính kèm theo tài liệu này:

  • pdf000000208228R.pdf
Tài liệu liên quan