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
114 trang |
Chia sẻ: maiphuongtl | Lượt xem: 1824 | Lượt tải: 0
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:
- 000000208228R.pdf