trí tuệ nhân tạo

Màu nền
Font chữ
Font size
Chiều cao dòng

TRƯỜNG ĐẠI HỌC ĐÀ LẠT

KHOA TOÁN –TIN 

Trương Chí Tín

GIÁO TRÌNH

NHẬP MÔN TRÍ TUỆ NHÂN TẠO

Đà Lạt, 04 - 2009

 LỜI MỞ ĐẦU

 Giáo trình “Nhập môn Trí tuệ nhân tạo”   được viết dành cho sinh

viên ngành Toán – Tin, Tin học và Công nghệ thông tin. 

Để đọc giáo trình này, sinh viên cần có kiến thức cơ bản về lôgic, cấu

trúc dữ liệu và thuật toán. Nội dung giáo trình này gồm 4 chương: 

 Chương 1: Khái niệm về trí tuệ nhân tạo

 Chương 2: Các phương pháp giải quyết vấn đề 

 Chương 3: Biểu diễn và xử lý tri thức 

 Chương 4: Lập trình lôgic 

 

 Chương 1 giới thiệu tóm tắt lịch sử hình thành và phát triển cũng như

các khái niệm chung nhất, các lĩnh vực nghiên cứu và ứng dụng chính của

trí tuệ nhân tạo. Chương 2 trình bày các phương pháp biểu diễn và giải

quyết vấn đề cơ bản: biểu diễn vấn đề trong không gian trạng thái bằng đồ

thị thông thường, đồ thị VÀ/HOẶC, các phương pháp xác định trực tiếp lời

giải, các phương pháp thử – sai (trong đó trình bày các phương pháp tìm

kiếm theo chiều rộng, chiều sâu, theo hướng cực tiểu giá thành trên cây và

đồ thị, thuật giải di truyền, phương pháp GPS, …) và các kỹ thuật heuristic.

Chương 3 đề cập đến các phương pháp biểu diễn tri thức bằng: lôgic, luật

sinh, mạng ngữ nghĩa, khung và các phương pháp xử lý tri thức bằng suy

diễn dựa trên lôgic tất định và bất định. Chương 4 giới thiệu kỹ thuật lập

trình lôgic thông qua ngôn ngữ lập tình Prolog.

Cuối mỗi chương có phần bài tập nhằm củng cố chắc hơn kiến thức

lý thuyết và rèn luyện kỹ năng thực hành cho học viên. Các phần được in

chữ nhỏ dành cho học viên đọc thêm. 

Chắc chắn tài liệu này không tránh khỏi sơ suất, tác giả rất mong

nhận được và chân thành biết ơn các ý kiến đóng góp quí báu của các bạn

đồng nghiệp và độc giả nhằm làm cho giáo trình hoàn chỉnh hơn trong lần

tái bản sau.

                   Đà lạt, 04 - 2009

          Tác giả 

 

MỤC LỤC

Lời mở đầu

                

CHƯƠNG  I.  KHÁI NIỆM VỀ TRÍ TUỆ NHÂN TẠO  

 I.1. Lược sử hình thành và phát triển     1     

 I.2. Những lĩnh vực nghiên cứu của trí tuệ nhân tạo (TTNT) 3

I.3. Những ứng dụng của TTNT      6

CHƯƠNG  II.  CÁC PHƯƠNG PHÁP GIẢI QUYẾT VẤN ĐỀ 

  II.1. Các phương pháp xác định trực tiếp lời giải   8

  II.1.1. Phương pháp giải chính xác    8

  II.1.2. Phương pháp giải gần đúng    8

  II.1.3. Phương pháp giải không tường minh, đệ qui  8

  II.1.4. Phương pháp qui hoạch động    11

 II.2. Các phương pháp thử – sai      13

II.2.1. Phương pháp vét cạn, nguyên lý mắt lưới, 

phương pháp sinh và thử, phương pháp nhánh cận  13

   a. Phương pháp vét cạn      13

   b. Nguyên lý mắt lưới      14

   c. Phương pháp sinh và thử     15

   d. Phương pháp nhánh cận     16

II.2.2. Phương pháp ngẫu nhiên     17

   a. Phương pháp Monte - Carlo    17

   b. Thuật giải di truyền GA     18

II.2.3. Nguyên lý mê cung     20

II.2.4. Các phương pháp biểu diễn và giải quyết vấn đề 

trong không gian trạng thái bằng cây và đồ thị 22

   a. Biểu diễn vấn đề trong không gian trạng thái   22

   b. Phương pháp tìm kiếm lời giải    27

     c. Các dạng đặc biệt thường gặp: tìm kiếm theo 

       chiều rộng, chiều sâu, sâu dần, cực tiểu AT

  28

II.2.5. Quy bài toán về bài toán con và các chiến lược 

tìm kiếm trên đồ thị VÀ / HOẶC    32

      a. Quy bài toán về bài toán con    32

      b. Biểu diễn bài toán dưới dạng đồ thị VÀ / HOẶC 33

      c. Các phương pháp tìm kiếm trên cây VÀ / HOẶC:                   tìm kiếm theo chiều rộng, chiều sâu, cực tiểu  34

 II.2.6. Phương pháp GPS      40 

II.3. Kỹ thuật Heuristic       42 

II.3.1. Các thuật giải tìm kiếm tối ưu trên cây và đồ thị

với tri thức heuristic     44

   a. Thuật giải AKT

       44

   b. Thuật giải A*       44

   c. Các ví dụ        45

II.3.2. Nguyên lý tham lam     48

II.3.3. Nguyên lý hướng đích, phương pháp leo núi  51

   II.3.4. Nguyên lý sắp thứ  tự, nguyên lý trùng khớp nhất  52

 Bài tập         55

CHƯƠNG  III. BIỂU DIỄN VÀ XỬ LÝ TRI  THỨC

III.1. Khái niệm về biểu diễn và xử lý tri thức   59

  III.1.1. Từ  dữ liệu đến tri thức     59

III.1.2. Một số đặc trưng của tri thức    60

III.1.3. Phân loại tri thức      60

III.1.4. Các phương pháp biểu diễn tri thức   60

III.1.5. Các phương pháp xử lý diễn tri thức   60

III.2. Một số phương pháp biểu diễn tri thức    61 

III.2.1. Biểu diễn tri thức nhờ lôgic    61

III.2.2. Biểu diễn tri thức nhờ luật sinh  

 63

III.2.3. Biểu diễn tri thức nhờ mạng ngữ nghĩa  64

III.2.4. Biểu diễn tri thức bằng Frame    64

III.3. Xử  lý tri thức tất định bằng phương pháp suy diễn lôgic  65 

  III.3.1. Các cơ chế lập luận với tri thức tất định    65

   III.3.2. Thuật toán Vương Hạo       65 

   III.3.3. Thuật toán Robinson      69

  III.3.4. Thuật toán suy diễn tiến     72

  III.3.5. Thuật toán suy diễn lùi      74

III.4. Xử  lý tri thức bất định bằng phương pháp suy diễn logic  78

  III.4.1. Các cơ chế lập luận với tri thức bất định và 

  không chính xác               78

  III.4.2. Phân bố khả xuất của khái luật và các phép toán 

  nối kết trên chúng      78

 Bài tập         79

 

CHƯƠNG  IV. LẬP TRÌNH LÔGIC 

IV.1. Giới thiệu ngôn ngữ lập trình lôgic Prolog   80

  IV.1.1. Mở đầu       80

  IV.1.2. Vị từ, sự kiện, qui tắc, mục tiêu trong Prolog 81

  IV.1.3. Cấu trúc chính của một chương trình trong Prolog 83

IV.2. Danh sách, đệ qui, lát cắt trong Prolog    87

IV.2.1. Danh sách       87

IV.2.2. Đệ qui, cơ chế quay lui và tìm nghiệm bội trong 

Prolog       87

IV.2.3. Lát cắt trong Prolog              89

IV.3. Các ví dụ                          92

  IV.3.1. Bài toán “Tháp Hà Nội”              92

  IV.3.2. Bài toán xử lý vi phân ký hiệu             93

  IV.3.3. Bài toán suy luận lôgic               94

IV.4. Phụ lục: Vài vị từ chuẩn trong Prolog             96

Bài tập                   105

Tài liệu tham khảo        110

   Chöông 1 – Khaùi nieäm veà Trí tueä nhaân taïo  1

Chương I

KHÁI NIỆM VỀ TRÍ TUỆ NHÂN TẠO

I.1. Lược sử hình thành và phát triển

* Trí tuệ nhân tạo (TTNT hay AI – Artificial Intelligence) là một trong

những ngành mới trong lĩnh vực công nghệ thông tin. Có nhiều quan điểm về

trí tuệ nhân tạo. 

-  Năm 1950, Alan Turing đã đưa ra các “trắc nghiệm thông minh” để

nhận biết máy tính có thông minh hay không. Tuy vậy, cũng theo ông

ta, tuy máy tính có thể thất bại trong các trắc nghiệm thông minh nhưng

nó vẫn có thể thông minh. 

-  Theo quan  điểm của Minsky, trí tuệ nhân tạo là một ngành khoa học

nhằm nghiên cứu, mô phỏng trên máy tính các hành vi và tư duy thông

minh tương tự như con người. Nó giúp máy tính có khả năng nhận thức,

suy luận và phản ứng. Có hai hướng tiếp cận trí tuệ nhân tạo: dùng máy

tính để bắt chước quá trình xử lý của con người và thiết kế những máy

tính thông minh độc lập với cách suy nghĩ của con người.

- Từ điển bách khoa toàn thư Webster thì định nghĩa: “Trí tuệ là khả năng: 

1.  Phản ứng một cách thích hợp với những tình huống mới thông qua

hiệu chỉnh hành vi một cách thích đáng; 

2.  Hiểu rõ những mối liên hệ qua lại giữa các sự kiện của thế giới bên

ngoài nhằm đưa ra những hành động phù hợp để đạt tới một mục

đích nào đó”.  

-  Theo những nhà tâm lý học nhận thức  thì quá trình hoạt động trí tuệ

của con người bao gồm 4 thao tác cơ bản:

1.  Xác định tập đích (goal) cần đạt tới;

2.  Thu thập các sự kiện (facts) và các luật suy diễn (inference rules) để

đạt tới tập đích đặt ra;

3.  Thu gọn (prunning) quá trình suy luận nhằm xác  định một cách

nhanh chóng tập các luật suy diễn có thể sử dụng được để đạt tới

một đích trung gian nào đó;

4.  Áp dụng các cơ chế suy diễn (tiến hoặc lùi) cụ thể (inference

mechanisms), dựa trên các thao tác thu gọn quá trình suy luận và

những sự kiện trung gian mới được tạo ra, để dẫn dắt từ những sự

kiện ban đầu đến những đích đã đặt ra.

* TTNT ra đời dựa trên các thành quả của các ngành tâm lý học nhận

thức, lôgic hình thức, … Từ trên 2000 năm trước, các nhà triết học và tâm lý   Chöông 1 – Khaùi nieäm veà Trí tueä nhaân taïo  2

học đã cố gắng tìm hiểu cách thức, cơ chế của quá trình nhớ, học tập, nhận thức

và suy lý. 

-  Vào đầu những năm 50 của thế kỷ XX, nhờ sự ra đời và cải tiến liên tục

về hiệu suất hoạt động của máy tính, đã xuất hiện xu hướng không chỉ

nghiên cứu trí tuệ về mặt lý thuyết mà còn kiểm nghiệm các kết quả lý

thuyết thông minh  đó trên máy tính. Trong thời gian  đầu mới hình

thành, nhiều công trình lý thuyết về TTNT vẫn chưa được kiểm nghiệm

và triển khai trên thực tế do chưa có ngôn ngữ  lập trình đặc trưng cho

TTNT, do hạn chế về kỹ thuật máy tính, giới hạn về bộ nhớ đặc biệt là

tốc  độ thực hiện và do vấn  đề bùng nổ tổ hợp nảy sinh trong những

thuật toán tìm kiếm lời giải cho các bài toán khó trong TTNT.

-  Dựa trên các thành quả về kỹ thuật phần cứng, cùng với sự xuất hiện

các ngôn ngữ lập trình  đặc thù cho TTNT,  chuyên xử lý ký

hiệu hình thức phục vụ cho lập trình lôgic như IPL.V, LISP (viết tắt của

LISt Processing, do Mc Cathy tại  đại học MIT  đề xuất năm 1960),

PLANNER, PROLOG (viết tắt của PROgramming in LOGic, do Alain

Colmerauer và nhóm công sự của ông tại đại học Marseilles xây dựng

năm 1972),  nhiều giả thuyết hay kết quả thú vị về lý thuyết trong TTNT

có điều kiện được kiểm nghiệm và trở thành các sản phẩm tin học cụ thể

trên thị trường mang tính thông minh,  hoạt động như các nhóm chuyên

gia nhiều kinh nghiệm trong từng lĩnh vực hẹp nào đó như y học, địa

chất, dạy – học, chơi cờ,... Chẳng hạn các sản phẩm, chương trình: dẫn

xuất kết luận trong hệ hình thức, chứng minh các  định lý hình học

phẳng, tính tích phân bất định, giải phương trình đại số sơ cấp, chơi cờ

(Samuel), phân tích và chữa bệnh tâm lý (ELIZA), chuyên gia về y khoa

(MYCIN  ở  đại học Stanford), phân tích và tổng hợp tiếng nói,  điều

khiển Robot theo  đồ án “Mắt - tay”, thăm dò khoáng sản

(PROSPECTOR)... 

Khi sử dụng những sản phẩm chuyên dụng thông minh này, đặc biệt

là lần đầu tiên, ta không khỏi ngạc nhiên về tính “thông minh” đến mức

đôi khi ta có cảm giác chúng vượt trội hẳn khả năng của những người

không chuyên nghiên cứu về lĩnh vực đặc thù đó.

-  Trong những năm  1990, ngành TTNT càng phát triển mạnh hơn nữa

theo các hướng:  cơ sở tri thức và hệ chuyên gia, xử lý ngôn ngữ tự

nhiên, lý thuyết nhận dạng hình ảnh, tiếng nói và ứng dụng vào các kỹ

thuật đa phương tiện, siêu văn bản, mạng nơron, máy học, lý thuyết mờ

trong lập luận xấp xỉ, lập trình tiến hoá, khai thác tri thức từ dữ liệu, ...

* Có vài dấu hiệu quan trọng của trí tuệ máy là các khả năng:

học; mô phỏng các hành vi sáng tạo của con người; trừu tượng hóa, tổng quát hóa

và suy diễn; tự giải thích hành vi; thích nghi với tình huống mới gồm khả năng thu   Chöông 1 – Khaùi nieäm veà Trí tueä nhaân taïo  3

nạp dữ liệu tích hợp, rút tri thức từ dữ liệu; xử lý các biểu diễn hình thức (các ký

hiệu tượng trưng, danh sách); vận dụng các tri thức heuristics sẵn có; xử lý các

thông tin bất  định, không  đầy  đủ, không chính xác, ...Trí tuệ máy khác trí tuệ

người ở chỗ nó không thể nhìn trước được một phần hay toàn thể quá trình giải

trong những tình huống mới và không tự sinh ra được các heuristics của chính

bản thân chúng.

 

* TTNT gồm các phương pháp và kỹ thuật cơ bản  sau: phương

pháp biểu diễn và giải quyết vấn đề; kỹ thuật heuristics; phương pháp biểu diễn và

xử lý tri thức; phương pháp học và nhận dạng, xử lý ngôn ngữ tự nhiên và các

ngôn ngữ lập trình cho TTNT. TTNT vẫn kế thừa các kỹ thuật cơ bản của tin học

truyền thống như: xử lý danh sách, kỹ thuật đệ qui và quay lui, cú pháp hình thức,

... Trong bất kỳ một hệ thống TTNT nào cũng đều có 2 thành phần cơ bản liên

quan mật thiết với nhau: các phương pháp biểu diễn vấn  đề và tri thức, các

phương pháp tìm kiếm trong không gian bài toán, các chiến lược thu hẹp không

gian lời giải và suy diễn.

 

I.2. Những lĩnh vực nghiên cứu của trí tuệ nhân tạo

I.2.1. Từ  thuật toán đến thuật giải

* Đặc trưng của thuật toán (Algorithm): yêu cầu thỏa mãn nghiêm

ngặt 3 tính chất: xác định, hữu hạn, đúng đắn. Ưu điểm: những bài toán giải được

bằng thuật toán có độ phức tạp không quá đa thức được áp dụng tốt trong thực tế.

Nhược  điểm: những thuật toán có phức tạp trên  đa thức chỉ  được áp dụng với

không gian bài toán nhỏ; trên thực tế, lớp các bài toán khó chưa có thuật toán giải

hoặc chưa biết được thuật toán giải hiệu quả rộng hơn rất nhiều. 

Một hướng để giải quyết khó khăn đó là mở rộng tính xác định, tính đúng

và đưa vào thêm các thông tin đặc trưng về bài toán, đưa vào máy tính một kiểu

kinh nghiệm và “tư duy” của con người là sự ước lượng, để thu được các thuật

giải heuristic.

* Đặc trưng của  thuật giải heuristic:  độ phức tạp bé, cho phép

nhanh chóng tìm ra các lời giải, nhưng không phải luôn luôn tìm ra mà có thể tìm

thấy lời giải chỉ trong đa số trường hợp; vả lại, các lời giải này chưa chắc luôn

đúng hay tối ưu mà thường gần đúng hay gần tối ưu.

I.2.2. Phân loại các phương pháp giải quyết vấn đề

* Biểu diễn vấn đề: Xét vấn đề: A  → B.   Chöông 1 – Khaùi nieäm veà Trí tueä nhaân taïo  4

- Dạng chuẩn: Cho A, B tìm → (hai loại thuật toán, chương trình:

thuật toán cần xác định trước chính là → và thuật toán tổng quát để tìm ra

→).

 - Cho A, →, tìm B (suy diễn tiến).

- Cho B, →, tìm A (suy diễn lùi).

* Nhóm các phương pháp xác định trực tiếp lời giải: phương pháp

chính xác, phương pháp xấp xỉ gần đúng, phương pháp không tường minh, đệ qui,

nguyên lý qui hoạch động

* Nhóm các phương pháp xác định gián tiếp lời giải hoặc tìm

kiếm lời giải: 

-  Phương pháp thử – sai: vét cạn, nguyên lý mắt lưới, phương pháp

nhánh cận, sinh và thử lời giải, phương pháp ngẫu nhiên (phương pháp

Monte – Carlo, thuật giải di truyền GA), nguyên lý mê cung (dạng đệ

qui), vét cạn dần (dưới dạng lặp) bằng cách quay lui và xác định dần

thông tin về bài toán trong quá trình giải thông qua các cấu trúc không

tuyến tính (chẳng hạn: cây,  đồ thị hoặc  đồ thị VÀ/HOẶC như  các

phương pháp tìm kiếm: theo chiều rộng, sâu, sâu dần, cực tiểu AT

),

phương pháp GPS, …

-  Phương pháp heuristic trong trí tuệ nhân tạo: là hướng tiếp cận quan

trọng để xây dựng các hệ thống TTNT. Nó bao gồm các phương pháp

và kỹ thuật tìm kiếm có sử dụng các tri thức đặc biệt từ chính bản thân

lớp bài toán cần giải nhằm rút ngắn quá trình giải và nhanh chóng đi đến

kết quả mong muốn mặc dù có thể không chắc chắn  đó là cách giải

quyết tối ưu nhưng lại có tính khả thi trong điều kiện thiết bị hiện có và

thời gian yêu cầu. Trong kỹ thuật này người ta thường sử dụng kỹ thuật

heuristis định lượng thông qua các hàm đánh giá. Chúng ta sẽ minh họa

các phương pháp heuristics thông qua các phương pháp vét cạn thông

minh (tìm kiếm tối ưu được bổ sung bằng tri thức đặc trưng về bài toán

trên cây hoặc đồ thị tổng quát: AKT

, A*), nguyên lý tham lam, nguyên lý

hướng đích (thuật giải leo núi), nguyên lý sắp thứ  tự, nguyên lý khớp

nhất, …

Những thông tin heuristic này vẫn được gián tiếp đưa vào máy  tính thông

qua con người. Vậy máy tính có thể tự  tạo ra các “tri thức”, biết suy luận, chứng

minh, tự học qua kinh nghiệm, máy có khả năng rút ra tri thức và vận dụng chúng

vào việc giải quyết bài toán hay không ? 

Các phương pháp trong trí tuệ nhân tạo đã giúp máy tính thực hiện được

trong một chừng mực nào đó các vấn đề đặt ra ở trên: các phương pháp biểu diễn   Chöông 1 – Khaùi nieäm veà Trí tueä nhaân taïo  5

và xử lý tri thức, lập trình tiến hoá, mạng neuron nhân tạo, máy học, khai thác tri

thức từ dữ liệu, …

I.2.3. Biểu diễn và xử lý tri thức

Có 4 phương pháp cơ bản biểu diễn và xử lý tri thức - dữ liệu tích hợp:

phương pháp hình thức sử dụng cách tiếp cận logic (lôgic cổ điển - tất định: lôgic

mệnh đề, lôgic vị từ; lôgic bất định: lôgic xác suất, lôgic khả xuất, lôgic mờ), các

luật sinh (thường dùng trong các hệ chuyên gia), mạng ngữ nghĩa, bộ ba liên hợp

OAV, cách biểu diễn bằng khung (hay dàn - Frame),... Các hệ chuyên gia là những

thể hiện của việc kết hợp của các phương pháp biểu diễn và phương pháp xử lý tri

thức (ví dụ: DENDRAL, MOLGEN, PROSPECTOR, MYCIN, ...).

I.2.4. Xử lý ngôn ngữ tự nhiên, các ngôn ngữ lập trình dựa trên việc xử

lý danh sách, ký hiệu và lập trình logic: các ngôn ngữ lập trình LISP, PROLOG

có hạn chế là chi phí lớn và khó phát triển hệ thống; còn CLIPS nhằm biểu diễn tri

thức theo hướng đối tượng và xử lý các luật suy dẫn. Ta có thể thấy sự khác nhau

cơ bản giữa lập trình truyền thống và lập trình xử lý ký hiệu trong TTNT qua bảng

so sánh I.1.

Lập trình truyền thống  Lập trình xử lý ký hiệu và lôgic

- Xử lý dữ liệu  - Xử lý tri thức - dữ liệu tích hợp

- Dữ liệu trong bộ nhớ được đánh địa chỉ số  - Tri thức được cấu trúc trong bộ nhớ làm việc

theo ký hiệu

- Xử lý theo các thuật toán  - Xử lý theo các thuật giải heuristics và cơ chế

lập luận

- Định hướng xử lý các đại lượng định lượng số -  Định hướng xử lý các  đại lượng  định tính,

logic, ký hiệu tượng trưng, danh sách

- Xử lý tuần tự  hoặc theo lô  - Xử lý theo chế  độ tương tác cao (hội thoại,

theo ngôn ngữ tự nhiên,...)

- Không giải thích trong quá trình thực hiện  - Có thể tự giải thích hành vi hệ thống trong

quá trình thực hiện

Bảng I.1

 

I.2.5. Lý thuyết nhận dạng  theo hướng thống kê, cấu trúc,  đại số và

heuristics gồm: nhận dạng hình ảnh và âm thanh (HEARSAY-II, …). 

I.2.6. Lập trình tiến hoá, mạng nơron, máy học, khai thác dữ liệu

- Lập trình tiến hóa  (Revolution Programming)  sử dụng ý tưởng  qui

luật tiến hoá và học thuyết di truyền của ngành sinh học: những gì hợp lý,  thích   Chöông 1 – Khaùi nieäm veà Trí tueä nhaân taïo  6

nghi tốt với môi trường sẽ có khả năng tồn tại lâu dài hơn trong quá trình đào thải,

sinh tồn; một số đặc điểm của thế hệ trước sẽ di truyền, ảnh hưởng đến thế hệ sau

thông qua  lai chéo; thỉnh thoảng vẫn xuất hiện vài cá thể có đặc điểm khác hẳn

(hoặc nổi trội lại các đặc điểm tiềm tàng) với thế hệ trước của chúng thông qua đột

biến, … Khởi  điểm của hướng nghiên cứu này là thuật giải di truyền (GA -

Genetic Algorithm). Ưu điểm của các thuật giải GA là có thể áp dụng đối với các

bài toán chưa biết thuật toán nào hay chưa có thuật giải nào khả dĩ, hiệu quả để

giải.  Có thể xem GA thuộc vào lớp các thuật toán ngẫu nhiên thông qua việc tạo

ngẫu nhiên quần thể ban đầu cũng như các vị trí lai chéo hay tỉ lệ lai chéo và đột

biến của chúng. 

-  Mạng nơron nhân tạo  (hay vắn tắt hơn là mạng nơron,  ANN -

Artificial Neural Networks) mô phỏng mô hình và cơ chế hoạt động hưng phấn và

ức chế thần kinh để điều khiển hoạt động của con người. Mô hình ANN có thể

gồm nhiều lớp, trong đó ít nhất phải có lớp nhập (input layer) và lớp xuất (output

layer), ngoài ra có thể có nhiều lớp  ẩn (hidden layers) trung gian. Mỗi lớp gồm

nhiều nút. Các kích thích từ môi trường ngoài được truyền vào mạng thông qua

các nút của lớp nhập. Tổng hợp các kích thích này (phụ thuộc các trọng số mà

mạng này cần học), nếu vượt quá một ngưỡng nào  đó, sẽ gây kích thích (hưng

phấn hay ức chế) đến các nút của lớp kế tiếp. Cứ thế, quá trình tiếp tục lan truyền

đến lớp xuất. Một mô hình ANN thường được ứng dụng nhiều trong thực tế là

mạng nơron lan truyền ngược. Lặp lại quá trình này, dựa trên việc cập nhật trọng

số qua mỗi thế hệ sao cho giảm dần sai số giữa giá trị thật và giá trị do mạng kết

xuất. Qua một số thế hệ luyện, mạng sẽ học được bộ trọng số thích hợp. Ưu điểm

của các phương pháp luyện mạng ANN là  có thể áp dụng đối với các bài toán

chưa biết thuật toán nào hay chưa có thuật giải nào khả dĩ, hiệu quả để giải.

- Máy học (LM - Learning Machine) là quá trình rút ra qui luật từ dữ liệu,

chẳng hạn học thông qua lôgic, học bằng quan sát dựa trên các độ đo phù hợp, học

dựa trên cây định danh thông qua độ đo hỗn loạn thông tin trung bình, … Ta có

thể dùng ANN để ứng dụng vào máy học.

  

 - Khai thác dữ liệu (DM - Data Mining) nhằm rút ra tri thức từ dữ liệu

thô, chẳng hạn đo độ phụ thuộc của một lớp các thuộc tính xác định vào lớp các

thuộc tính phổ biến khác trong tập dữ liệu thô cho trước thông qua luật kết hợp, ...

I.3. Những ứng dụng của TTNT

-  Điều khiển học, Robotic, giao diện người máy thông minh

-  Trò chơi máy tính

-  Thiết bị điện tử thông minh nhờ sử dụng lôgic mờ

-  Hệ chuyên gia trong: giáo dục, y khoa, địa chất, quản lý, …

-  Xử lý ngôn ngữ tự nhiên   Chöông 1 – Khaùi nieäm veà Trí tueä nhaân taïo  7

-  Nhận dạng hình ảnh, âm thanh, …

-  Các hệ thống xử lý tri thức và dữ liệu tích hợp: cho phép xử lý đồng

thời tri thức và dữ liệu (cơ sở dữ liệu suy diễn, biểu diễn luật đối tượng,

hệ hỗ trợ quyết định)

-  Mô hình hoá các giải pháp giải bài toán

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  8  

Chương  II

CÁC PHƯƠNG PHÁP GIẢI QUYẾT VẤN ĐỀ

II.1. Các phương pháp xác định trực tiếp lời giải

Đặc điểm của các phương pháp này là xác định trực tiếp được lời giải thông

qua một  thủ tục tính toán hoặc các bước căn bản để có được lời giải. Có ba loại

phương pháp chính để xác định trực tiếp lời giải. Loại thứ  nhất được áp dụng để

giải các bài toán đã biết cách giải bằng các công thức chính xác (như công thức

toán học). Loại thứ  hai được dùng cho các bài toán đã biết cách giải bằng các

công thức xấp xỉ (như các công thức xấp xỉ trong phương pháp tính). Loại thứ  ba

được áp dụng vào các bài toán đã biết cách giải không tường minh thông qua các

hệ thức truy hồi hay kỹ thuật đệ qui.

II.1.1. Phương pháp giải chính xác: thông qua các công thức giải chính xác.

Chẳng hạn, thuật toán giải phương trình bậc hai.

II.1.2. Phương pháp giải xấp xỉ: thông qua các công thức giải gần  đúng.

Chẳng hạn, phương pháp lặp tính tích phân xác định theo công thức hình thang

trong học phần “Phương pháp tính”.

II.1.3. Phương pháp giải không tường minh: thông qua các hệ thức truy hồi

hoặc kỹ thuật đệ qui.

* Thao tác đệ qui F(x) trên 1 đối tượng x ∈ D nào đó. Xét hai trường hợp:

-  Nếu đối tượng x thuộc một tập đặc biệt X0 nào đó (X0 ⊂ D) mà đã biết

cách giải đơn giản thì thực hiện các thao tác sơ cấp tương ứng;

-  Ngược lại, trước hết có thể thực hiện một thao tác G(x) nào đó, biến đổi

x thành x’= H(x) ∈ D rồi thực hiện thao tác tương tự  F(x’) trên x’, sau

đó có thể thực hiện thêm một thao tác K(x) nào đó trên x, sao cho sau

một số hữu hạn bước này, các điểm xn(‘)

 sẽ rơi vào tập X0.

F(x) //x ∈ D

{

   if (x ∈ X0) ThaoTácSơCấp(x);  // điều kiện dừng

   else ⎨ G(x);

      x’ = H(x);    //H(x) ∈ D   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  9  

      F(x’);   // lời gọi đệ qui

  K(x);   

           ⎬

}

 

Các thao tác đệ qui thường gặp trong tin học là: định nghĩa đệ qui, hàm hoặc

thủ tục đệ qui, thuật toán đệ qui.

 * Chú ý: Khi thiết kế một thao tác đệ qui, ta cần có hai phần:

- Phần cơ sở (phần neo hay điều kiện dừng): thao tác sơ cấp đã biết cách

thực hiện ngay trên tập con X0 ⊂ D. 

  - Phần gọi đệ qui F(x’): cần phải bảo đảm sau một số hữu hạn bước biến

đổi x  thì  ta sẽ gặp điều kiện dừng:  H(H( … H(x))) = x0 ∈ X0.

- Trên đây, ta xét đệ qui đuôi trực tiếp. Các trường hợp phức tạp hơn một

chút như đệ qui nhánh trực tiếp và đệ qui gián tiếp (hay đệ qui hỗ tương)

được xét tương tự.

Ví dụ 1a *   (dãy số Fibonacci): Ở đầu tháng thứ 1 có 1 cặp thỏ con mới ra đời (F(0) = 0,

F(1) = 1). Giả sử:

- Cứ sau mỗi tháng một cặp thỏ (từ sau hai tháng tuổi) sẽ sinh thêm một cặp thỏ con;

 - Các con thỏ không bao giờ chết.

Hỏi số cặp thỏ F(n) sau n tháng là bao nhiêu?

 Ta có công thức truy hồi để tính F(n) như sau: 

F(0) = 0; F(1) = 1 (X0 = ⎨0; 1⎬)

  F(n) = F(n-1) + F(n-2), ∀n ≥ 2 

  Để tính F(n), ta có thể thực hiện theo các cách sau:

-  Thuật toán  đệ qui  sau  đây có  độ phức tạp thuật toán với số phép cộng là O(F(n)) =

O(((1+ 5 )/2)

n

): độ phức tạp mũ, quá lớn, không khả thi !

Nguyên Fibonaci_De_Qui(n)

{ if (n ≤ 1) return n;

 else return (Fibonaci_De_Qui(n-1) + Fibonaci_De_Qui(n-2));

}

 - Thuật toán lặp sau đây có độ phức tạp thuật toán với số phép cộng là O(n):  hiệu quả hơn

nhiều ! Ta có thể khử đệ qui bằng cách dùng vòng lặp và vài biến phụ hoặc dùng cơ chế ngăn xếp.

Nguyên Fibonacci_Lặp(n)

{  if (n==0 or n==1) return n;

    else { j = 1;

Truoc = 0;

HienTai = 1;

while (j < n) do

{ Sau = Truoc + HienTai;

 Truoc = HienTai;    Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  10  

 HienTai = Sau;

j = j + 1;

}

return Sau;

         }

}

 - Tìm công thức tường minh từ hệ thức truy hồi

 F(n) =

2

51+

2

51−

5

1 n n

[ ( )  + ( ) ]

* Phương pháp tổng quát tìm công thức tường minh cho Fn từ hệ thức truy hồi tuyến tính

(hệ số hằng):

   Fn = b1 Fn-1 + b  F 2 n-2  ,  ∀n ≥ 2  với các trị {F0, F1} cho trước.

         Gọi {Φ1, Φ2} là 2 nghiệm của đa thức đặc trưng tương ứng: 

Φ

2

- b1Φ - b2=0. 

n n

Khi đó:    Fn  = c  Φ  +  c 1 1 2 Φ  , (c 2 1, c  sẽ được xác định từ các điều kiện đầu của F 2 0,

F1).

Nhận xét *  : Trong nhiều bài toán khó, ta có thể dùng chiến lược “Chia để trị” để tách nó

thành nhiều bài toán con có cùng cách giải như bài toán ban đầu thông qua kỹ thuật đệ

qui. 

- Ví dụ 1b: Tráo đổi hai phần a[1..k] và a[k+1..n] của mảng a gồm n phần tử (không nhất

thiết có độ dài bằng nhau) mà không dùng mảng phụ.

Nếu k ≤ n-k thì trước tiên trao đổi hai phần bằng nhau a[1..k] và a[n-k..n]; sau đó

trong mảng con a[1..n-k], ta chỉ cần tráo đổi k phần tử đầu với phần còn lại. Trường hợp

k ≥ n-k, giải tương tự, ...

TraoHaiPhanBangNhau(a, Tu1, Tu2, SoPTuTrao)

{  for (i=1; i ≤ SoPTuTrao; i++)

  DoiCho(a[Tu1+i], a[Tu2+i]);

  }

  TraoHaiPhanBatKy(a, n, k) // k >= 0

{ i = 1; j = n;

    while (k >= i)

    { if (k < (i+j)/2)

            { TraoHaiPhanBangNhau(a, i, i+j-k, k-i+1);

        j = i + j - k - 1;

            }

       else { TraoHaiPhanBangNhau(a, i, k+1, j-k);

        i = i + j - k;

     }

    }

}

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  11  

 II.1.4. Phương pháp qui hoạch động:

* Ý tưởng của nguyên lý qui hoạch động: nghiệm của một bài toán con (của

một bài toán) là sự kết hợp các nghiệm của các bài toán con nhỏ hơn của nó (trong

trường hợp rời rạc có tính đệ qui thì nghiệm trong n bước sẽ có được từ lời giải

của k bước trước và lời giải trong n-k bước). Ta thường dùng phương pháp này để

giải các bài toán tối ưu mà thỏa mãn nguyên lý trên. 

Có thể xem nguyên lý tối ưu là một sự thể hiện tốt của phương pháp chia để

trị trong việc giải quyết vấn đề. Khi thực hiện các tính toán trong phương pháp qui

hoạch động, để thực hiện tính toán tại bước thứ n, nên  tận dụng các kết quả đã

tính ở các buớc trước thông qua các hệ thức truy hồi và một vài biến phụ để lưu

các kết quả trung gian trước đó (chẳng hạn, xét bài tập: tính tất cả các số tổ hợp

Ck

n, với mọi k: 0 ≤ k ≤ n).

* Mô hình toán học của nguyên lý tối ưu

 - Định nghĩa II.1 (hàm phân tích được): Cho hàm f: D → R, D ⊂ Rn

D1 = {x1 ∈ R1

: ∃y∈Rn-1

 & (x1,y)∈D}, D(x1) = {y ∈ Rn-1

 : (x1, y) ∈ D} ∀ x  ∈ D .  1 1

Ta nói hàm f là phân tích được nếu tồn tại hai hàm g: R2

 → R  và h: Rn-1

 → R1

 sao cho:

  . f(x , y) = g(x 1 1, h(y)), ∀ x = (x1, y) ∈ D, x  ∈ D , y ∈ D(x 1 1 1)

 . Hàm g đơn điệu không giảm theo biến thứ hai:

  ∀ x1 ∈ D1, ∀ z1, z2 ∈ R1

: z  ≥ z   ⇒ g(x 1 2 1, z ) ≥ g(x , z 1 1 2)

(thật ra chỉ cần: ∀ x1 ∈ D1, ∀ z1, z  ∈ R1

, ∃ y 2 1, y2 ∈ D(x1): z1 = h(y ), z 1 2 = h(y2),

 z  ≥ z   ⇒ g(x 1 2 1, z1) ≥ g(x1, z ))  2

  - Mệnh đề II.1: Cho f  là hàm phân tích được (trong định nghĩa II.1). Khi đó, ta có: 

)])]([,([)(

)(

1

1 11

yhoptxgoptxfopt

xDy Dx Dx ∈ ∈ ∈

=

Nhận xét -  : 

. Kết quả của mệnh đề trên cho phép ta đưa việc tối ưu hàm nhiều biến về tối ưu

các hàm theo các biến thành phần có số chiều bé hơn. 

. Ta thường gặp trường hợp hàm g có dạng tuyến tính theo biến thứ hai:

    g(x , z) = r(x 1 1) + z

  và f có dạng cộng tính theo từng thành phần: 

     f(x1, x , …, x 2 n) = r(x ) + r(x 1 2) + … + r(xn) 

Ví dụ 2 *   (bài toán người giao hàng Salesman): Hàng ngày, người giao hàng phải chuyển

hàng qua n địa điểm, mỗi địa điểm đúng một lần, rồi quay lại địa điểm xuất phát. Bài toán

đặt ra là: làm thế nào để anh ta có được một hành trình với đường đi ngắn nhất. 

Ta biểu diễn bài toán bằng đồ thị định hướng G = (V, A), với V={1, 2, …, n} và

độ dài cung C(i,j) > 0, nếu (i,j) ∈ A và C(i,j) = ∞, nếu (i,j) ∉ A. Không mất tính tổng

quát, ta có thể giả sử đường đi của anh ta xuất phát từ đỉnh 1. Bất kỳ đường đi nào (chấp

nhận được) của người giao hàng cũng có thể phân thành: cung (1,k) với k ∈ V\{1} và

đường đi từ k tới 1 qua mỗi đỉnh thuộc V\{1} đúng một lần. Nếu đường đi của anh ta

ngắn nhất thì đường đi từ k tới đỉnh 1 qua các đỉnh thuộc V\{1, k} phải ngắn nhất. Do đó   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  12  

nguyên lý tối ưu được thỏa mãn. Gọi d(j, S) là độ dài đường đi ngắn nhất từ j đến đỉnh 1

qua mỗi đỉnh k ∈ S (∀S ⊂ V và S ≠ ∅) đúng một lần. Ta có công thức truy hồi: 

 Nghiệm tối ưu cần tìm là:

Rõ ràng, d(j, ∅) = C(j,1), ∀ j ∈ [2, n]. Từ công thức truy hồi trên, ta tính được 

d(j, S) với mọi S chỉ chứa 1 đỉnh. Từ đó, ta tính được d(j, S) với mọi S chỉ chứa 2 đỉnh, ...

Cứ thế tiếp tục, ta tính được d(k, S) với mọi S = V\{1,k}, ∀ k ∈ [2, n]. Từ đó, ta tìm được

nghiệm tối ưu.

})),1{\,(),1((min})1{\,1(

2

kVkdkC Vd

nk

+ =

≤≤

})){\,(),((min),( kSkdkjCSjd

Sk

+ =

- Cụ thể, xét đồ thị định hướng có 4 đỉnh và độ dài các cung được cho bởi ma trận C như

sau: 

0988

120136

10905

2015100

Ta có: d(2, ∅) = C(2,1) = 5

 d(3, ∅) = C(3,1) = 6

 d(4, ∅) = C(4,1) = 8

Từ công thức truy hồi, ta tính được:

 d(2, {3}) = C(2,3) + d(3, ∅) = 15

 d(2, {4}) = C(2,4) + d(4, ∅) = 18

 d(3, {2}) = C(3,2) + d(2, ∅) = 18

 d(3, {4}) = C(3,4) + d(4, ∅) = 20

 d(4, {2}) = C(4,2) + d(2, ∅) = 13

 d(4, {3 3 }) = C(4, ) + d(3, ∅) = 15

Tương tự:

 d(2, {3, 4}) = min{C(2,3) + d(3, {4}), C(2,4) + d(4, {3})} 

         = min {29, 25} = 25

 d(3, {2, 4}) = min{C(3,2) + d(2, {4}), C(3,4) + d(4, {2})} 

         = min {31, 25} = 25

 d(4, {2, 3}) = min{C(4,2) + d(2, {3}), C(4,3) + d(3, {2})}

         = min {23, 27} = 23

Cuối cùng, ta có:

d(1, {2, 3, 4}) = min{ C(1,2) + d(2, {3, 4}), C(1,3) + d(3, {2, 4}), 

C(1,4) + d(4, {2, 3})} 

35           = min { , 40, 43} = 35

Để tìm được hành trình ngắn nhất, gọi K(j,S) là đỉnh k, tại đó nó đạt min của công

thức truy hồi để tính d(j,S). Từ đó, ta có:

  K(1, {2, 3, 4}) = 2

 K(2, {3, 4}) = 4

 K(4, {3}) = 3

Vậy đường đi {1, 2, 4, 3, 1} là ngắn nhất và đạt giá trị 35.

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  13  

Tương tự, có thể áp dụng nguyên lý qui hoạch động để giải bài toán sau.

Ví dụ 3 -   (bài toán sắp ba lô): một chiếc ba lô có thể chứa được một khối lượng w. Có n

loại đồ vật được đánh số 1, 2, …, n. Mỗi đồ vật loại i có khối lượng ai và có giá trị ci  (các trị w,

a , c i i đều nguyên dương, i = 1, 2, …, n). Cần sắp xếp các đồ vật vào ba lô để ba lô có giá trị lớn

nhất có thể được. Giả sử rằng mỗi loại đồ vật có đủ nhiều để xếp (bài tập).

II.2. Các phương pháp thử - sai 

 II.2.1. Phương pháp vét cạn, nguyên lý mắt lưới, phương pháp sinh và thử,

phương pháp nhánh cận

* Bài toán 1: Tìm tập  LờiGiải = ⎨x ∈ D: tính chất P(x) đúng⎬.      (BT1)

 

a. Phương pháp vét cạn

 * Thuật toán vét cạn V1.1: giải BT1

     { B1: LờiGiải = ∅; 

  B2: Trong khi D ≠ ∅ thực hiện:

   x ← get(D);  //lấy khỏi D một phần tử x

   if (P(x)) LờiGiải = LờiGiải ∪ {x}; 

  B3: if (LờiGiải == ∅) write(“Không có lời giải”);

         else XuấtLờiGiải(LờiGiải);

     }

* Chú ý: - Nếu hạn chế miền D càng bé thì thuật toán chạy càng nhanh.

* Ví dụ 4: Cho trước các số M, K nguyên dương. Tìm các bộ số nguyên dương x, y, z sao

cho:

Thay vì chọn D = {(x,y,z) ∈ N3

 ∩ [1;M-2]

3

} (N là tập các số tự nhiên), ta lấy: D =

{(x,y,z) ∈ N

=++

=++

Kzyx

Mzyx

333

3

 ∩ [1; min{M-2; }]

3

}. 

3

2 − K

Khi lập trình, ta thể hiện miền D bởi 3 vòng for theo x, y, z:

Tìm_xyz(M, K)

{ Max = min(M-2, pow(K-2,1.0/3)); 

      for (x = 1; x ≤ Max; x++)

       for (y = 1; y ≤ Max; y++)

          for (z = 1; z ≤ Max; z++)

           if (x+y+z==M && pow(x,3)+ pow(y,3)+ pow(z,3) == K)

   XuấtLờiGiải(x,y,z);

  }

Thật ra, vẫn có thể cải tiến chương trình trên để thu hẹp miền D hơn nữa (bài tập) !

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  14  

-  Dựa vào thuật toán trên, ta có thể sửa  đổi chút ít  để giải bài toán tìm

kiếm chỉ một lời giải sau:

* Bài toán 2:  Tìm một lời giải x0∈ D: mệnh đề P(x0) đúng.   (BT2)

Thuật toán tìm một lời giải V1.2: giải BT2

    { Trong khi D ≠ ∅ thực hiện:

     {   x ← get(D);  //lấy khỏi D một phần tử x

if (P(x)) 

{ XuấtLờiGiải(x); 

   Dừng;  // điểm chính khác với thuật toán V1

          }

  }

 write(“Không có lời giải”);

    }

    * Đối với một lớp bài toán nào đó mà có thể tìm được một điều kiện đủ

Q(x) cho lời giải x, ta có thể dùng  thuật giải  sau:  với mỗi x ∈ D mà Q(x)

đúng thì xuất lời giải. Chú ý rằng, ngoài các lời giải trên, trong D còn có thể

chứa các lời giải khác mà không thỏa điều kiện đủ này ! Nguyên lý mắt lưới

sau đây là một thể hiện của ý tưởng trên.

b. Nguyên lý mắt lưới:

* Ý tưởng: Những con cá lớn hơn kích thước mắt lưới lớn nhất sẽ còn lại

trong lưới ! 

   Để giải bài toán (1), nếu chứng minh được: 

“nếu tìm được điều kiện Q(x) đúng với một vài x thuộc một miền

con của D có kích thước nhỏ hơn ε thì P(y) đúng với mọi y ∈ miền

con đó”

thì: Chia lưới D thành n miền con Di (mỗi miền Di có kích thước nhỏ hơn ε).

Với mỗi i = 1 .. n, xét nếu tồn tại x∈ Di mà Q(x) đúng thì P(x) đúng.

 

* Ví dụ 5: Tìm nghiệm gần đúng (với độ chính xác eps > 0) của phương trình f(x) = 0 trên

miền [a, b], với f là hàm liên tục. 

Ta đã biết nếu f(xi).f(xi+1) < 0, với [xi, xi+1] ⊂ [a, b] và abs(xi+1-xi) < eps thì  xk =(xi+1+x i)/2

sai khác với một nghiệm chính xác của phương trình f(x) = 0 không quá eps/2 (một điều kiện đủ

để tìm nghiệm của phương trình liên tục).

 - Thuật giải tìm nghiệm gần đúng:

 Nghiem_Gan_Dung(a, b, eps)

{ Sai_so = 1e-3; 

   n = (b-a)/eps;  

   xi = a;   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  15  

   for  i=1 to n do

    { xi_1 = xi + eps;

       if (f(xi)*f(xi_1) < - Sai_so)  writeln((xi + xi_1)/2);

      xi = xi_1;

    }

  }

 c. Phương pháp sinh và thử

  * Ý tưởng: Sinh dữ liệu (cấu trúc của lời giải có thể không xác định trước

khi giải mà chỉ được tạo ra dần  trong quá trình tìm kiếm lời giải), sau đó

kiểm tra nó có thỏa điều kiện dừng hay không ?

- Thuật toán sinh và thử:

Sinh_Thử 

{ Init;         // khởi tạo dữ liệu xuất phát

   Stop = False;

   While not Stop do

   { if (Accept(C)) Show(C); // nếu phương án C là chấp nhận thì xuất C

       if (Generate(C)=False) Stop = True; 

// nếu không sinh được thêm phương án C nào nữa thì dừng

   } 

}

 

  * Ví du 6: bài toán chỉnh hợp lặp chập k của n  phần tử  X = ⎨0,1, …,n-1⎬: ⎨x1, x2, …,

xk⎬, với xi ∈ X, 1 ≤ i ≤ k.

Thủ tục Init sẽ khởi tạo 0 cho vectơ lời giải x = ⎨x1, x2, …, xk⎬. Trong ví dụ này

không cần  đến  điều kiện kiểm tra  Accept: ta luôn cho nó nhận trị  đúng. Thủ tục

Generate(x, k) sẽ tăng dần x[j] một đơn vị, j bắt đầu từ  k đến 1, cho đến khi x[j]=n-1 thì

khởi động lại 0 cho x[j], rồi giảm j đi một. Khi nào j=0 thì dừng việc sinh dữ liệu.

Boolean Generate(x, k)

{ j = k; // sinh chỉnh hợp kế tiếp

   while (j>0 and x[j] == n-1) do

   { x[j] = 0;

      j = j-1;

   } 

    if (j==0) return False;

    else { x[j] = x[j]+1;

   return True;

 }

}

(Ngoài ra, ta có thể giải bài toán này bằng cách này sử dụng thuật toán  đệ qui

TryRờiRạc(i) trong II.2.3 với điều kiện kết thúc nghiệm là i = k).

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  16  

Để các thuật toán vét cạn không bị bùng nổ tổ hợp về thời gian và không

gian nhớ, ta cần: giảm độ phức tạp tính toán (không tính lại các hằng trong vòng

lặp, cần tận dụng lại các kết quả tính toán ở các bước trước, dùng kỹ thuật lính

canh để đơn giản các biểu thức điều kiện của vòng lặp, …); thu gọn không gian

tìm kiếm. 

* Chiến lược  thu hẹp không gian tìm kiếm: Trong các thuật toán vét cạn

để tìm kiếm lời giải trong không gian D,  đối với một lớp các bài toán nào đó, dựa

trên các đánh giá toàn cục (ví dụ: duyệt các bộ tổ hợp), cục bộ (ví dụ: bài toán sắp

ba-lô), nếu ta tìm ra được các điều kiện cần cho lời giải, khi đó ta có thể cải tiến

thuật toán bằng cách loại bỏ ngay các phương án trong D không thỏa điều kiện

cần này ! Khi đó:

. hoặc xét tập D (nếu có thể) chỉ chứa những trạng thái thoả mãn điều kiện

cần cho lời giải mà thôi, do đó D được thu hẹp ngay;

. hoặc xét tập D như thông thường, nhưng trong các thuật toán vét cạn, ta

hiểu get(D) là lấy ra khỏi D phần tử x: nếu x không thỏa điều kiện cần cho lời giải

thì loại ngay nó (và thực hiện việc này càng sớm tới mức có thể để tránh các thao

tác thừa) rồi lấy ngay phần tử x tiếp theo của D, ngược lại mới kiểm tra tính chất

P(x). 

Phương pháp nhánh cận là một thể hiện của chiến lược này.

d. Phương pháp nhánh cận

* Ý tưởng:  nhánh có chứa quả phải nặng hơn trọng lượng quả. Khi xây

dựng thêm thành phần cho lời giải, dùng các phép kiểm tra đơn giản để xác định

chi phí tối thiểu đến lời giải (điều kiện cần cho lời giải). Loại bỏ ngay các hướng

đi tiếp theo mà chi phí tối thiểu này còn lớn hơn cả chi phí thấp nhất hiện thời

(hướng không thỏa điều kiện cần).

* Ví dụ 7 (Bài toán người du lịch): Có n thành phố (được đánh số từ 1 đến n). Một người

du lịch xuất phát từ một thành phố, muốn đi thăm các thành phố khác, mỗi thành phố

đúng một lần rồi lại quay về nơi xuất phát. Giả thiết giữa hai thành phố j, k khác nhau bất

kỳ đều có đường đi với chi phí c(j,k). Hãy tìm một hành trình có tổng chi phí nhỏ nhất.

Một hành trình x[1], x[2], …, x[n] là một hoán vị của {1, 2, …, n}. Dùng mảng

lôgic ChưaĐến để đánh dấu các thành phố đã đi qua: ChưaĐến[k] = True nghĩa là người

du lịch chưa đến thành phố k. Nếu việc đến thành phố k có tổng chi phí dự đoán thấp nhất

để hoàn thành toàn bộ hành trình lớn hơn chi phí thấp nhất hiện thời thì ta không chọn đi

tiếp k, ngược lại thì chọn k.

Giả sử ta đã đi qua j-1 thành phố x[1], …, x[j-1] với chi phí là S. Nếu đi tiếp đến

thành phố x[j] = k thì chi phí từ x[1] đến x[j] là T = S + c[x[j-1],k]. Đoạn còn lại của

hành trình gồm (n-j+1) đoạn nữa, với chi phí trên mỗi đoạn không ít hơn Cmin (là chi phí

trực tiếp thấp nhất giữa hai thành phố khác nhau trong ma trận chi phí). Tổng chi phí thấp

nhất để hoàn thành hành trình là: 

 T + (n-j+1) * Cmin = S + c[x[j-1],k] + (n-j+1)*Cmin

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  17  

  Để giải bài toán này, ta sẽ gọi thủ tục chính TryRờiRạc(0, 2).

TryRờiRạc(S, j)

{ for  k =1 to n do

       if (ChưaĐến[k])

  { T = S + c[x[j-1],k];

     if (T + (n-j+1)*Cmin < Min)  // Min = MaxInt trước thủ tục Try

     { x[j] = k; ChưaĐến[k] = False; 

        if  ( j == n)

      { if (T + c[k, xp] < Min) // xp là thành phố xuất phát

{  y = x;       // mảng y lưu trữ lời giải tốt nhất tạm thời

Min = T + c[k, xp];

}

      }

        else Try(T, j+1);

        ChưaĐến[k] = True;

     }

  }

}

 II.2.2. Phương pháp ngẫu nhiên

Phương pháp này được sử dụng khi chưa biết thuật toán nào hiệu quả

hay chưa có nhiều thông tin để giải bài toán.

a. Phương pháp Monte – Carlo 

  * Ý tưởng: Cho hai hình S ⊂ Ω = [a, b]

m ⊂ Rm, giả sử đã biết công thức

tính độ đo S() của S phụ thuộc vào tham số  nào đó cần tính. Tung ngẫu

nhiên n lần (độc lập) vectơ x = {x1, x2, …, xm} ∈ Ω (có phân phối đều trên

Ω), gọi nS là số lần x ∈ S. Khi đó, với n đủ lớn, ta có: S() ≈ (b-a)

m.nS/n. 

Từ đó rút ra tham số cần tính  theo số liệu thực nghiệm nS, n:

       ≈ S-1

((b-a)

m.nS/n)  

* Thuật toán ngẫu nhiên:  

   { nS = 0;

  for  (j=1; j ≤ n; j++)  

{ for  (k=1; k ≤ m; k++) 

       x[k] = random(a,b); // tạo  các số ngẫu nhiên ∈ [a; b)    

   if (x ∈ S) nS = nS + 1;

   }  

   ≈ S-1

((b-a)

m.nS/n);  

  }

* Ví dụ 8: Dựa vào phương pháp trên, với m = 2, a = 0, b = 1 và S là hình tròn bán kính R=½,

ta có thể tính số π như sau:

π = S/R2

  ≈  4*nS/n        

   x ∈ S < --- >   (x1 – ½ )

2

 +  (x2 – ½ )

2

  ≤ ¼    Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  18  

     x2

 

       0 R=½        x1

- Trong một số trường hợp, ta có thể dùng PP thử ngẫu nhiên để kiểm tra

một tính chất hay giả thuyết nào đó.

   . Ví dụ 9:  Kiểm tra giả thuyết Fermat bằng thực nghiệm: N ≥ 2 (N khá lớn) là nguyên tố

nếu: xN-1

 mod N = 1, ∀x nguyên dương < N ? 

Để phù hợp với điều kiện thực nghiệm trên máy tính, ta xét N và số lần lặp n lớn

vừa phải. Ta có thuật toán sau: 

  { for  (j = 1; j ≤ n; j++)  

   { x = 1 + random(N-1);  // 1 ≤ x < N

     y = xN-1

 mod N; // hãy cải tiến cách tính này hiệu quả hơn trên máy tính !

     if (y ≠ 1) {cout << N << “ không là số nguyên tố”; Dừng; }

   }

  cout << N << “ là số nguyên tố”;

  }

b. Thuật giải di truyền GA (Genetic Algorithm) và lập trình tiến hoá

* Lập trình tiến hoá:  

Chương trình Tiến hóa = CTDL + GA

* Các đặc trưng cơ bản của GA

 . Ngẫu nhiên;

. Duyệt toàn bộ giải pháp (lời giải), sau đó chọn giải pháp tốt nhất dựa trên độ thích

nghi của chúng;

  . Không quan tâm đến chi tiết vấn đề mà chỉ quan tâm đến giải pháp và phương

pháp biểu diễn nó.

* Các bước tiến hành chính của thuật giải di truyền GA

- Bước 1: Chọn mô hình để biểu diễn vấn đề thông qua các dãy ký hiệu (số, chữ hoặc

hỗn hợp) để biểu diễn cho mỗi giải pháp của vấn đề và  số cá thể  (số lời giải chấp

nhận được) trong quần thể biểu diễn vấn đề.

- Bước 2: Tìm hàm số thích nghi (Fitness function) và tính số thích nghi cho từng

giải pháp.

- Bước 3: Dựa trên các số thích nghi, thực hiện việc sinh sản và  tiến hoá  (gồm:  lai

ghép và đột biến) các giải pháp.

- Bước 4: Tính số thích nghi cho các giải pháp mới sinh sản, loại bỏ giải pháp kém

nhất, chỉ giữ lại một số nhất định các giải pháp (có độ thích nghi cao). 

- Bước 5: Nếu chưa tìm được giải pháp tối ưu hoặc chưa đến thời hạn (hay số thế hệ)

ấn định thì trở lại bước 3 để tìm giải pháp mới.

- Bước 6: Nếu tìm được giải pháp tối ưu hay hết thời hạn ấn định thì dừng và xuất

kết quả.

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  19  

* Các phương pháp tiến hoá của GA: sinh sản (Reproduction), lai ghép (hay lai chéo,

Crossover), đột biến (Mutation). So với lai ghép, tần suất đột biến xảy ra ít hơn nhiều

vì quá trình này tạo ra thông tin hoàn toàn mới.

 * Thuật giải GA

  Thuật_Giải_Di_ Truyền

  { t =0;

    Khởi tạo lớp P(t);

    Đánh giá lớp P(t);

    while (not(Điều kiện kết thúc)) do

    { t = t + 1;

  Chọn lọc P(t) từ P(t-1);

  Kết hợp các cá thể của P(t);

   Đánh giá lớp P(t);

    }

  }

 * Ví dụ 10: Giải phương trình sau trên tập số tự nhiên: x2

 = 64.

    . Bước 1:  - Xác định số lượng cá thể (giải pháp, biến x): 4

   - Dùng dãy ký hiệu nhị phân {0, 1} để biểu diễn mỗi biến

   . Bước 2: - Chỉ định các cá thể: 4, 21, 10, 24

  - Biểu diễn đáp số: dùng 5 bits 

STT  Thập phân  Nhị phân

1  4  00100

2  21  10101

3  10  01010

4  24  11000

2

    . Bước 3:  - Chọn hàm số thích nghi: F(x) = 1000 – |x  – 64|≥0, ∀x∈[-32..31]

- Tính độ thích nghi phù hợp cho đáp số ≈ 1000 

STT  Thập phân  Nhị phân  HS thích nghi  Chọn

1  4  00100  1048  X

2  21  10101  623  

3  10  01010  964  X

4  24  11000  488  

    . Bước 4: - Chọn các đáp số (có độ thích nghi gần phù hợp): 4, 10

- Tiến hoá: lai ghép ở vị trí thứ 3: 

          

   . Bước 5: Tính độ thích nghi cho giải pháp mới: 

STT  Thập phân  Nhị phân  HS thích nghi  Chọn

1  6  00110  1028  

2  21  10101  623  

3  8  01000  1000  X

4  24  11000  488  

4    →   0  0  1  0  0

10  →   0  1  0  1  0

6    →   0  0  1  1  0

8  →   0  1  0  0  0   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  20  

    . Bước 6: x = 8 có độ thích nghi 1000: phù hợp → Dừng.

    . Bước 7: Đáp số: x = 8.

      II.2.3. Nguyên lý mê cung 

* Vét cạn bằng cách quay lui: Để tránh tràn bộ nhớ vì không gian D quá lớn,

ta có thể xác định dần các thành phần của lời giải bài toán mặc dù các lời giải có

cấu trúc không tuyến tính phức tạp. Lời giải xLG = (x0, x1, …) được xây dựng dần

(xuất phát từ x0  ∈ X0) trong quá trình giải cho  đến khi gặp  điều kiện kết thúc

P(xLG). 

Các thành phần của lời giải được lưu dần vào tập DONG (có cấu trúc stack).

Khi đó, tại mỗi thời điểm đang xét, phần tử ở đỉnh của DONG chính là phần tử

cuối của đường đi có khả năng dẫn đến lời giải. Vì vậy, ta có thể kiểm tra điều

kiện kết thúc P(DONG)  bởi  Top(DONG) ∈ DICH.

Dưới dạng đệ qui, ta có các thuật toán sau để giải BT1 và BT2.

* Thuật toán vét cạn V2.1 (dưới dạng đệ qui giải BT1) 

{   for each x ∈ X0  do

{DONG = {x}; Try_1(DONG);

}

  }

Try_1(DONG)     

{ // điều kiện nhận biết trạng thái kết thúc của một lời giải    (*)

   if (P(DONG)) // hay (Top(DONG) ∈ DICH) 

        XuấtLờiGiải(DONG);     

              else  for each x ∈ B(Top(DONG))

         if (Q(x)) // nếu có yêu cầu thêm về tính chất của lời giải, chẳng hạn 

// (x∉DONG) khi đòi hỏi các thành phần của lời giải không trùng lặp

     {  Push (x, DONG);

         Try_1(DONG);

         Pop(y, DONG); // Trả lại trạng thái trước để quay lui

     }

}

trong đó: Push(x,DONG), Pop(x,DONG)  và  x = Top (DONG)  lần lượt là

các thao tác đưa vào, lấy ra và xem một phần tử x ở đỉnh ngăn xếp DONG;

B(x) (⊂ D) là tập các trạng thái kế tiếp có thể lấy từ x để xét tiếp.

Nếu tập xuất phát X0 trong bài toán chỉ gồm một điểm x0 thì để thi hành

thuật toán, ta chỉ cần gọi Try_1({x0}).

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  21  

  Để đưa ra  thuật toán V2.2 giải BT2,  ta chỉ cần  thay điều kiện nhận

biết trạng thái kết thúc một lời giải (*) bởi điều kiện dừng chương trình sau

đây:

if (P(DONG)) // hay (Top(DONG) ∈ DICH)

        { XuấtLờiGiải(DONG);     

    Dừng;     // điều kiện dừng

  }

Thuật toán vét cạn kiểu đệ qui trên đây vét hết mọi khả năng nhưng

tại mỗi bước chỉ lưu một khả năng, những khả năng còn lại được lưu dần

trong ngăn xếp thông qua cơ chế đệ qui. Nếu biểu diễn không gian tìm kiếm

của bài toán dưới dạng đồ thị hay cây thì thuật toán vét cạn trên đây thực

chất là thủ tục tìm kiếm theo chiều sâu.

Trong trường hợp lời giải là vectơ hữu hạn chiều (chẳng hạn, kích thước được

biết trước là cố định hữu hạn), ta có phiên bản đơn giản và hiệu quả sau đây thường gặp

trong các tài liệu tin học trước đây.

  Thủ tục vét cạn Try trong trường hợp cấu trúc rời rạc tuyến tính đơn giản

TryRờiRạc(j: integer)

{  for (k thuộc tập các khả năng) do

  if  (chấp nhận khả năng thứ k)

  { Xác định xj theo khả năng thứ k;

    Đánh dấu (đã xét) trạng thái mới;

     if (xj là trạng thái kết thúc)  // hay thoả điều kiện kết thúc

         XuấtLờiGiải(x);

     else TryRờiRạc(j+1);

     Trả lại trạng thái cũ;   // Bỏ việc đánh dấu trạng thái cũ

  }

}

* Ví dụ 11: Tìm các đường đi từ điểm xuất phát đến cửa ra của mê cung trên hình II.1. Ta

biểu diễn mê cung dưới dạng ma trận kề a[j,k] =1 hay 0 nếu có hay không có đường đi từ

j đến k tương ứng, với số đỉnh n=20.

Gọi thủ tục TryRờiRạcMêCung(1) sau đây để  giải bài toán mê cung.

TryRờiRạcMêCung (SoDinh)  // trước đó gán x[1] = XuấtPhát = 1

{  int k;

     for (k=1; k <= n; k++) 

       // nếu có đường đi từ đỉnh x[SoDinh] đến đỉnh k và chưa đi qua k

  if  (a[x[SoDinh], k]==1 && DaDiQua[k]==0)

  {  x[SoDinh+1]=k; 

    DaDiQua[k] = 1;  // Đánh dấu đã đi qua k

     if (x[SoDinh+1] == Cửa_ra)   

         XuấtLờiGiải(x, SoDinh+1);

     else TryRờiRạcMêCung (SoDinh+1);

     DaDiQua[k] = 0;  // Bỏ việc đánh dấu trạng thái cũ   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  22  

  }

}

   15      Cửa ra=20

16  17   19

13  14  10 11 

    12 9 18

8  5 2 Xuất phát=1  

    6 

  7  4 3

(Hình II.1)

II.2.4. Các phương pháp biểu diễn và giải quyết vấn đề trong không gian

trạng thái bằng cây và đồ thị

Biểu diễn vấn đề trong không gian trạng thái a.   

Trước tiên, ta xét hai ví dụ. 

- Ví dụ 12 (bài toán Toci hay  trò chơi n2

 - 1 số,  n là số tự nhiên, n > 2):

Trong bảng ô vuông n hàng, n cột, mỗi ô chứa một số nguyên từ 1 đến n2

 -1 sao

cho không có hai ô có cùng giá trị. Chỉ được có một ô trong bảng bị trống (ta có

thể gán trị 0). Xuất phát từ một cách sắp xếp x0 nào đó các số trong bảng, hãy dịch

chuyển ô trống sang phải, trái, lên, xuống (nếu có thể được) để đưa về bảng mục

tiêu x* như sau: 

   x     x*          0

28 3

16 4

7 5    

12 3

8 4

76 5 

(Trò chơi 8 số, khi n=3)

  Để giải bài toán này, ta biểu diễn nó trong không gian trạng thái S. Mỗi

trạng thái là một ma trận cấp  n x n  nhận các giá trị nguyên từ  0 đến n2

 -1 (trị  0

thay cho vị trí trống trên bảng) sao cho không có hai phần tử khác nhau có cùng

trị, mỗi toán tử  o là một phép dịch chuyển hợp lệ từ bảng này sang bảng khác. Số

trạng thái chấp nhận được khá lớn: khoảng  (1/2)*16!  ≈ 10,5*1012

 (khi n=4).  Một

cách biểu diễn trực quan đối với không gian trạng thái và các toán tử là đồ thị.

Trong đồ thị định hướng này các đỉnh tương ứng với các trạng thái, còn các cung

tương ứng với các toán tử. Ta cần xây dựng dần các toán tử, bắt đầu từ các toán tử

có thể áp dụng cho trạng thái đầu, sau đó ở mỗi bước thêm vào một toán tử hợp lệ

nào đó cho đến khi đạt được trạng thái đích.

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  23  

  Điểm mấu chốt khi giải quyết bài toán trong không gian trạng thái là lựa

chọn một dạng mô tả nào đó của các trạng thái phù hợp với bản chất vật lý của bài

toán. Ta cần biểu diễn các trạng thái sao cho việc áp dụng các toán tử biến đổi

trạng thái trở nên đơn giản hơn.

 * Có hai cách biểu diễn: tập O  các toán tử biến đổi trạng thái

hoặc tập P  những luật sinh để chuyển trạng thái:

- Gọi O  là tập các hàm o xác định và nhận trị trên không gian trạng thái S:

o : S → S

 Với ví dụ trên, tập các toán tử chuyển  đổi trạng thái  O gồm 4 toán tử

O={o , o len xuong, otrai, ophai}. Chẳng hạn, toán tử dịch chuyển vị trí ô trống lên trên

o  được xác định như sau:  len

   o (A) = B    len

trong đó:

], A = [a ], giả sử ô trống trong ma trận A  ở vị trí (i       . B = [bij ij 0, j0 )

          aio-1, jo    nếu (i, j) =  (io, jo) , io   > 1      

     . bi, j =    aio, jo       nếu (i, j) = (i -1, j o  o)  , io   > 1 

         ai , j             nếu ngược lại        

        - Gọi P là tập các luật sinh (production rules) p : S ik i ⇒  Sk để chuyển từ  trạng

thái Si  đến trạng thái Sk

Với ví dụ trên (với n=3),  ta có các luật sinh sau: 

x1     x2     x3  x1     x2    x3

 

         x4     x5  x4             x5

P1

 

P2

 

P3

 

P4

x6     x7    x8  x6      x7    x8

x1     x2    x3  x1     x2     x3

x4             x5  x4     x7     x5

x6     x7    x8  x6              x8

x1     x2    x3  x1     x2    x3

x4             x5  x4     x5    

x6     x7    x8  x6      x7   x8

x1     x2    x3  x1              x3

x4             x5   x4     x2    x5

x6     x7    x8  x6      x7    x8   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  24  

x1     x2    x3            x2    x3

 

          x4   x5   x1     x4    x5

P5

 

P6

 - Nhận xét:

   . Với cách biểu diễn dùng các toán tử có biểu diễn tổng quát: số các toán

tử ít (4, với bài toán n2

 – 1 số) , rất gọn và dễ cài đặt.

. Với cách liệt kê dưới dạng các luật sinh  tuy trực quan nhưng số lượng

quá lớn (4(n2

 – n), với bài toán n2

 – 1 số). Ta thường dùng cách liệt kê này

với các bài toán mà phép chuyển đổi trạng thái rất khó khái quát.

- Ví dụ 13 (bài toán tháp Hà Nội): Cho 3 cọc 1, 2, 3. Ở cọc 1 ban đầu có n

đĩa sắp xếp theo thứ tự đĩa lớn ở dưới và đĩa nhỏ ở trên. Hãy dịch chuyển n đĩa đó

sang cọc 3 sao cho: mỗi lần chỉ chuyển 1 đĩa, trong mỗi cọc không chấp nhận đĩa

lớn nằm trên đĩa nhỏ. Với n=3, ta có:

         1         2         3       1    2    3

   A3               A3 

 A2                 A2  

          A1         A1 

 

Với bài toán này, ta có thể biểu diễn mỗi trạng thái là bộ ba (i, j, k) để mô tả

đĩa A1 ở cọc i, đĩa A2 ở cọc j, đĩa A3 ở cọc k. Khi đó, các toán tử dịch chuyển trạng

thái sau là hợp lệ:

 (i, j, k)   →     (i, j, j) , k ≠ j

 (i, j, k)   →     (i, j, i) , k ≠ i

       . . . 

 

 * Để biểu diễn bài toán trong không gian trạng thái, cần xác định rõ:

  - Không gian S biểu diễn các trạng thái.

  - Tập O  các toán tử biến đổi trạng thái hoặc tập P các luật sinh.

  - Trạng thái đầu xo ∈ S và tập các trạng thái đích DICH ⊂ S

x6     x7    x8  x6      x7    x8

x1     x2    x3  x1     x2     x3

         x4    x5  x6      x4    x5

x6     x7    x8            x7    x8   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  25  

* Một cách hình thức, ta có thể phát biểu bài toán tìm kiếm trong không

gian trạng thái dưới 3 dạng tương đương như sau: Cho trạng thái đầu xo ∈ S

và tập các trạng thái đích DICH ⊂ S.

Dạng 1 (toán tử)   : Problem(S, O, xo, DICH)

Hãy tìm:

. dãy trạng thái xo,..., x sao cho x n  n  ∈  DICH và có thể áp dụng dãy các toán

tử biến đổi trạng thái o ∈ O  nào đó  để chuyển từ x i  i-1   đến xi

o : x i  i-1  → xi  ∀ i = 1, 2, ..., n

 . hoặc dãy toán tử o1 ,..., on ∈ O :  on (o  (...o n-1 1 (xo )...)) ∈ DICH

Dạng 2 (luật sinh)   : Problem(S, P, xo, DICH)

Hãy tìm:

. dãy trạng thái xo,..., x sao cho x n  n  ∈  DICH và có thể áp dụng dãy các luật

sinh pi ∈ P  nào đó  để chuyển từ xi-1   đến xi

p : x ⇒ x i  i-1  i ∀ i = 1, 2, ..., n

. hoặc dãy luật sinh p1 ,..., pn  ∈ P sao cho: 

   p                 p 1  n

           xo ⇒ x1 ⇒   ...   ⇒ xn  ∈ DICH

Nếu không gây ra nhầm lẫn ta có thể dùng ký hiệu Problem(xo, DICH).

Dạng 3 (đồ thị)   :

  Đồ thị định hướng  G  là cặp  G =  (S, A), trong đó S là tập các đỉnh, A là

tập các cung: A  =  {(a, b)/  a, b ∈ S}  ⊂  S x S.

 Với mỗi n ∈ S, ta ký hiệu tập các  đỉnh con của n là: B(n) =  {con ∈ S:  

(n,con) ∈ A}. Một cách tổng quát, ta định nghĩa:  

B1

(n)  ≡  B(n)

1k),m(B ))n(B(B)n(B

)n(Bm

1k k

1k

> = ≡ −

U

  Khi đó:  

)n(B)n(B i

1i

=

≡ U

được gọi là tập các đỉnh hậu duệ của n và nếu  m ∈ B(n) thì  n  được gọi là tổ tiên

của m. Dãy các đỉnh  p ={n1,..., nk} sao cho: ∀ i=1,..., k-1, a = (n i  i , ni+1 )∈A   được

gọi là đường đi từ  n  đến n hay còn có thể biểu diễn bởi: p ={a  ,..., a },  với  a 1  k  1  k-1 i  

=  (ni , ni+1  ). Nếu tồn tại một đường đi từ đỉnh n đến đỉnh m   thì  m ∈ B(n) và

ngược lại. Khi đó ta còn nói rằng m có thể đạt được từ  n. 

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  26  

 Khi đồ thị G có đỉnh gốc no∈S và ∀ n ∈ S\{no} tồn tại duy nhất đường đi từ

no đến n (∀ n ∈ S\{no}, n∈B(no) và ∃! m∈ S, n∈ B(m)) thì G được gọi là cây với

gốc no.

 Thông thường, người ta thêm vào mỗi cung một đại lượng thể hiện ý nghĩa

được lượng hóa của nó thông qua hàm giá c: A → R , c(n + i, nk) ∈ R , ∀(n + i, nk) ∈A.

Khi đó, ta định nghĩa giá của đường đi  p = {n1, ..., nk}:  

=

+ =

1

1

1 ),()(

k

i

ii

nncpc

Trường hợp  c ≡ 1 thì c(p) được gọi là độ dài đường đi của p.

 Theo ngôn ngữ  đồ thị,  không gian trạng thái tương  ứng với  đồ thị  định

hướng trong  đó: các trạng thái tương ứng với các  đỉnh trong  đồ thị và có một

cung nối từ trạng thái s đến trạng thái t nếu tồn tại toán tử o sao cho o(s) = t 

(hoặc tồn tại luật sinh p sao cho p: s⇒ t).

 * Bài toán tìm kiếm trong không gian trạng thái có thể phát biểu dưới dạng

đồ thị như sau:

 

- Bài toán 3: Problem_3(G = (S, A), x0, DICH)    (BT3)

Cho đồ thị G = (S, A), với đỉnh xuất phát x0∈ S, tập đích DICH ⊂ S.

Hãy tìm một đường đi từ x0 đến một đỉnh nào đó thuộc tập DICH.

Bài toán 3* -  : (bài toán tìm kiếm tối ưu)

Problem_3*(G = (S, A), x0, DICH, c)            (BT3*)

Cho đồ thị G = (S, A), với hàm giá  c: A → R , đỉnh xuất phát x + 0∈ S

và tập đích DICH ⊂ S. Hãy tìm một đường đi từ x0 đến một đỉnh nào đó

thuộc tập DICH và làm tối ưu hàm giá.

Từ tập các cung A, ta có thể xây dựng các thuật toán tìm kiếm lời giải dựa

trên toán tử B(x), ∀x ∈ S. Trong nhiều bài toán thực tế, B(x) chính là tập các

trạng thái kế tiếp hợp lệ từ x. Khi không gian trạng thái S quá lớn, lời giải xLG =

⎨x0, x1, …⎬  thường được xây dựng dần trong quá trình giải cho đến khi gặp điều

kiện kết thúc P(xLG) (hay P’(x*): khi tìm  được trạng thái mục tiêu x*  ≡

TrạngTháiCuối(xLG) ∈ DICH ứng với BT3 hoặc thêm một điều kiện nào đó, chẳng

hạn tối ưu một hàm mục tiêu hay hàm giá ứng với BT3*).

* Hai phương pháp xây dựng đồ thị  G = (S, A)   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  27  

        - PP tường minh: tập các nút S và tập các cung A đã biết và được xây dựng

trước. Thông thường, đối với các đồ thị hữu hạn ứng với các bài toán đơn giản và

có kích thước nhỏ mới có thể biểu diễn tường minh chúng dưới dạng bảng.  

- PP không tường minh: xuất phát từ đỉnh ban đầu x0,  trong quá trình tìm

kiếm lời giải, xây dựng dần các đỉnh con dựa trên toán tử B(n) để tạo ra các đỉnh

con của n. Nghĩa là chỉ khi nào xét đến đỉnh n, tập các đỉnh con n mới được xây

dựng. Phương pháp này rất có ý nghĩa,  đặc biệt là đối với các đồ thị biểu diễn

những bài toán phức tạp và có kích thước lớn. 

Nếu  xét BT3 dưới dạng BT1 thì D là một tập con của tập Un≥1Sn

.Trong

những bài toán  lớn và phức tạp, do khó xác định tập D ngay từ đầu hoặc có thể

xác định D nhưng kích thước của nó quá lớn, việc dùng các  thuật toán vét cạn

V1.a hay V1.b là không hiệu quả. Đối với lớp các bài toán mà các thành phần của

lời giải có thể xác định dần trong quá trình giải, ta sẽ cải biên các thuật toán đơn

sơ V1  để  thu  được dần các thuật toán hiệu quả hơn  trong phần tiếp theo như:

TìmKiếm, AT

, AKT

, A*, …

 Trong các thuật toán hay thuật giải tìm kiếm lời giải sau đây, ta dùng tập

DONG để lưu các trạng thái đã xét, tập MO dùng để lưu các trạng thái dự định sẽ

xét trong các bước kế tiếp.

b.  Phương pháp tìm kiếm lời giải cho BT3 (dưới dạng lặp) 

Trong phần này, không có gì khó khăn, ta đưa ra thuật toán tìm kiếm lời

giải cho bài toán mở rộng của BT3 như sau: Cho X0 (⊂ S) là tập những trạng thái

có thể xuất phát. Thay vì tìm đường đi từ x0 đến DICH, ta tìm đường đi mà có thể

xuất phát từ một trong các trạng thái x ∈ X0  đến DICH. Khi đó, để giải BT3 ta chỉ

cần gọi: TìmKiếm({x0}, DICH).

* Thuật toán tìm kiếm: (Giải BT3’: Problem_3’(G = (S, A), X0, DICH))

TìmKiếm(X0, DICH)

⎨    MO = X0; DONG = ∅;  // tập DONG có cấu trúc stack

if (∃y ∈ X0: P’(y))   // hay if (∃y ∈ MO ∩ DICH) 

⎨XuấtLờiGiải(y, DONG); Dừng;⎬ 

while (MO ≠ ∅)

⎨ x = get(MO);   // lấy một phần tử  x ra khỏi tập MO 

   DONG  = DONG U ⎨x⎬; // đưa x vào tập DONG

   if (∃y ∈ B(x) : P’(y) đúng)   // hay if (∃y ∈ B(x)∩DICH) 

⎨XuấtLờiGiải(y, DONG); Dừng;⎬

   else MO = MO U B(x); // đưa tập đỉnh con B(x) của x vào tập MO

write (“Không có lời giải”);   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  28  

  ⎬

 

Từ đây về sau, để gần hơn với dạng cài đặt thành chương trình máy tính,  ta

qui ước: Remove(x, MO) là thao tác rút phần tử  x khỏi đầu hàng đợi MO, Add(x,

MO) là thao tác thêm phần tử  x vào đuôi hàng đợi MO, AddSet(B(x),MO) là thao

tác thêm tập B(x) vào đuôi hàng đợi MO; Push(x, MO) là thao tác thêm phần tử  x

vào đầu (hay đỉnh) ngăn xếp MO, PushSet(B(x), MO) là thao tác thêm tập B(x) vào

đỉnh ngăn xếp MO, Pop(x, MO) là thao tác rút x khỏi  đỉnh ngăn xếp MO, x =

Top(MO) là hàm trả lại (nhưng không lấy ra khỏi) phần tử ở đầu danh sách MO.

 

- Thuật toán XuấtLờiGiải(y, DONG) cho cây G (tổng quát hơn cho đồ thị mà mỗi nút có

không quá một nút cha), với tập DONG có cấu trúc stack, dưới dạng đường đi từ x0 đến y được lưu

trong ngăn xếp LờiGiải như sau:

  XuấtLờiGiải(y, DONG)

  { LờiGiải = {y};

    while (not(EmptyStack(DONG)))

   { Pop(x, DONG);

// nếu có cung nối từ x đến phần tử ở đỉnh ngăn xếp LờiGiải thì đưa // x vào thành

phần LờiGiải

  if ((x, Top(LờiGiải)) ∈ A) // hay Top(LờiGiải) ∈ B(x)

Push(x, LờiGiải);

    }

     ShowStack(LờiGiải); // xuất các phần tử của stack LờiGiải

  }

Dựa trên thuật toán  TìmKiếm  nhằm tìm lời giải  đầu tiên cho bài toán

BT3, ta có thể cải biên để thu được thuật toán vét cạn dưới dạng lặp VétCạn

để tìm mọi lời giải của bài toán sau (bài tập):

- Bài toán 4: Problem_4(G = (S, A), x0, DICH)    (BT4)

Cho đồ thị G = (S, A), với đỉnh xuất phát x0∈ S, tập đích DICH ⊂ S.

Hãy tìm mọi đường đi từ x0 đến một đỉnh nào đó thuộc tập DICH.

c.  Các dạng đặc biệt thường gặp: Các thuật toán tìm kiếm lời giải trong

không gian trạng thái theo chiều rộng, chiều sâu, chiều sâu dần, tìm

kiếm cực tiểu giá thành AT

.

Để giải BT3 trên cây G, trong thuật toán TìmKiếm, với các cách chọn cấu

trúc dữ liệu khác nhau của tập MO, ta sẽ có các phương pháp tìm kiếm khác nhau.

Sau đây, ta luôn xem x = Get(MO)  là thao tác lấy phần tử x ra khỏi đầu danh sách

MO.

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  29  

* Phương pháp tìm kiếm theo  chiều rộng (TKR):  chính là  thuật toán

TìmKiếm, với tập MO có cấu trúc hàng đợi (queue) hay  thay MO = MO U B(x)

bởi phép toán AddSet(B(x),MO).

- Nhận xét: Khi G là cây với gốc x0,

. nếu tồn tại ít nhất một đường đi từ xo tới tập DICH thì thuật toán

TKR dừng và cho ta đường đi p có độ dài ngắn nhất (thậm chí khi G là cây

vô hạn);

. nếu không tồn tại đường đi như vậy thì thuật toán dừng nếu và chỉ

nếu cây G là hữu hạn.

 - Ví dụ 14: Xét cây như  hình dưới đây  với  tập DICH = {H}. Thủ tục TKR

cho kết qủa là đường đi ADH theo hành trình duyệt các đỉnh ABCDEFGH.

          A    

         B  C  D       

        

            E  F    G     H

(TKR)

    

* Phương pháp tìm kiếm theo  chiều sâu (TKS):  chính là  thuật toán

TìmKiếm, với tập MO có cấu trúc ngăn xếp (stack), hay  thay MO = MO U B(x)

bởi phép toán PushSet(B(x),MO).

- Nhận xét: Khi G là cây với gốc x0,

. nếu cây G hữu hạn thì thủ tục TKS sẽ dừng và nếu tồn tại đường đi

từ x0 đến DICH thì sẽ cho kết qủa là một đường đi từ xo tới tập DICH, khi

đó  đường  đi  nhận  được trong thủ tục TKS  không nhất thiết là  đường  đi

ngắn nhất. Hơn nữa, nếu cây G vô hạn, thủ tục TKS có thể lặp đến vô hạn,

thậm chí trong trường hợp tồn tại đường đi từ  xo   đến tập DICH.

 . nếu  không tồn tại  đường  đi  từ x0  đến DICH thì thủ tục TKS chỉ

dừng khi cây G  hữu hạn.

 - Ví dụ 15: Xét cây như  hình dưới đây  với  tập DICH = {D, H}. Thủ tục

TKS cho kết qủa là đường đi AD theo hành trình duyệt các đỉnh ABCEFD.

          A    

         B  C  D       

        

            E  F    G     H

(TKS)

        Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  30  

  Để khắc phục tình trạng không dừng của thuật toán TKS ngay cả khi tồn tại

đường đi từ gốc của cây đến DICH, ta đưa vào đại lượng DS đặc trưng cho giới

hạn sâu và dùng khái niệm độ sâu d(x) của đỉnh x trong cây G = (S, A) được định

nghĩa đệ qui như sau:

 d(xo) = 0

 d(con) = d(cha) + 1,   nếu  con ∈ B(cha) hay (cha, con) ∈ A.

* Phương pháp tìm kiếm sâu dần (TKSD) giải BT3 với độ sâu DS = k≥ 1:

điều chỉnh lại thuật toán TìmKiếm, với tập MO được trang bị các phép toán cơ

bản của cả hàng đợi lẫn ngăn xếp.

TKSD(x0, DICH, k)

⎨  MO = ⎨x0⎬; 

DONG = ∅;     // chọn DONG có cấu trúc stack

      DS = k; 

if (P’(x0))    // hay if (x0 ∈ DICH) 

⎨XuấtLờiGiải(x0, DONG); Dừng;⎬ 

while (MO ≠ ∅)

⎨ x = get(MO);    // lấy phần tử  x từ đầu danh sách MO 

   Push(x, DONG);   // đưa x vào tập DONG

   if (∃y ∈ B(x)∩DICH)  ⎨XuấtLờiGiải(y, DONG); Dừng;⎬

         if (d(x) > DS) DS = DS + k; // khi  đó d(x) <= DS

         if (d(x) < DS) PushSet(B(x), MO);// tìm theo chiều sâu

          else AddSet(B(x), MO);      // d(x) = DS: việc tìm được lan theo chiều rộng 

write (“Không có lời giải”);

  ⎬

- Nhận xét: Khi G là cây với gốc x0,

. với  k = 1, thủ tục  TKSD trở thành thủ tục TKR. Khi  k rất lớn

(chẳng hạn k ≥ chiều cao của cây G hay k → ∞ đối với cây vô hạn) thủ tục 

TKSD sẽ giống hay gần giống tương ứng như thủ tục TKS;

. với k ≥ 2 thủ tục TKSD tìm kiếm theo chiều sâu đối với các đỉnh có

độ sâu nằm trong khoảng từ  tk  đến  (t+1)k  với t = 0, 1, 2, ...;

. nếu tồn tại ít nhất một đường đi từ gốc tới DICH thì thủ tục TKSD

sẽ  dừng  và cho kết qủa là  đường  đi  có  độ dài khác  đường  đi ngắn nhất

không quá k-1;

. nếu trong cây  không tồn tại đường đi như vậy thì thủ tục TKSD

dừng khi và chỉ khi cây G là hữu hạn.

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  31  

 - Ví dụ 16: Xét thủ tục TKSD trên cây như hình dưới đây, với độ sâu k = 2,

thứ  tự duyệt các đỉnh là : ABDECFG__HQRKSTLMUVNXYOP 

          A 

          B  C      

              D          E      F        G

      H       K      L    M  N   O  P

                  Q        R   S    T       U    V  X  Y

(TKSD)

* Phương pháp tìm kiếm cực tiểu AT

: Giải bài toán tối ưu BT3* trên cây G

- Problem_3*(G = (S, A), x0, DICH, c) - với tiêu chuẩn tối ưu: tìm một đường đi từ 

x0 đến một trong các trạng thái thuộc tập DICH  có hàm giá đường đi g0

cực tiểu. 

Trong đó, hàm giá đường đi g0

 được xác định theo kiểu đệ qui như sau: 

g0

(x0) = 0 

g0 0

(cha) + c(cha, con),  ∀ (cha, con) ∈ A.  (con) = g

Thuật toán sau đây tương tự như thuật toán TìmKiếm, trong đó: tập DONG

có cấu trúc stack và tập MO chứa các phần tử  (x, g0

(x)), thao tác lấy ra một phần

tử  được thực hiện ở đầu danh sách MO, việc đưa một phần tử  x vào danh sách

MO ChènTăng((x, g0

(x)), MO) được thực hiện theo kiểu chèn (x, g0

(x)) vào MO

tăng dần theo g0

()  từ  đầu đến cuối danh sách MO. 

T

A (x0, DICH)

⎨  DONG = ∅;     // DONG có cấu trúc stack

g0

(x0)=0; MO = ⎨(x0, g0

(x0))⎬;  

while (MO ≠ ∅)

⎨ Pop((x, g0

(x)), MO);   // trong MO, x ở đỉnh và có g0

(x) nhỏ nhất !

   if (x ∈ DICH) ⎨XuấtLờiGiải(x, DONG); Dừng; ⎬

   Push(x, DONG);    // đưa x vào đỉnh stack DONG

//ChènTăng(y,MO) ∀y∈B(x)={(con,g0 0 0

(con)):(x,con)∈A & g (con)=g (x)+c(x, con)}

  ChènTăngTập(B(x), MO);   

write (“Không có lời giải”);

  ⎬

 - Nhận xét: 

     . Thủ tục TKR là trường hợp riêng của thủ tục AT

 khi hàm giá c≡1.

T

    . Thủ  tục TKS cũng là trường hợp riêng của thủ tục A  khi: hoặc thay

thao tác ChènTăngTập(B(x), MO) bởi ChènGiảmTập(B(x), MO); hoặc xem

hàm giá c: A → R  và c ≡ -1.   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  32  

. Nếu trong cây G tồn tại đường đi  p: xo → DICH thì thủ tục AT

 sẽ dừng

và cho kết qủa là đường đi po sao cho c(po)  → min, nếu: hoặc cây G hữu

hạn, hoặc cây G vô hạn nhưng tổng giá theo mọi nhánh vô hạn phải phân

kỳ hoặc hội tụ về một giá trị lớn hơn giá tối ưu ! 

 - Ví dụ 17: Xét cây và giá các cung được cho như hình dưới đây và  tập 

DICH = {D, H}. Thủ tục tìm kiếm cực tiểu AT

 cho kết qủa là đường đi AFH.

          A    

           4  3     6

         B  C    F       

        5 2      7   1  

            D  E    G     H

(Thuật toán AT

)

- Chú ý: Trong thuật toán AT

, ta chọn phương án tối ưu nhất  trong  tất cả

những phương án đã đi qua (được lưu lại để quay lui khi lạc hướng). Chính vì thế,

với các bài toán lớn, có thể xảy ra tình trạng nhanh tràn bộ nhớ nếu các bước kế

tiếp cần lưu quá nhiều; khi đó ta có thể dùng heuristic sau: thay vì lưu hết 100%

các phần tử B(x) vào tập MO, ta chỉ cần lưu p%, 1≤ p≤ 100, mà thôi ! Giá trị của

p phụ thuộc vào từng bài toán cụ thể và tài nguyên máy tính hiện có.

 II.2.5. Quy bài toán về bài toán con và các chiến lược tìm kiếm trên đồ thị

VÀ/HOẶC

a. Qui bài toán về bài toán con

 * Ý tưởng: dựa trên ý tưởng của phương pháp chia để trị, tách bài toán lớn

và phức tạp ban đầu thành những bài toán con nhỏ, đơn giản tới mức sơ cấp (lời

giải của chúng đã biết).

 - Ví dụ 18: Xét bài toán tháp Hà nội với n = 3 và dùng cách biểu diễn như

trong ví dụ 2 phần 2.2.4.a. Ta qui bài toán về các bài toán con dạng VÀ như sau:

           (111)→(333) 

                    (111)→(122)        (122)→(322)      (322)→(333)

(Chuyển 2 đĩa A,B từ cọc 1 sang cọc 2)

 (111)→(113)     (113)→(123)  (123)→(122)                (322)→(321)      (321)→(331)     (331)→(333)

              (Hình II.2.5.1)   

 Liên kết những lời giải sơ cấp trên từ trái sang phải, ta có lời giải của bài

toán ban đầu:

(111)→(113)→(123)→   (122)→(322)  →(321)→(331)→(333).

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  33  

  Cây bình thường là cây HOẶC, dạng  đặc biệt của cây VÀ/HOẶC. Cây

VÀ/HOẶC thường  được dùng  để  biểu diễn quá trình chia bài toán lớn thành

nhiều nhóm các bài toán con được nối kết lôgic với nhau bởi dãy các phép toán

lôgic: and, or.

b. Biểu diễn bài toán dưới dạng đồ thị VÀ/HOẶC

  Đồ thị định hướng VÀ/HOẶC là đồ thị định hướng thông thường  G = (S,

A) và thêm vào tính chất: với mỗi đỉnh n ∈ S tất cả các đỉnh con của nó B(n) cùng

thuộc vào một trong 2 kiểu: đỉnh VÀ hay đỉnh HOẶC. Khi các đỉnh con m của n

là đỉnh VÀ thì các cung nối các đỉnh con của nó m ∈ B(n) được nối với nhau bởi

ngoặc tròn (như dấu góc tròn trong hình học phẳng). Sự tương ứng giữa quá trình

qui bài toán về bài toán con với đồ thị VÀ/HOẶC được cho trong bảng sau:

Qui bài toán về bài toán con Đồ thị VÀ/HOẶC      

 Bài toán      Đỉnh 

 Toán tử qui bài toán về bài toán con  Cung

 Bài toán ban đầu     Đỉnh gốc (đỉnh xuất phát)

 Bài toán sơ cấp      Đỉnh lá 

 Các bài toán con phụ thuộc    Đỉnh con dạng VÀ

 Các bài toán con độc lập    Đỉnh con dạng HOẶC

 Lời giải bài toán     Đồ thị con lời giải

 Giải bài toán     Tìm đồ thị con lời giải

  Định nghĩa đỉnh giải được (đỉnh gđ):

 - Các đỉnh lá tương ứng với bài toán con  sơ cấp giải được  (hay đỉnh kết

thúc) là đỉnh giải được.

 - Nếu đỉnh n có các đỉnh con là đỉnh HOẶC thì nó giải được khi và chỉ khi

tồn tại một đỉnh con của nó giải được.

 - Nếu đỉnh n có các đỉnh con là đỉnh VÀ thì nó giải được khi và chỉ khi mọi

đỉnh con của nó giải được.

  Đồ thị lời giải là đồ thị con của đồ thị VÀ/HOẶC chỉ bao gồm đỉnh xuất

phát và các đỉnh giải được liên quan đến nó.

Định nghĩa đỉnh không giải được    (đỉnh kgđ):

 - Các đỉnh lá tương ứng với bài toán con  sơ cấp không giải được  là đỉnh

không giải được.

 - Nếu đỉnh có các đỉnh con là đỉnh VÀ thì nó là không giải được khi và chỉ

khi  tồn tại một đỉnh con của nó không giải được.

 - Nếu đỉnh có các đỉnh con là đỉnh HOẶC thì nó là không giải được khi và

chỉ khi  mọi đỉnh con của nó không giải được.

 - Ví dụ 19: Trên hình II.2.5.2  là các đồ thị VÀ/HOẶC, trong đó các đỉnh

giải được tương ứng với các hình tròn tô đậm và các cung trong đồ thị con lời giải

cũng được tô đậm, các đỉnh không giải được được đánh dấu bởi vòng tròn.   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  34  

 - Nhận xét: Khi đồ thị VÀ/HOẶC không có đỉnh VÀ thì nó trở thành đồ thị

thông thường. Lúc đó đồ thị lời giải suy biến thành một đường đi từ đỉnh xuất phát

đến một  đỉnh kết thúc nào  đó.  Mục  đích của quá trình tìm kiếm trên  đồ thị

VÀ/HOẶC là chứng tỏ đỉnh xuất phát có thể giải được hay không giải được theo

hướng ngược từ các đỉnh lá đến đỉnh xuất phát và trong trường hợp khẳng định thì

chỉ ra đồ thị con lời giải (có thể thỏa mãn thêm một tính chất nào đó).

    •               •  

     •          o      •    •

         •          •    o  o           o       •  •         • 

   o      •      o               •         o 

   •                                                            o    •

       o       •           o        o 

       •    

         

          •              • 

     o        •      • 

          o    •      o    

               o   o    

      •     •   

      (Hình II.2.5.2)

Các phương pháp  tìm kiếm trên cây VÀ/HOẶC c.   (theo chiều rộng,

chiều sâu, cực tiểu giá thành)

  Để đơn giản, ta chỉ xét các phương pháp tìm kiếm trên cây VÀ/HOẶC. Tùy

theo phương pháp (PP) lựa chọn thứ tự các đỉnh sẽ xét mà ta có các phương pháp

tìm kiếm khác nhau theo: chiều sâu, chiều rộng, tìm kiếm cây lời giải có giá thành

nhỏ nhất, ... 

 Sự khác biệt chủ yếu của các PP tìm kiếm trên đồ thị hay cây VÀ/HOẶC

với các PP tìm kiếm trong không gian trạng thái ở mục trước là việc kiểm tra tính

giải được của đỉnh xuất phát (xem nó giải được hay không giải được) và các PP

sắp xếp, lựa chọn đỉnh để mở phức tạp hơn nhiều. Với mỗi đỉnh n, ta sẽ cần dùng

đến 2 thủ tục gán nhãn giải được gđ(n ∈ S)  hay gán nhãn không giải được Kgđ(n

∈ S).

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  35  

 Với mỗi đỉnh n ∈ S, ta dùng các ký hiệu:

 - Nếu n là đỉnh kết thúc thì kt(n) = true, ngược lại kt(n) = false.

   “gđ”    , khi n là đỉnh giải được

   “kgđ” , khi n là đỉnh không giải được

-Nhãn(n) =   “kxđ” , (không xác định) nếu n chưa đủ thông tin để quyết định 

           hoặc n chưa được xét tới

Chú ý rằng, một đỉnh không phải là giải được không nhất thiết là không

giải được mà còn có thể là không xác định !

 - KiểuVÀ(n)=true nếu các đỉnh con của n là đỉnh VÀ và nhận trị false nếu

ngược lại.

- Để đơn giản hoá trong các thuật toán sẽ trình bày sau đây, ngay từ đầu, ta

sẽ gán nhãn:

. “gđ”   : đối với các đỉnh lá giải được

. “kgđ” : đối với các đỉnh lá không giải được

. “kxđ” : đối với mọi đỉnh còn lại

 * Thủ tục gán nhãn giải được

gđ(n ∈ S)

  {1 if  (nhãn(n) = “kxđ”) then   // do đó B(n) ≠ ∅

  if (n ∈ MO U DONG) then // nếu n ∉ MO U DONG: chưa xét n

if  (KiểuVÀ(n)) 

             then {2 bien  = true;

          while  (B(n) ≠∅  and bien) do 

            { con ← get(B(n));

      gđ(con);  bien = (nhãn(con) == “gđ”);

            }             

         if  (bien) then      // mọi đỉnh con dạng VÀ của n đều gđ

         nhãn(n) = “gđ” 

               }2

            else  {3 bien = false;

     repeat  {  con ←  get(B(n));

           gđ(con); bien = (nhãn(con) == “gđ”)

       }

     until  (bien or  B(n) == ∅);

    if  (bien) then      // có một đỉnh con dạng HOẶC của n gđ

nhãn(n) = “gđ” 

         }3

  }1

 Tương tự, có thể viết thủ tục không giải được Kgđ(x ∈ S) (bài tập).

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  36  

* Dạng chung của các thủ tục tìm kiếm trên cây VÀ/HOẶC

  Input   :  Cây VÀ/HOẶC  G = (S,A) với gốc xo.

Output:  Thông báo “Không thành công” nếu xo không giải được và 

   “thành công” nếu xo giải được và khi đó xuất cây lời giải.

Tính gđ hay kgđ của gốc được lan truyền ngược từ lá. Trong các thuật toán

của phần này, ta sử dụng tập DONG có cấu trúc stack. 

       TìmKiếm_VÀ_HOẶC(xo, DONG)

      {1  MO = {xo}; DONG = ∅;   

          while (MO ≠ ∅) do

         {2 n ← get(MO);    // lấy n ra khỏi đầu danh sách MO

 Push(n, DONG);

  if  (B(n) ≠∅)      // đỉnh n có con

 then  {3  MO ←  MO U B(n); 

          bool = false;

          while (B(n) ≠ ∅ and not bool) do

  { con ← get(B(n)); bool = (nhãn(con) == “gđ”);

              }

       if  (bool) then   // nếu có ít nhất 1 con gđ thì xem lại gốc có gđ không ? 

                 { gđ(xo); 

             if   (nhãn(xo) == “gđ”)  

             then {  write(“Thành công”);

         XuấtCâyLờiGiải(x0, DONG); exit; 

           }  

             else  Loại khỏi MO các đỉnh có tổ tiên là đỉnh giải được 

                } 

          }3 

  else         // n là đỉnh lá

if   (kt(n))     // n là đỉnh lá gđ, xem lại gốc có gđ không ?

  then { gđ(xo); 

             if   (nhãn(xo) == “gđ”)  

             then {  write(“Thành công”);

        XuấtCâyLờiGiải(x0, DONG); exit; 

            }  

             else Loại khỏi MO các đỉnh có tổ tiên là đỉnh giải được  

                } 

  else     // n là đỉnh lá kgđ, xem lại gốc có kgđ không ?

        { Kgđ(xo);

    if  (nhãn(xo) == “Kgđ”) 

    then exit(“Không thành công”)   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  37  

    else Loại khỏi MO các đỉnh có tổ tiên là đỉnh không giải được  

                    } 

         }2

      }1

* Thuật toán tìm kiếm theo chiều rộng trên cây VÀ/HOẶC

 Chính là thuật toán TìmKiếm_VÀ_HOẶC(xo, DONG), với cấu trúc tập MO là

hàng đợi.

Nếu cây lời giải tồn tại thì thủ tục tìm kiếm theo chiều rộng sẽ dừng và cho

kết qủa là cây lời giải có độ cao nhỏ nhất.

Ví dụ 20 -  : Xét cây VÀ/HOẶC như trên hình II.2.5.3.  Thứ tự đưa các nút vào

danh sách DONG được chỉ ra bởi các số ở bên cạnh các đỉnh. Các đỉnh tô đậm là

các đỉnh giải được. Cây lời giải được phân biệt bởi các cung tô đậm.

 Quá trình đưa các đỉnh vào tập MO và DONG như sau:

  MO    DONG

  1

  2,3    1

  3;4,5    1,2

  4,5;6,7    1,2,3

  5;6,7;8,9   1,2,3,4

  6,7;8,9;12,13   1,2,3,4,5

  7;8,9;12,13;10,11  1,2,3,4,5,6   (đỉnh 2,3: “gđ”)

      (Đỉnh gốc 1 : “gđ”  →  Kết thúc)

         • 1

               2•                • 3

      4 •       o 12   o 5   • 6    18 o      o7    o19

     o8     9 o13 •10         •11

            o15       20•   •        •21     22•      o23

      (Hình II.2.5.3)

* Thuật toán tìm kiếm theo chiều sâu trên cây VÀ/HOẶC

Chính là thuật toán TìmKiếm_VÀ_HOẶC(xo, DONG), với cấu trúc tập MO

là ngăn xếp.

* Thuật toán tìm kiếm cực tiểu giá thành trên cây VÀ/HOẶC

 Cho cây VÀ/HOẶC  G = (S, A) với gốc x0 và hàm giá c: A → R+. Để tìm cây lời giải có

giá cực tiểu, ta cần đến khái niệm hàm giá tối ưu h(n) của cây lời giải có gốc  là đỉnh n bất kỳ và

hàm giá ước lượng h0

(n) của nó.

Định nghĩa: hàm giá tối ưu h(n) của cây lời giải có gốc n được xây dựng như sau:

  - Nếu n là đỉnh lá và    Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  38  

. giải được (đỉnh kết thúc) thì  h(n) = 0.

  . không giải được thì hàm h(n) không xác định

 - Nếu n có các đỉnh con  n1, ..., nk   là: 

. đỉnh HOẶC thì:  h(n) =  min (c(n, ni) + h(ni)).

. đỉnh VÀ thì:      k

      h(n) =  ∑    (c(n, ni) + h(ni)),  với kiểu giá tổng cộng

       

i=1 

       h(n) =  max (c(n, ni) + h(ni)), với kiểu giá cực đại

       

1≤ i ≤n

 (Lưu ý rằng  h(n) không xác định đối với các đỉnh không giải được)

          Mục đích của việc tìm kiếm là xây dựng cây lời giải có giá cực tiểu h0

(no).

 - Trên thực tế,  trong thuật toán ta cần xác định và sử dụng hàm ước lượng heuristics ho  

đối với các đỉnh không phải là đỉnh không giải được. Cây lời giải được xây dựng dần trong quá

trình mở rộng cây lựa chọn, tại mỗi thời điểm các nút lá của nó thuộc một trong 3 dạng sau:

 a. Các đỉnh lá giải được (đỉnh kết thúc).

 b. Các đỉnh lá không giải được (đỉnh không kết thúc và không có con).

 c. Các đỉnh chưa được tháo (chưa được xử lý).

 Mỗi đỉnh này của cây lựa chọn gọi là đỉnh mút.

 - Ta xây dựng ước lượng ho 

của h như sau:    (1)

 1. n là đỉnh mút

      - Nếu n là đỉnh lá giải được thì  ho

(n) = 0.

- Nếu n là đỉnh lá không giải được thì ho

(n) không xác định.

- Nếu n  chưa được xử lý thì để làm giá trị ho

(n) có thể lấy một ước lượng heuristic nào

đó của h(n). Ước lượng này dựa trên các thông tin heuristics về bài toán.

 2. n không là đỉnh mút: nếu n có các đỉnh con  n1,..., nk  là: 

    - đỉnh HOẶC thì: ho

(n) =  min (c(n, ni) + ho

 (ni)).

     - đỉnh VÀ thì:      k

     ho

(n) =  ∑    (c(n, ni) + ho

(ni)), với kiểu giá tổng cộng

      

i=1 

     ho

(n) =  max (c(n, ni) + ho

 (ni)), với kiểu giá cực đại

      

1≤ i ≤n

 - Trong cây tìm kiếm, ở mỗi bước có thể chứa một tập các cây con có gốc xo sao cho

chúng trở thành phần trên của cây lời giải đầy đủ. Ta gọi các cây này là cây lời giải tiềm tàng gốc

xo. Dựa vào ho

 có thể xây dựng một cây lời giải tiềm tàng To  gốc xo có nhiều triển vọng trở thành

phần trên của cây lời giải thật sự gốc xo như sau:      

  (2) 

 1. Đỉnh xuất phát xo ∈ To.

 2. Nếu n ∈ To có các đỉnh con n1,..., nk  là đỉnh :

a. HOẶC thì chọn một con nio đưa vào To sao cho: c(n,nio)+ho

(nio)→ min và nhãn(nio) ≠

“kgđ”.

    b. VÀ thì đưa tất cả n1,..., nk vào To  nếu nhãn(ni) ≠ “kgđ” ∀ i = 1, .., k

  TìmKiếm_VÀ_HOẶC_CựcTiểu

{1 MO = {xo};  To ← xo ;

   while   (MO ≠∅)   do

   {2 n ← Get(MO);   // lấy n ra khỏi đầu danh sách MO

       DONG ← {n} U  DONG;

                  if  (kt(n))       // n là đỉnh lá gđ

       then  {  gđ(xo);   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  39  

  if  (nhãn(xo) == “gđ”)

   then {  Xuất cây lời giải tối ưu To; exit;

           }

            else  Loại khỏi MO các đỉnh có tổ tiên là đỉnh giải được  

       } 

       else  if  (B(n) ≠∅)        // n không là đỉnh lá 

  then  { for each  m ∈ B(n) do Tính ho

(m);  // theo (1)

o

     for each  m ∈ MO U DONG do Tính h (m);

       MO←MO U B(n);  // ChènTăngTập(B(n), MO) theo h0

(n)

   xd(To);   // Xây dựng cây To dựa trên n và B(n) theo (2)

            }

   else        // n là đỉnh lá Kgđ

        { Kgđ(xo);

    if  (nhãn(xo) == “kgđ”) 

    then exit(“Không thành công”)

    else Loại khỏi MO các đỉnh có tổ tiên là đỉnh không giải được  

                    } 

      }2

  }1

 - Ví dụ 21: Xét cây VÀ/HOẶC như hình II.2.5.4. 

        •a,9,9

 

    •b,8,7      •c,14,8

  •d,3,3    •e,3,3         •f,7,3  •g,2,2

     •h,2             •i,6  •j,2         •k,7   •l,7  •m,6

•        •      o       o      •       •       o         o                o         o      o         o

n,o    o,o   p,2   q,2    r,o    s,o    t,3      u,2       v,3     w,2   x,2     y,2

     (Hình II.2.5.4)

Các số bên cạnh đỉnh là giá trị h và ho

. Đối với các nút mút trong quá trình tìm kiếm các

giá trị ho

  là các đánh giá heuristics giá tổng cộng của bài toán tương ứng. Lấy ho

 = h đối với các

nút ở độ sâu 3, 4 (các nút từ h đến y), ho

(d) = 3, ho

(e) = 3, ho o

(f) = 3, h (g) = 2, ho

(b) = 7, ho

(c) = 8,

ho

(a) = 9. Quá trình tìm kiếm được chỉ ra trên hình II.2.5.5 và II.2.5.6. Cây lời giải tiềm tàng chỉ ra

bởi các cung tô đậm.

   a.        b.    c.

      (Hình II.2.5.5)

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  40  

 Vấn đề cần đặt ra là chọn đỉnh nào trong các đỉnh VÀ của To để xử lý. Có thể chọn các

đỉnh mút trong To có xác suất lớn nhất sao cho có thể dẫn đến bác bỏ giả thuyết rằng To sẽ là phần

trên của cây lời giải có giá tối ưu. Với giá tổng cộng  thì đỉnh có xác suất lớn nhất chứng tỏ sự

không phù hợp của T   là đỉnh có giá trị  ho

 lớn nhất. Với giá cực đại ta chọn đỉnh MO trong T o o 

như sau: Xuất phát từ xo  đi trong cây To, nếu gặp một đỉnh n nào đó có đỉnh con là đỉnh VÀ thì

chọn cung từ  n tới đỉnh con n o

 sao cho  c(n,n i i ) + h (ni ) → max.

 Quá trình đưa các đỉnh vào tập MO và DONG như sau:

   To   MO    DONG

(a,9) /* (a,ho

) */ (a,9)    (a,9) 

(b,7)    (b,7),(c,8);      min  (b,7)

(d,3) ,(e,3)  (d,3) ,(e,3);(c,8);      ∀ (d,3)

(h,2)   (h,2),(i,6);(e,3);(c,8);   min (h,2)

(n,0),(o,0)  (n,0),(o,0;(i,6);(e,3);(c,8)∀ (n,0),(o,0)

(e,3)   (e,3),(c,8)   (e,3)

(j,2)   (j,2),(k,7);(c,8)     min (j,2)

(r,0),(s,0)  (r,0),(s,0);(k,7);(c,8)      ∀ (r,0),(s,0)

   (→  Kết thúc)

       (Hình II.2.5.6)

 II.2.6. Phương pháp GPS (General Problem Solving)

 Phương pháp này còn gọi là phương pháp mục đích - phương tiện  (Ends-

Means). PP GPS là sự tổng quát hóa của những PP giải quyết vấn đề đối với bài

toán biểu diễn vấn đề trong không gian trạng thái đã trình bày.

Ý tưởng -  : Giả sử bài toán được biểu diễn trong không gian trạng thái S và

cần phải đưa ra cách biến đổi từ trạng thái ban đầu xo ∈ S về trạng thái cuối x* ∈

S. Trong các PP dựa trên lôgic hình thức quá trình mở rộng cây tìm kiếm dựa trên

các thông tin định lượng: số phép biến đổi đã sử dụng và ước lượng heuristics ho

phản ánh mối liên hệ giữa  xo và x*. Trong khi đó , GPS quan tâm đến sự khác biệt

δ  giữa x i  o  và x*  (theo nghĩa định tính lẫn định lượng) và mối liên hệ giữa chúng

với các phép biến đổi trạng thái oj.  Như vậy, giả sử xuất phát từ xo   ta đã đi tới

trạng thái x. Xem xét một khác biệt “quan trọng nhất” δ  giữa x i    và x*, dựa vào đó

xác định phép biến đổi oj  nào đó “hiệu qủa nhất”  để biến đổi  x sao cho giảm sự

khác biệt δi  nhiều nhất. Kết qủa nhận được trạng thái mới oj (x) và cứ như vậy tiếp

tục cho đến khi x ≡ x*. 

 - Ba yếu tố cơ bản trong PP GPS:

a. Xác định không gian trạng thái S, trạng thái đầu xo ∈ S, trạng thái cuối

x* ∈ S và tập các phép biến đổi O  từ trạng thái này sang trạng thái khác: 

  O  =  { oj ⏐  o : j  S →  S, j = 1..n }

 b. Xác định tập Δ các kiểu khác biệt giữa các trạng thái trong không gian 

2

   Δ = { δi  ⏐  δ : S  →  R, i = 1..m }  i  

c. Xây dựng ma trận M với các cột ứng với các toán tử, các hàng ứng với

các sự khác biệt có thể có 

   M  = (m  )  ij  m x n   , trong đó:   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  41  

     1 , nếu phép biến đổi o  làm giảm sự khác biệt δ  j i

  m ij   =

     0, nếu ngược lại

 (Có thể cho mij những trị khác để phản ánh tốt hơn tác dụng của toán tử oj

đến sự khác biệt δ ).  i

Bài toán   

  Input: - Tập  S  các trạng thái, trạng thái đầu xo, trạng thái cuối x*

            - Tập  Δ = { δ1, ..., δm}  các sự khác biệt

            - Tập O  =  { o1 ,..., on } các toán tử

 )             - Ma trận  M = (m ij  m x n  

  Output:  Ra thông báo “Thành công”  nếu đưa được xo  về x*

Thuật giải GPS

{ x = xo; OP  = ∅;

             while  (x ≠ x*)  do

    { D = match(x, x*) ; // D =  { δ } là tập sự khác biệt giữa x và x*   , ..., δ i1 ik

       δ ← get(D);     // chọn ra sự khác biệt quan trọng nhất δ  = δi ∈ D  

       for j=1 to n  do  

if (m ij  = 1) then  OP = OP U {o }; // OP ={o ⏐ m j j  ij=1}

 nhiều nhất          o ← get(OP); // chọn toán tử o = oj ∈ OP làm giảm sự khác biệt δi

       x = o(x);

    } 

    if (x = x*) Xuất lời giải;

         }

  Ở đây thủ tục match(x, x*) cho phép xác định những điểm khác biệt giữa x

và x*. Chẳng hạn, sự khác biệt giữa đỉnh x và x* trong đồ thị biểu diễn không gian

trạng thái S  có thể là độ dài đường đi từ x tới x*. 

 

Ví dụ 22 -  : Cần phải chứng minh sự tương đương logic giữa hai biểu thức

mệnh đề:

  R ∧ (¬ P ⇒  Q) ≡  (Q V  P) ∧ R

     Áp dụng phương pháp GPS để giải bài toán trên.

         . Trong không gian biểu diễn các mệnh đề lôgic xét 4 kiểu khác biệt sau:

ΔT: Có sự khác biệt về dấu  ¬ (phủ định) trong biểu thức. 

  ΔC: Khác nhau về số các phép toán (∧,∨,⇒).

  ΔG: Khác nhau về phương pháp nhóm các mệnh đề. 

  ΔP: Khác nhau về vị trí các thành phần trong hai biểu thức.

. Trong bài toán này, ta lấy các toán tử biến đổi trạng thái là 3 phép biến đổi

tương đương sau:   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  42  

 R1: A∨B ⇔ B∨A ;    A∧B ⇔ B∧A

 R2: A→ B  ⇔  ¬A ∨ B

 R3: ¬¬ A  ⇔ A

       . Xét ma trận M  sao cho các cột tương ứng với các phép biến đổi : R1-R3.

 R1 R2 R3

ΔT  1 1

ΔC  1

ΔG  

ΔP 1 

 

Đặt :   L1 ≡ R ∧ (¬ P ⇒  Q) ,  Lo ≡ (Q V  P) ∧ R

Đích 1:  Biến đổi L1 về Lo

Sự khác biệt: D = {ΔT, ΔP, ΔC}. Với ΔP (ΔP = get(D)), OP = {R1}: chọn

R1 áp dụng vào L1 ta được: L2 =  (¬ P ⇒  Q) ∧ R.

Đích 2: Biến đổi L2 về Lo

Sự khác biệt: D = {ΔT, ΔC, ΔP}. Với ΔC  (ΔC = get(D)), OP = {R2}: chọn

R2 áp dụng vào L2 ta được: L3 =  (¬ ¬ P ∨  Q) ∧ R.

Đích 3: Biến đổi L3 về Lo

Sự khác biệt: D = {ΔT, ΔP}. Với ΔP (ΔP = get(D)), OP = {R1}: chọn R1 áp

dụng vào L3 ta được: L4 =  (Q ∨ ¬ ¬ P) ∧ R.

  Đích 4: Biến đổi L4 về Lo

Sự khác biệt: D = {ΔT}. Với ΔT (ΔT = get(D)), OP = {R2, R3}: ta chọn R3

mới làm giảm sự khác biệt  ΔT của L4,  ta được: L5 = (Q∨P)∧R ≡ Lo.

 Bài toán được chứng minh.

 - Ngoài ra, nếu qui bài toán về dạng:

   [R ∧ (¬ P → Q)]  ⇒  [(Q V  P) ∧ R]

 và  [(Q V  P) ∧ R ]    ⇒  [R ∧ (¬ P ⇒  Q)]

thì ta có thể giải bài toán bằng: thủ tục tách Wong hay thủ tục hợp giải

Robinson sẽ được trình bày trong chương 3.

II.3. Kỹ thuật Heuristic

Những bài toán, đặc biệt là khi chúng có kích thước lớn và có cấu trúc phức

tạp, đã có thuật toán khả thi với độ phức tạp không quá đa thức, thường rất hẹp. 

Hạn chế của thuật toán -  : Trên thực tế, ta thường gặp nhiều bài toán:

. hoặc cho đến nay chưa có thuật toán nào để giải; 

. hoặc đã có thuật toán để giải nhưng không khả thi về không gian nhớ và thời

gian do độ phức tạp của nó có cấp vượt quá đa thức;

. hoặc có các phương pháp giải mặc dù được thực tế chấp nhận nhưng lại vi

phạm một số tính chất của thuật toán như tính xác định hay tính đúng. Chẳng   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  43  

hạn, để chuyển từ bước sơ cấp này đến bước sơ cấp sau đôi khi lại cần bổ

sung thêm thông tin kinh nghiệm (rút ra trong quá trình tìm kiếm lời giải thực

tế) đặc thù của bài toán mới quyết định được. Những bài toán liên quan đến số

thực được giải trên máy tính, nếu chấp nhận lời giải gần đúng hoặc gần tối ưu

thì có thể tồn tại nhiều cách giải đơn giản và hiệu quả hơn.

Để giải quyết khó khăn này người ta đã mở rộng tính xác định, tính đúng của

thuật toán và bổ sung  thêm các tri thức kinh nghiệm đặc thù hay mẹo giải để có

được các thuật giải heuristics. 

-  Các  đặc trưng của thuật giải heuristic:  thường  tìm  được lời giải  tốt

(không chắc luôn luôn là tốt nhất) hay gần tốt một cách đơn giản, nhanh chóng và

đôi khi độc đáo (so với những thuật toán tương ứng giải cùng một bài toán). Có 2

cách đưa các thông tin heuristics đặc tả vào các thủ tục tìm kiếm:

    . các tri thức được nạp ngay trong biểu diễn của bài toán;

   . các hàm đánh giá heuristics nhằm lượng hoá cấp độ của khả năng lựa

chọn việc thực hiện.

- Các kỹ thuật heuristics liên hệ chặt chẽ với chiến lược  điều khiển các

hướng tìm kiếm lời giải và xử lý cạnh tranh; chúng được chia thành 2 lớp chính:

  . Định tính: dưới dạng các luật nếu ... thì 

  . Định lượng: dưới dạng các hàm ước lượng heuristics ho

. Các hàm đánh

giá được xem là công cụ có rất hiệu quả khi sử dụng kỹ thuật heuristic. Hai

vấn đề quan trọng trong lập trình heuristic có sử dụng các hàm đánh giá là:

tách những dấu hiệu có ích và tổ hợp các dấu hiệu này để thể hiện thành

những hàm đánh giá tốt. Một kỹ thuật heuristics được coi là hợp lý khi nó

cho phép tiến hành đánh giá các khả năng để làm rõ khả năng nào tốt hơn

các khả năng còn lại.

- Hàm số heuristics

 Dạng tổng quát của các hàm đánh giá:  f(p) = f(t1, ... , tn), trong đó ứng với mỗi khả năng

p, dạng giải tích của hàm đánh giá f phụ thuộc vào các tham số t1, ... , tn và trả về một giá trị f(t1,

... , tn)  đặc trưng cho khả năng p.

   . Trường hợp đơn giản nhất (dạng afin): f(t1, ... , tn) =  ∑ ai.ti  , với ai  là các hệ số đặc

trưng cho trọng số của các tham số ti.

    . Phức tạp hơn là dạng toàn phương:

                   n              n                         n 

    f(t1, ... , tn) =  ∑ ai.ti    +   ∑ bi.ti

2   

+  ∑   cij .ti.tj

                      i=1            j=1                     i ≠j=1

 - Quá trình xây dựng hàm heuristic gồm 3 vấn đề chính sau:

     . Xác định các dấu hiệu đặc tả si  của bài toán;

. Với mỗi khả năng p, các dấu hiệu đặc tả si   được lượng hoá bởi các giá trị ti. Xác

định dạng của hàm đánh giá f(p) = f(t1, ... , tn)  dựa trên các giá trị đặc trưng cho các

thuộc tính ti và các tham số;

    . Xác định giá trị của các tham số cho từng bài toán cụ thể.   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  44  

 

      II.3.1. Các thuật giải tìm kiếm tối ưu trên cây và đồ thị với tri thức heuristic 

Problem_3**(G = (S, A), x0, DICH, c, h0

)          (BT3**)

a. Thuật giải AKT

 (Algorithm for Knowledge Tree search) giải bài toán tối

ưu BT3** với tri thức bổ sung heuristic trên cây

Tương tự  thuật toán AT

  tìm kiếm cực tiểu trên cây G = (S, A) có gốc x0

(phần II.2.4.c), nhưng thay hàm g0

(x) bởi f

0

(x) = g0

(x) + h0

(x). 

  - Ý nghĩa: 

     . g0

(x): giá chi phí thật sự  từ x0 đến x, cách tính g0

(x) theo công thức đệ

qui: g0

(x0) = 0 & g0

(con) = g0

(cha) + c(cha, con)  ∀ (cha, con) ∈ A.

    . h0

(x): ước lượng chi phí từ  x đến tập DICH, hay là hàm ước lượng khả

năng dẫn đến lời giải. 

   . Việc dùng hàm  f

0

(x) = g0

(x) + h0

(x) khi chọn lựa phương án kế tiếp,

không chỉ quan tâm đến chi phí đã trả từ điểm xuất phát x0 đến trạng thái

hiện tại x, mà còn tham khảo thêm cả thông tin heuristic đặc trưng cho khả

năng nhanh dẫn đến đích cuối cùng từ trạng thái hiện tại. Có thể xem các

phương pháp này là các  thuật giải vét cạn thông minh. Đây là cách giải

thông minh và độc đáo mà con người thường sử dụng khi gặp các bài toán

khó trong thực tế như chứng minh định lý, giải các bài toán suy luận lôgic,

chơi cờ, …

- Nhận xét: Có thể xem AKT

  là tổng quát hoá của AT

  khi xét h0

 ≡ 0. Để tìm

được đường đi tối ưu, ta cần chọn hàm ước lượng h0

(x) ≤  h*

(x) ∀ x ∈ S, với h*

(x)

là hàm giá tối ưu thật sự từ  x đến DICH.

b. Thuật giải A* giải bài toán  tối ưu BT3** với tri thức bổ sung heuristic  trên đồ thị

tổng quát 

Tập MO, DONG chứa các phần tử  E có dạng (x, cha(x), g0

(x), f

0

(x)), với qui ước: Ex =x,

Echa = cha(x), Eg0 = g0

(x), Ef0 = f

0

(x). ChènTăng(E, MO) là thao tác chèn E vào MO theo thứ  tự 

tăng của f

0

(x) = g0

(x) + h0

(x) từ  đầu đến cuối danh sách MO.

A*(xo, DICH)

{1 go

(xo) = 0; Tính f

o

(xo) = ho

(xo); MO  ← {(xo, rỗng, 0, f

o

(xo))};

DONG ← ∅;

  while   (MO  ≠  ∅)  do

  {2  E ← get(MO);      // trong MO, E ở đỉnh và có f

0

(Ex) nhỏ nhất !

   Push(DONG, E);

  if  (Ex ∈ DICH) then { XuấtLờiGiải(E, DONG); Dừng;}

  for  each con ∈ B(Ex) do

   {3  g0

(con) = g0

(Ex) + c(Ex, con); f

o

(con) = go

(con) + ho

(con);

   if   (con ∉  (MO  U DONG)x) 

    then ChènTăng((con, Ex, go

(con), f

o

(con)), MO);   

    else {4 if (∃ y ∈ MO  and  con == yx  and go

(con) < go

(yx))

        { Loại(y, MO);    // loại y khỏi tập MO

           ChènTăng((con, Ex, go

(con), f

o

(con)), MO);  

      }    Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  45  

     if (∃ y ∈ DONG  and  con == yx and go

(con) < go

(yx))

        { Loại(y, DONG);   // loại y khỏi tập DONG

Push((con, Ex, go

(con), f

o

(con)), DONG);  

Cập nhật lại go

 và f

o

 cho các hậu duệ của con đã lưu

               } 

           }4

   }3

  }2 

 write(“Không thành công”);

       }1

 c. Các ví dụ

- Ví dụ 23: Xét bài toán trò chơi 8 số như trong ví dụ 1 (hình II.3.1). 

Ta xác định hàm go

(x) là độ dài đường đi hiện tại từ  xo  đến x (số phép dịch

chuyển vị trí trống để đưa trạng thái xo về trạng thái x), còn đối với ho

(x), ta có thể

tính ít nhất theo hai cách sau đây.

 

. Cách 1: ho

1(x)  là số lượng chữ số (khác trống) trong trạng thái x không

nằm ở đúng vị trí của chúng trong trạng thái đích. Chẳng hạn, với đỉnh gốc xo, 

go

(xo) = 0, ho

1(xo) = 4, do đó f

o

1(xo) = go

(xo) + ho

1(xo) = 4. Trên hình II.3.1, ta có kết

qủa áp dụng thủ tục A* với tri thức bổ sung ho

1(n) được cho ở góc trên bên trái

mỗi trạng thái, giá trị của f

o

1

đối với mỗi nút được cho ở góc trên bên phải của nó.

Các số nằm ở  đỉnh trên mỗi trạng thái là thứ tự xét chúng trong thủ tục. Với cách

chọn hàm  ước lượng heuristic không tốt như   ho

1, ta có thể  đi nhầm một bước

(bước 3).

 . Cách 2: ho

2(x)  là tổng số các phép di chuyển ít nhất tương ứng với mỗi

chữ số (khác trống) trong trạng thái x cần di chuyển đến đúng vị trí của nó trong

trạng thái đích. Chẳng hạn, với đỉnh gốc xo,  go

(xo) = 0, ho

2(xo) = 5, do đó f

o

2(xo) =

go

(xo) + ho

2(xo) = 5. Trên hình II.3.1,  ta có kết qủa áp dụng thủ tục A* với tri thức

bổ sung ho

2(n) được cho ở góc dưới bên trái mỗi trạng thái, giá trị của f

o

2

đối với

mỗi nút  được cho  ở góc dưới bên phải của nó. Với cách chọn hàm  ước lượng

heuristic ho

2 tốt hơn ho

1, ta đi không nhầm bước nào, do ho

2 phản ánh sự khác nhau

giữa hai trạng thái tốt hơn. Ta nói heuristic ho

2  chứa nhiều thông tin hơn heuristic

ho

1 nếu:  h2

o

(n) ≥ h1

o

(n).

 Qua đó, ta thấy việc sử dụng tri thức bổ sung thông qua hàm ho

đóng vai trò

rất quan trọng. Nó cho phép gạt bỏ những thông tin không có triển vọng, nhưng

nếu ho 

vượt quá giới hạn h thật sự  thì có thể thuật toán nhanh dừng song không

cho kết quả mong muốn. Hàm ho 

và go 

có  ảnh hưởng khá lớn  đến sức mạnh

heuristics của thuật giải A*.

     1     Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  46  

              4  28 3  4                 

                1 64                    

              5  7• 5  5                 

                   2                    

  5  28 3  6        3 28 3  4       5 2 8 3  6      

    1 64            1• 4            1 6 4        

  6  • 75  7        4 7 65  5       6 7 5 •  7      

          3                4            

      3  283  5           3 2 • 3  5    4  28 3  6  

        •14                1 8 4        1 4•    

      5  765  7            3 7 6 5  5    5  7 65  7  

                        5            

      3  •83  6    4 28 3  7   2 • 2 3  5    4  23 •  7  

        214        71 4        1 8 4        18 4    

      6  765  9    6 •6 5  9   2 7 6 5  5    4  76 5  7  

                        6            

                      1 1 2 3  5          

                        • 8 4            

                      1 7 6 5  5          

                                  

                      0 1 2 3  5,2 1 2 3  7      

                        8 • 4  7 8 4        

                      0 7 6 5  5,2 •  6 5  7      

(Hình II.3.1)

           h=2

      g=0:                     f=2     

      g=1:       h=3              h=2    

                 f=4                          f=3   

     g=2:           h=1     h=3          h=2 

           f=3     f=5          f=4

     g=3:

        h=2           h=0  h=1

        f=5            f=3  f=4

(Hình II.3.2)

 - Ví dụ 24: Xét bài toán tháp Hà nội với n = 2 (hình  II.3.2). Hàm f

o

(n) = 

go

(n)

+ ho

(n), với go

(n) là số đĩa đã di chuyển,  ho

(n) là thông tin về mối liên hệ

giữa n và trạng thái đích, chẳng hạn:

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  47  

 - Nếu ở cọc C có  2  đĩa, đĩa nhỏ trên đĩa to thì  ho

 = 0.

 - Nếu ở cọc C có  1  đĩa to  thì  ho

 = 1.

 - Nếu ở cọc C chưa có đĩa nào thì  ho

 = 2.

 - Nếu ở cọc C có  1  đĩa nhỏ thì  ho

 = 3.

Lời giải tối ưu cho bài toán tháp Hà Nội được trình bày qua cây lời giải in

đậm trong hình II.3.2.

 Một cách tổng quát, ta có thể lấy f

o

(n) =  go

(n)

+ ho

(n),  với ho

(n) là tri thức

bổ sung chỉ ra triển vọng của đỉnh nằm trên đường tối ưu. Do đó có thể xem f

o

(n)

là ước lượng của hàm  f(n) = g(n) + h(n) với g(n) là giá đường đi tối ưu từ no tới

n, h(n) là giá đường đi tối ưu từ n tới tập DICH. Nói cách khác f

o

(n) là xấp xỉ giá

đường đi tối ưu từ no tới tập DICH và đi qua đỉnh n. Giá trị go

(n) đối với n có thể

tính được dựa trên ý tưởng thuật giải A* (giá đường đi “gần cực tiểu”từ no tới n),

còn ước lượng ho

(n) dựa trên thông tin heuristics gắn liền với bài toán.

         Những kết qủa sau bảo đảm tính đúng đắn và tính tối ưu của giải thuật A*.

 - (Tính đúng đắn): Nếu đối với mỗi đỉnh n ∈ N ta có  0 ≤  ho

(n) ≤  h(n) và tồn tại số  Δ >

0 sao cho đối với mọi cung  a∈ A, c(a) ≥ Δ thì thủ tục A* dừng và cho kết qủa là đường đi p  từ 

no  tới n*  ∈ DICH sao cho  g(n*)  → min.

 - Giả sử các thủ tục Ai* sử dụng hàm đánh giá:

   fi

o

(n) =  gi

o

(n)  + hi

o

(n),  i = 1, 2

Thêm vào đó, h2

o

 thỏa mãn điều kiện h2

o

 (m) - h2

o

 (n) ≤  h(m, n), trong đó h(m,n) là độ dài đường

đi ngắn nhất từ m tới n và với mọi n ∈ N : 0 ≤  h1

o

(n) ≤  h2

o

 (n) ≤  h(n). Khi đó: số nút đưa vào tập

DONG của thủ tục A2*  bao giờ cũng nhỏ hơn số nút đối với thủ tục A1*.

- Ba nhân tố ảnh hưởng đến sức mạnh heuristics là: giá của đường đi nhận được, số đỉnh

phải tháo trong quá trình xử  lý, chi phí tính toán giá trị hàm ho

.

- Nhận xét: 

    . Trong số các thủ tục tìm kiếm sử dụng tri thức bổ sung heuristic ho

, thủ

tục sử dụng triệt để các thông tin về các đỉnh (ho

 =  h và  f

= g + h) là tốt

nhất và thủ tục AT

  (f

o

 = go

, ho 

= 0) là tồi nhất.

  . Nếu chỉ cần tìm một đường đi nào đó tới đỉnh đích thì có thể không cần

tính đến go

, tức là f

 = ho 

. Trong một số trường hợp, nếu không sử dụng go 

thì số nút phải tháo có thể tăng đáng kể so với khi sử dụng go

.

 - Một số heuristics

   . Lựa chọn theo giai đoạn: Quá trình tìm kiếm có thể tiến hành theo từng

giai đoạn, ở mỗi giai đoạn chỉ giữ lại một tập con nào đó các đỉnh trong tập

MO, chẳng hạn các đỉnh với giá trị f

o

  gần nhỏ nhất.

. Hạn chế số đỉnh con: Mỗi lần tháo một đỉnh n, chỉ giữ lại những đỉnh m

∈ B(n) sao cho f

o

(m) thuộc lân cận của f

o

 nhỏ nhất.   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  48  

. Xây dựng dần các đỉnh con: Mỗi lần tháo đỉnh con chỉ xét tới một vài

đỉnh có triển vọng nhất, đầu tiên chỉ chú ý đến đỉnh con quan trọng nhất,

còn các đỉnh khác khi nào cần mới đưa ra.

II.3.2. Nguyên lý tham lam

Với thuật giải sử dụng nguyên lý tham lam giải bài toán tối ưu BT3*: ta sử

dụng các thuật toán TìmKiếm với một ít điều chỉnh: chỉ chọn lời giải tốt nhất trong

một bước kế tiếp (chứ chưa chắc tốt nhất trong cả quá trình),  do đó không cài đặt

cơ chế quay lui. Ưu điểm của nguyên lý này là trong một số trường hợp, có thể

nhanh thấy lời giải. Nhưng nhược điểm của nó là trong các trường hợp khác, có

thể tồn tại lời giải tối ưu nhưng thuật toán này không tìm ra. Khi đó, do không dự

phòng cơ chế quay lui nên có thể không tìm ra lời giải, mặc dù nó vốn tồn tại !

(Tham cái lợi trước mắt mà không để ý đến cái lợi toàn cục và không dự phòng

lưu lại đường đi đã qua để quay lui khi lạc hướng!)

ThamLam(x0, DICH)

⎨    DONG = ∅;    // có thể chọn DONG có cấu trúc stack

MO = ⎨x0⎬;     

if (x0 ∈ DICH) ⎨XuấtLờiGiải(x0, ∅); Dừng;⎬ 

while (MO ≠ ∅)     

⎨ x = get(MO);   // lấy một phần tử  x ra khỏi tập MO 

   Push(x, DONG);  // đưa x vào tập DONG

   if (∃ phần tử  y “tốt nhất” trong B(x))      // luôn tồn tại nếu B(x) ≠ ∅ !

  ⎨ if (y ∈ DICH)

⎨XuấtLờiGiải(y, DONG); Dừng;⎬

      MO = MO U⎨y⎬;  // đưa y vào tập MO

  ⎬

write (“Không tìm thấy lời giải”);

  ⎬

Thật ra MO chỉ gồm không quá 1 phần tử ! Vì vậy, có thể thay thuật toán

trên bởi: 

ThamLam2(x0, DICH)

⎨    DONG = ∅;  // có thể chọn DONG có cấu trúc stack

if (P(x0))       // hay if (x0 ∈ DICH)   

⎨XuấtLờiGiải(x0, ∅); Dừng;⎬ 

x = x0;

while (∃ phần tử  y “tốt nhất” trong B(x))  // luôn tồn tại nếu B(x) ≠ ∅ !

⎨ Push (x, DONG);   // đưa x vào tập DONG

   if (P(y))      // hay if (y ∈ DICH)     Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  49  

⎨XuấtLờiGiải(y, DONG); Dừng;⎬

   x = y;

write (“Không tìm thấy lời giải”);

  ⎬

  1             1

 5         2        2

           

 3            4    4

5            2

         3           7  3

             1       

  4

 - Ví dụ 25: (Traveling Saleman)

Xây dựng một hành trình TOUR có chi phí nhỏ nhất COST cho bài

toán đi qua n thành phố, với ma trận chi phí C, sao cho bắt đầu từ thành phố

u và đi qua mỗi thành phố đúng một lần.

 Biến thể của thuật giải ThamLam2 là thuật giải GTS giải bài toán người bán hàng

du lịch trên đây.

Thuật giải  GTS

{  // Khởi tạo

 TOUR  = ∅; COST = 0; 

 v = u; 

   // thăm tất cả các thành phố

       for  k = 2 to n do   

{  // Chọn cạnh kế tiếp <v, w> là cạnh có chi phí nhỏ nhất từ v đến  

  // các đỉnh chưa  được sử dụng w:

 TOUR = TOUR + <v, w>;

 COST  = COST + C(v, w);

 v = w;      // khi đó w đã được sử dụng

     }

   // Chuyến đi hoàn thành

 TOUR = TOUR + <v, u>;

 COST = COST + C(v, u);

}

Minh họa việc thực hiện:

C =       

3235

3147

2142

3441

5721

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  50  

 1. TOUR  =  ∅; COST = 0; u = 1;      1 

   ⇒ w = 2;              1   

          2

 2. TOUR = <1,2>; COST = 1; u = 2;

            1

   ⇒ w = 5;             1

          2

           5            3

 3. TOUR = {<1,2>, <2,5>}; COST  = 4; u = 5;

            1

   ⇒ w = 3;                1

          2

           5  3

 4. TOUR = {<1,2>, <2,5>, <5,3>}             2   3

     COST  = 6; u = 3;     

               1

   ⇒ w = 4;          1

          2

        5             3

                2

           3

         1

           4     

5. TOUR = {<1,2>, <2,5>, <5,3>, <3,4>}; COST = 7; 

    u = 1;

     TOUR = {<1,2>, <2,5>, <5,3>, <3,4>, <4,1>}; COST = 14;

        Tìm min trong các phần tử còn lại của hàng. Xuất phát ở cột nào thì bỏ cột đó

    1       2     4     5     3

3235

3147

2142

3441

5721

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  51  

 Do không quay lui nên lời giải vừa tìm chưa chắc là đường đi ngắn nhất.

Thật vậy, lời giải vừa tìm có chiều dài lớn hơn đường đi: 

TOUR2 = {<1, 3>, <3, 4>, <4, 5>, <5, 2>, <2, 1>} với COST2 = 10.

II.3.3. Nguyên lý hướng đích, phương pháp leo núi (Hill-Climbing)

           - Với thuật giải sử dụng nguyên lý hướng đích hay  leo đồi (Hill climbing)

giải  bài toán tối  ưu BT3*, ta sử dụng các thuật toán  TìmKiếm với một ít  điều

chỉnh, trong  đó  không cài  đặt cơ chế quay lui ! Ưu  điểm của nguyên lý này là

trong một số trường hợp, có thể  tìm thấy rất nhanh lời giải. Nhưng nhược điểm

của nó là trong nhiều trường hợp khác, có thể tồn tại lời giải tối ưu nhưng thuật

giải này không tìm ra, hoặc nó chỉ tìm thấy lời giải tối ưu địa phương! (Chỉ chú ý

đến tối ưu địa phương trước mắt, không dự phòng lưu lại đường đã đi qua để quay

lui khi lạc hướng, không biết lùi một bước để tiến nhiều bước hơn!)

LeoĐồi(x0, DICH)

⎨    DONG = ∅;    // tập DONG có cấu trúc stack

MO = ⎨x0⎬;

if (P(x0))       // hay if (x0 ∈ DICH)   

⎨XuấtLờiGiải(x0, ∅); Dừng;⎬ 

while (MO ≠ ∅)

⎨ x = get(MO);   // lấy một phần tử  x ra khỏi tập MO 

   Push(x, DONG);  // đưa x vào tập DONG

   if (∃ phần tử  tốt nhất y ∈ B(x) mà “tốt hơn” x) 

// chưa chắc tồn tại dù B(x) ≠ ∅ !

  ⎨ if (P(y) đúng)    // hay if (y ∈ DICH)  

⎨XuấtLờiGiải(y, DONG); Dừng;⎬

     MO = MO U⎨y⎬;   // đưa y vào tập MO

  ⎬

write (“Không tìm thấy lời giải”);

  ⎬

Thật ra MO chỉ gồm không quá 1 phần tử ! Vì vậy, có thể thay thuật toán

trên bởi: 

LeoĐồi2(x0, DICH)

⎨    DONG = ∅;     // tập DONG có cấu trúc stack

x = x0; Push(x, DONG);

if (P(x0))  ⎨XuấtLờiGiải(DONG); Dừng;⎬ 

while (B(x) ≠ ∅  and ∃ phần tử  tốt nhất y ∈ B(x) mà “tốt hơn” x) 

//chưa chắc tồn tại dù B(x) ≠ ∅ !   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  52  

⎨ Push(y, DONG);   // đưa x vào tập DONG

   if (P(y))      // hay if (y ∈ DICH)  

⎨XuấtLờiGiải(DONG); Dừng;⎬

   x = y;

write (“Không tìm thấy lời giải”);

  ⎬

- Một biến thể của thuật giải LeoĐồi_2 là thủ tục sau:

  

Hill_Climbing

  {

- Bước 1:  n = Đỉnh xuất phát;

- Bước 2:  if  (n ∈ DICH) then Dừng;

- Bước 3:  else Chọn h0

(ni) bé nhất trong số mọi đỉnh con ni  ∈ B(n)

  (gọi đỉnh này là Next(n));

- Bước 4:  if  (h0

(n) < h0

(Next(n)) then Dừng; 

      else n = Next(n);

- Bước 5:  Goto 2;

}

- Nhận xét: Để khắc phục nhược điểm của thuật toán: nhanh rơi vào lân cận của tối ưu địa

phương mà không thoát ra được, ta có thể kết hợp thêm thuật giải di truyền GA.

II.3.4. Nguyên lý sắp thứ  tự, nguyên lý trùng khớp nhất

Ta minh họa các nguyên lý trên qua bài toán phân công công việc trên các

máy và bài toán xếp hàng vào container.

- Ví dụ 26: (Bài toán phân công công việc) 

Giả sử có n máy: P1, . . ., Pn và m công việc: J1, . . ., Jm. Các công việc được

tiến hành đồng thời và có thể được thực hiện trên bất kỳ máy nào. Mỗi lần một

công việc được đưa vào máy, máy chỉ dừng khi công việc hoàn tất. Thời gian hoàn

tất công việc Ji là  ti. Thời gian nạp công việc vào máy là 0.

Vấn đề đặt ra là: Bố trí công việc vào các máy sao cho tổng thời gian xử lý

hết mọi công việc là bé nhất.

Mô hình 1:

. 3 máy: P1, P2, P3.

. 6 công việc

Ji 1  2  3  4  5  6

ti 2  5  8  1  5  1

 Chọn phương án:  (J2, J5, J1, J4, J6, J3 ); T = 12.   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  53  

P :      J   5  1 2

P : J 2 5          5

P : J     J 3 1 4   J     J 6 3

        2    3   4            12

* Nguyên lý sắp thứ tự:  Sắp xếp công việc theo thứ tự thời gian giảm dần.

Bố trí công việc theo thứ tự trên:  L*

  =  (J3, J2, J5 , J1, J4, J6 ); T = 8.

  Đây là lời giải tối ưu.

 P1: J3               8

P2: J2        J1

       5           7

P3: J5       J4   J6 

        5     6    7

Mô hình 2(Phản ví dụ về nguyên lý sắp thứ  tự  không cho lời giải tối ưu)

. 2 máy: P1, P2.

. 5 công việc

1  2  3  4  5  Ji

t 3  3  2  2  2  i

      Phương án:(J1, J2, J3, J4, J5 ) không tối ưu (sắp giảm dần theo thời gian): T=7.

         Phương án tối ưu là:  L* =  (J1, J3, J4, J , J 2 5 ); T = 6.

Ví dụ 27.1 -  : Bài toán đóng gói (xếp hàng vào container)

Cho dãy các gói hàng:1, 2, 3, 4, 6, 7, 7, 9; kích thước container: 13.

Vấn đề đặt ra: cần xác định số container  ít nhất để chứa hết hàng.

-  Các cách (heuristics) xếp thùng khác nhau vào container:

H1: - Thứ tự: tùy ý

 - Cách xếp tùy ý.

H2: - Thứ tự: Giảm dần theo kích thước

 - Cách xếp: Lớn trước, nhỏ sau. 

Nguyên lý trùng khớp nhất

H3: - Thứ tự: tùy ý 

 - Ưu tiên xếp trùng khít.

H4: - Ưu tiên xếp trùng khít.

- Sau đó xếp theo Thứ tự: Giảm dần của kích thước

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  54  

 Áp dụng nguyên lý này vào ví dụ trên, ta được phương án tối ưu: (9, 7, 7; 4,

6, 3; , , 2; , , 1;) chứa trong 3 container.

4        1

    6    2

        3

9    7    7

        

        

- Ví dụ 27.2: (Phương án thu được từ nguyên lý trùng khít nhất không là

phương án tối ưu !)

Cho dãy các gói hàng:2, 2, 5, 6, 8, 9; kích thước container: 16. 

. Phương án thu được từ nguyên lý trùng khít nhất: cần 3 thùng 

. Phương án tối ưu chỉ cần 2 thùng ! (Bài tập)

 

 - Nhận xét: Đối với các bài toán phức tạp và đa dạng trong thực tế, nếu chỉ áp

dụng riêng một phương pháp, ý tưởng hay một nguyên lý heuristic riêng rẽ

thì  không thể giải quyết nổi. Khi  đó ta cần áp dụng  chiến lược lai nhiều

phương pháp, ý tuởng và nguyên lý giải một cách phù hợp.

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  55  

Bài tập

1.  Tìm biểu thức giải tích tường minh từ các hệ thức truy hồi sau:

+

a.  an = a  + 2n

,  ∀n ≥ 1  n-1

a0 = 1

+

b.  an = 2a  + 1,  ∀n ≥ 1  n-1

a0 = 1

c.  *Fn = b1 F  + b n-1 2 F ,  ∀n ≥ 2  với {F n-2 0, F1}, b1, b2 cho trước (trường hợp tổng

quát)

+

Dãy số Fibonacci:  d.

= 0,  F F0  1= 1

Fn = F  + F ,  ∀n ≥ 2   n-1 n-2

e.  *un – 4u  + 4u  = 2n,  ∀n ≥ 2  n-1 n-2

= 0, u u0  1 = 1

f.  *T(n) = 2T(n/2) + n – 1, ∀ n ≥ 2

T(1) = 1

g.  un = u  - u n-1 n u ,  ∀n ≥ 1  n-1

= 1  u0

h.  **c0 = 0, c1 = 0

=

≥∀++=

1n

1k

k n 2n,c

n

2

1nc

2.  Thể hiện phương pháp “chia để trị”bằng các thuật toán đệ qui để giải các bài

toán sau:

+

Tìm min và max của một dãy số.  a.

b.  *Tráo đổi hai phần của mảng (không nhất thiết có độ dài bằng nhau) mà

không dùng mảng phụ. 

Viết  thuật toán  (bằng nhiều phương pháp khác nhau, nếu có thể)  và cài đặt

chương trình để giải các bài toán sau (trong đó có các ví dụ đã được giới thiệu

trong lý thuyết):

(Nguyên lý qui hoạch động)

+

Ví dụ 2: bài toán người giao hàng.  3.

4.  Ví dụ 3 (bài toán sắp ba lô có giá trị lớn nhất): Có n loại đồ vật có khối lượng

{ai} và giá trị {ci} vào một ba lô có khối lượng w (nguyên dương) . Giả sử mỗi

loại đồ vật có đủ nhiều để xếp. Hãy tìm cách xếp sao cho đạt giá trị cao nhất.

5.  Tìm dãy các hệ số của nhị thức.

6.  Bài toán đổi tiền: Có n loại tiền A1, A2, …, An (nguyên dương). Hãy tìm cách

dùng các loại tiền này để có được số tiền L cho trước (nguyên dương và không

bé hơn loại tiền nhỏ nhất) sao cho tổng số tờ là ít nhất.

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  56  

(Phương pháp vét cạn đơn giản hoặc vét cạn có quay lui)

7.

+

Ví dụ 4.

8.  Bài toán 8 quân hậu.

9.

+

Bài toán mã đi tuần (chú ý có thể sử dụng phần tử cầm canh).

10.  *Tìm các tập con của dãy A các số nguyên dương sao cho có tổng bằng một số

nguyên dương M cho trước (sắp vừa khít các đồ vật vào ba lô).

(Nguyên lý  mắt lưới)

11.

+

Ví dụ 5: tìm nghiệm gần đúng của phương trình f(x) = 0 trên [a, b], với f(x)

liên tục trên [a,b].

(Phương pháp sinh và thử)

12.

+

Ví dụ 6: bài toán tìm các bộ chỉnh hợp lặp.

13. Tìm các bộ tổ hợp, chỉnh hợp (không lặp).

(Phương pháp nhánh cận)

14.

+

Ví dụ 7: bài toán người du lịch.

(Phương pháp Monte-Carlo)

15.

+

Ví dụ 8: tìm số π  từ công thức tính diện tích hình tròn hoặc thể tích hình cầu.

16.

+

Ví dụ 9: Kiểm tra giả thuyết Fermat bằng thực nghiệm.

17.

+

Tính gần đúng diện tích hình phẳng giới hạn giữa trục hoành và đồ thị hàm số

liên tục không âm trên đoạn [a, b].

(Thuật giải di truyền - GA)

18.  *Ví dụ 10: giải phương trình x2

 = 25.

(Nguyên lý  mê cung )

19.

+

Ví dụ 11: bài toán mê cung.

20.

+

Dựa trên thuật toán TìmKiếm nhằm tìm lời giải đầu tiên cho bài toán BT3, ta

có thể cải biên để thu được thuật toán vét cạn dưới dạng lặp VétCạn để tìm mọi

lời giải của bài toán sau (bài tập):

- Bài toán 4: Problem_4(G = (S, A), x0, DICH)    (BT4)

Cho đồ thị G = (S, A), với đỉnh xuất phát x0∈ S, tập đích DICH ⊂ S.

Hãy tìm mọi đường đi từ x0 đến một đỉnh nào đó thuộc tập DICH.

(Các phương pháp tìm kiếm trong không gian trạng thái theo chiều rộng, chiều

sâu, sâu dần, cực tiểu)

21.

+

Ví dụ 12: bài toán Toci hay n2

 – 1 số.

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  57  

(Các phương pháp tìm kiếm trên đồ thị VÀ/HOẶC theo: chiều rộng, chiều sâu, sâu

dần, cực tiểu)

22.

+

Ví dụ 13: bài toán tháp Hà Nội với n = 2, 3.

(Phương pháp GPS)

23. Bài toán chứng minh tính hằng đúng của các biểu thức lôgic sau:

+

 [a → (b → c)] ⇔ [c ∨ ¬ b ∨ ¬a]  a.

b.  *[a ∧ (a∨c → b) ∧ (a∧b → c∧d)]  ⇒  d 

(Phương pháp heuristic)

24.  Tìm thuật giải heuristics để giải bài toán tháp Hà Nội với:

+

n = 3   a.

b.  *n ≥ 2 bất kỳ.

25.

+

Bài toán Toci hay n2

 – 1 số

.

26.

+

Tìm đường đi ngắn nhất từ thành phố A đến thành phố B dựa trên bản đồ các

thành phố sau đây, trong đó ngoài các khoảng cách thật sự giữa hai thành phố

được chỉ ra bởi các số trên các cung, còn có dãy h0

 chứa chiều dài ước lượng từ

một thành phố bất kỳ đến B là khoảng cách đường chim bay từ thành phố đó

đến thành phố B.

A  B  C  D  F  G  L  M  O  P  R  S  T  Z

366  0  160  242  178  77  244 241 380 98  193 253  329  374

Dãy h0

 chứa khoảng cách ước lượng từ các đỉnh đến B

           O

      71  

  Z

           75  151

        A

                    140       S

     118     99      F

           88    R

    T             97       211

   111

           L         146      P

          70       100        B

    M         66

         75       G

     D     120    C

(Bản đồ thành phố)

   Chöông II - Caùc phöông phaùp giaûi quyeát vaán ñeà  58  

(Nguyên lý  tham lam)

27.

+

Ví dụ 25: bài toán người du lịch.

28.  *Bài toán tô màu (trên đồ thị).

(Nguyên lý  leo đồi) 

29. Tìm đường đi từ đỉnh 1 đến một trong các đỉnh thuộc tập DICH = {5} trên đồ

thị sau với giá thành lớn nhất:

       5

           1      

      7         4  5  4

           2                 3       

     1     2          3         6

                1

(Nguyên lý sắp thứ tự và nguyên lý trùng khớp nhất )

30.

+

Ví dụ 26: bài toán phân công công việc.

31.

+

Ví dụ 27: bài toán sắp xếp vào container.

   Chöông III - Bieåu dieãn vaø xöû lyù tri thöùc  59

Chương III

BIỂU DIỄN VÀ XỬ LÝ TRI  THỨC

III.1. Khái niệm về biểu diễn và xử lý tri thức

 III.1.1. Từ  dữ liệu đến tri thức 

Dữ liệu → Thông tin → Tri thức → Siêu tri thức và Cơ sở tri thức

        Siêu tri thức

            Mức độ trừu tượng 

     Tri thức

      Thông tin

     Dữ  liệu

Dữ liệu thô, chưa được xử lý

Dữ liệu -   (Data): được biểu diễn dưới dạng chuỗi số, ký tự hay hỗn hợp cả

hai loại mà tự bản thân nó không có ý nghĩa độc lập. 

Thông tin -   (Information): là dữ liệu được tổ chức có đầy đủ ý nghĩa đối với

người nhận nó.

Tri thức -   (Knowledge): là những thông tin liên quan đến một vấn đề nào đó

được tổ chức  để có thể giải quyết  được vấn  đề.  Siêu tri thức (Meta

Knowledge)  là loại tri thức ở mức cao, nó có tính khái quát và trừu tượng

hơn tri thức.

Cơ sở tri thức  -   (CSTT, Knowledge Base): là tập hợp các tri thức liên quan

đến một vấn đề được sử dụng trong một hệ thống trí tuệ nhân tạo. Nó không

chỉ bao gồm các sự kiện mà còn chứa các  luật suy diễn và nó có thể được

sắp xếp lại  khi có thêm một tri thức mới làm thay  đổi mối liên hệ giữa

chúng. Hệ chuyên gia (Expert system) là một hệ cơ sở tri thức (Knowledge-

based system) được xây dựng từ tri thức của các chuyên gia trong một lĩnh

vực nào đó.

Chương Trình = Cấu Trúc Dữ Liệu  + Thuật Toán

HỆ CHUYÊN GIA = CSTT + SUY LUẬN   Chöông III - Bieåu dieãn vaø xöû lyù tri thöùc  60

III.1.2. Một số đặc trưng của tri thức: tự giải thích nội dung, có cấu trúc

(sự phân cấp giữa các khái niệm và mối quan hệ giữa chúng), có tính liên hệ

giữa các tri thức, tính chủ động.

III.1.3. Phân loại tri thức

 Có thể phân loại tri thức theo các quan niệm sau:

* Tri thức tất định và bất định:

- Tri thức tiền định (hay tất định) : độc lập với cảm giác, có tính phổ quát

và phi mâu thuẫn

- Tri thức tất yếu (hay bất định): thu được từ kinh nghiệm mà tính đúng –

sai của nó chỉ có thể kiểm tra bằng cảm giác hay kinh nghiệm với độ tin

cậy nào đó và có thể có một “mức độ mâu thuẫn” nào đó.

* Tri thức tồn tại dưới hai dạng cơ bản:

- Tri thức định lượng: thường gắn với các loại heuristics khác nhau, nó phụ

thuộc vào chất lượng các hàm đánh giá heuristic và có thể được dùng làm

cơ sở để chọn các chiến lược điều khiển.

 - Tri thức định tính gồm 3 dạng chính:  

   . Tri thức mô tả (hay khai báo): cho những thông tin về một sự  kiện,

hiện tượng hay quá trình mà không đưa ra thông tin về cấu trúc bên trong

cũng như phương pháp sử dụng bên trong tri thức đó. Nó cho phép mô tả

các mối liên hệ, ràng buộc giữa các đối tượng.

   . Tri thức thủ tục: cung cấp các phương pháp cấu trúc tri thức, ghép nối,

suy diễn và tổng hợp các tri thức mới từ các tri thức đã có. Nó tạo cơ sở

cho công nghệ xử lý tri thức: suy diễn, qui diễn, qui nạp, học tri thức.

   . Tri thức điều khiển: dùng để điều khiển, phối hợp các nguồn tri thức

thủ tục và tri thức mô tả khác nhau. 

III.1.4. Các phương pháp biểu diễn tri thức: thông qua lôgic (lôgic cổ điển

hay tất định: lôgic mệnh đề hay vị từ; lôgic bất định: lôgic tình huống, lôgic

xác suất, lôgic khả xuất, lôgic mờ),  luật sinh, mạng ngữ nghĩa, khung

(frame), ... 

III.1.5.  Các phương pháp xử lý tri thức:  chuyển  đổi, suy luận (chứng

minh tự động, các phương pháp giải quyết vấn đề, …),  tổng hợp  tri thức

(liên kết, sắp xếp, khái quát hoá, …).

   Chöông III - Bieåu dieãn vaø xöû lyù tri thöùc  61

Trong bất kỳ một hệ thống biểu diễn tri thức đều có chứa 3 yếu tố: ngôn

ngữ biểu diễn, cơ chế suy dẫn và công cụ tạo lập cơ sở tri thức cho từng lĩnh vực

cụ thể.

III.2. Một số phương pháp biểu diễn tri thức

III.2.1. Biểu diễn tri thức nhờ lôgic

Lôgic cổ điển (hay tất định) a.   gồm : lôgic mệnh đề, lôgic vị từ

* Lôgic mệnh đề: 

- Xét các mệnh đề p chỉ có thể nhận một trong hai giá trị lôgic: đúng

(True, 1) hoặc sai (False, 0). 

- Các phép toán lôgic nối kết các mệnh đề: phép hội (and, ∧, và),

phép tuyển (or, ∨, hoặc), phủ định (not, ¬ , không), kéo theo (⇒, →),

tương đương (⇔, ↔, ≡), …

  - Vài phép biến đổi tương đương:

   . (a ↔ b) ⇔ ((a → b)  ∧ (b → a))

   . (a → b) ⇔  (¬a ∨  b) ⇔   (¬b  ⇒  ¬a)

) (:)()();()(

1 1 1 1

DeMorgan a aVaVa i

ni

i

ni

i

ni

i

ni

¬ Λ = ¬ ¬ =Λ¬ ≤≤ ≤≤ ≤≤ ≤≤

             . a ∧ (b ∨ c) ⇔ (a ∧ b) ∨ (a ∧ c); a ∨ (b ∧ c) ⇔ (a ∨ b) ∧ (a ∨ c): phân phối

. Các phép toán  ∧, ∨ có tính giao hoán , kết hợp, lũy đẳng.

- Các phép toán lôgic được thực hiện theo  thứ tự ưu tiên giảm dần

sau: phủ định, hội, tuyển, kéo theo, tương đương. 

- Mọi biểu thức logic mệnh  đề  đều có thể  đưa về dạng biểu thức

tương đương chỉ chứa các phép ∧, ∨ và ¬ dưới dạng:

    . chuẩn hội: 

)aV( ki

mki1 ni1 i

≤≤ ≤≤

Λ

     . chuẩn tuyển:

)a(V ki

mki1 ni1 i

≤≤ ≤≤

Λ

     trong đó: aki = pki hoặc aki = ¬pki, với pki là mệnh đề đơn.

- Giá trị chân lý của một biểu thức có thể được tính dựa trên bảng

chân   lý. Nếu biểu thức có n biến mệnh đề khác nhau thì để xác định

giá trị chân lý của nó ta phải xét đến 2n

 bộ giá trị của các biến mệnh đề

(hạn chế là sẽ bị bùng nổ tổ hợp).    Chöông III - Bieåu dieãn vaø xöû lyù tri thöùc  62

- Để chứng minh một biểu thức lôgic mệnh đề là đồng nhất đúng, ta

có thể dùng: bảng chân lý, các phép biến  đổi tương  đương, số lôgic,

phương pháp GPS, các thuật toán: Robinson, Wong Havard (Vương

Hạo), suy diễn tiến, suy diễn lùi mà ta sẽ xét trong phần sau. 

 

- Một luật suy diễn ở dạng chuẩn Horn được biểu diễn như sau:

  p1∧ .... ∧pn     →       q1 ∨ .... ∨ qm      (1)

   Ta thường gặp các trường hợp đặc biệt sau:

     . n = 0, m = 1: biểu diễn các sự kiện (facts)

     →  q = F(t1, ...., tk); 

. n ≥ 1, m = 1: ta có dạng chuẩn sơ cấp dùng để biểu diễn các luật

(rules):

  p1∧ .... ∧pn     →       q      (2)

   Mọi luật (1) đều có thể biểu diễn dưới dạng (2):

    (1)     ⇔ (p1∧ .... ∧pn  ∧ ¬q ∧ .... ∧¬q 2  m  →  q1})   

-  Quá trình suy diễn  có thể dựa trên 2 nguyên lý:

B

BAA,

  : Ponens Modus

A

BAB,

  : Tollens Modus

¬

→ ¬

- Cơ sở tri thức biểu diễn bằng logic mệnh đề: dựa trên tập các sự

kiện Facts và tập các luật suy diễn Rules (tương tự cách xây dựng

một chương trình trong ngôn ngữ lập trình PROLOG).

* Lôgic vị từ:

- Vị từ p(x1, x2, ... , xn) là một phát biểu chứa các biến mệnh đề x1,

x2, ... , xn sao cho khi chúng nhận các giá trị lôgic cụ thể thì nó cũng

nhận một trong hai giá trị lôgic đúng hoặc sai. 

- Trong logic vị từ, ngoài các phép toán trong logic mệnh đề, người 

ta còn sử dụng  lượng từ tồn tại (ký hiệu là ∃) và  lượng từ với mọi (ký

hiệu là ∀).

Ví dụ 1 -  : Vị từ p(x, y, z)  biểu diễn đẳng thức: x.y = z, cho ta một vị

từ của 3 biến thực x, y, z. Tính chất giao hoán của phép nhân được diễn

tả: 

      ∀ x, y  p(x,y,z) ⇒  p(y,x,z)

- Logic vị từ cho phép diễn đạt hầu hết các khái niệm và nguyên lý

khoa học cơ bản, nhất là toán học và vật lý. Có 2 phạm vi ứng dụng cơ

bản của lôgic hình thức là: giải quyết vấn đề và chứng minh định lý tự

động.    Chöông III - Bieåu dieãn vaø xöû lyù tri thöùc  63

- Ngoài việc dùng lôgic vị từ thông thường (lôgic vị từ bậc 1), người

ta còn nghiên cứu thêm lôgic vị từ bậc cao. Lôgic vị từ  bậc n là lôgic vị

từ  mà các biến vị từ của nó thuộc lôgic vị  từ bậc n-1.

* Ưu điểm của phương pháp lôgic:

 - Là ngôn ngữ biểu diễn kiểu mô tả;

- Có khả năng suy diễn với các cơ chế quen thuộc: modus ponens,

modus tollens;

 - Khá trực quan đối với người sử dụng;

- Khá  gần gũi về mặt cú pháp với các câu lệnh của ngôn ngữ lập

trình logic như PROLOG;

 - Có thể dùng để mô tả cấu trúc mô hình và xử lý động mô hình;

 - Có thể kiểm tra tính mâu thuẫn trong cơ sở tri thức;

- Tính môđun hoá cao, do đó có thể thêm, bớt, sửa các tri thức khá

độc lập với nhau và có cả cơ chế suy diễn.

* Nhược điểm của phương pháp lôgic:

- Mức độ hình thức hoá quá cao, dẫn tới khó hiểu ngữ nghĩa của các

vị từ khi xem xét chương trình.

- Năng suất xử lý thấp (khó khăn cơ bản trong quá trình suy diễn là

cơ chế hợp và suy diễn vét cạn).

- Do tri thức được biểu diễn bằng logic vị từ nên các ưu thế sử dụng

cấu trúc dữ liệu không được khai thác triệt để.

 

b.  Lôgic bất định gồm: lôgic tình huống (các kết xuất của nó có thể rút ra

từ các tình huống không gian và thời gian xác  định có liên quan với

nhau), lôgic xác suất, lôgic khả xuất và lôgic mờ.

III.2.2. Biểu diễn tri thức nhờ luật sinh: thông qua các luật nếu … thì:

  Nếu P1, P2, … và Pn  thì Q , hay:

P1 ∧  P2 ∧ … ∧ Pn  → Q

  Cách biểu diễn này được áp dụng trong:

- Lôgic mệnh đề và vị từ: với P1,P2,…, Pn  là các biểu thức lôgic và →

là phép kéo theo

- Ngôn ngữ lập trình: luật sinh là các câu lệnh

  if  (P1 and P2  and … and Pn) then Q ;

- Ngôn ngữ tự nhiên: luật sinh là phép dịch.

- Hệ chuyên gia: là một hệ cơ sở tri thức, gồm:

     . CSDL các sự kiện F (Facts)  : {f1, ..., fn}

   . Tập luật R (Rules) gồm các luật có dạng:   Chöông III - Bieåu dieãn vaø xöû lyù tri thöùc  64

  qfff

k iii

→ ∧ K 2 1

   . Phương pháp suy diễn tiến và lùi, chẳng hạn:

        FFf,,f,f

k 21 iii

∈ ⇒ ∈  q    K

III.2.3. Biểu diễn tri thức nhờ mạng ngữ nghĩa (Semantic Network)

Ví dụ 2 -  : Ta có thể biểu diễn CSTT:

 . An là người

 . Người thích suy nghĩ  

 . Người biết lao động

thông qua sơ đồ III.2.3.1. 

Theo cơ chế lan truyền thông tin theo các cung nối  được thể hiện

bằng các mũi tên, ta thu được: An thích suy nghĩ và biết lao động.

 

    An    

        là

    Người    

    Thích  biết

Suy nghĩ        Lao động

                                           (Hình III.2.3.1)

Biểu diễn mạng ngữ nghĩa bằng đồ thị -  :

 Mạng ngữ nghĩa là đồ thị có hướng mở rộng: G = (V, R), trong đó:

V = {x1, x2, …, xn} là tập các đỉnh, R = {r1, r2, …, rm} là tập các quan hệ

nhiều ngôi trên V.

Đặc điểm của mạng ngữ nghĩa -  : trực quan, có tính kế thừa và lan truyền

thông tin; khó kiểm tra tính phi mâu thuẫn, …

Ứng dụng của mạng ngữ nghĩa -  : trong xử lý ngôn ngữ tự nhiên (dễ dàng

hơn trong việc hiểu ngữ nghĩa của câu, do đó có thể tiến hành việc đọc và

dịch các bài text), hệ chuyên gia, giải các bài toán thông minh (chẳng hạn,

giải bài toán hệ thức lượng trong tam giác), … 

III.2.4. Biểu diễn tri thức bằng Frame

 - Tương tự như bản ghi (record), nhưng ngoài các trường dữ liệu,

khung (frame) còn có thể có các trường là các thao tác, thủ tục hay luật suy

diễn nào đó, các trường này có thể kế thừa các tính chất từ các khung khác

(tương tự như khái niệm lớp trong lập trình hướng đối tượng OOP). Frame

có tính cấu trúc rất cao.   Chöông III - Bieåu dieãn vaø xöû lyù tri thöùc  65

 - Ví dụ:

 Frame MáyTínhCáNhân;

   { Là một dạng riêng của MáyTínhĐiệnTử;

     KiếnTrúc   {8 bits, 16 bits, 32 bits};

     SốỔĐĩa  {1, .., 5};

     MànHình Thủ tục xác định tính năng của màn hình;

   }

 Ngoài ra, người ta còn có thể biểu diễn tri thức bằng bộ ba liên hợp OAV

(Object -  Attribute – Value), mạng nơron (ANN  - Artificial Neural Network), …

III.3. Xử  lý tri thức tất định bằng phương pháp suy diễn logic

  III.3.1. Các cơ chế lập luận với tri thức tất định

- Suy diễn  (Deduction, diễn dịch): modus ponens, modus tollens, các kiểu

suy diễn: tiến và lùi.

 - Qui diễn (Abduction, tương tự): Cho trước: A → B.

  . A ~  A’ ⇒ A’ → B 

  . B ~  B’ ⇒ A → B’ 

  . A ~  A’ & B ~ B’ ⇒ A’ → B’ 

- Qui nạp (Induction): hoàn toàn và không hoàn toàn

Sau đây ta sẽ xét các thuật toán và thuật giải nhằm chứng minh tự động các

biểu thức lôgic mệnh đề. 

Bài toán A (BTA):Xét bài toán lôgic mệnh đề sau có hằng đúng hay không:

j

mj1

i

mi1

KLVGT ≤≤ ≤≤

⇒ Λ

với {GTi , KLj} là các biểu thức lôgic mệnh đề bất kỳ.  

Tại sao ta chỉ xét bài toán dạng trên đây (bài tập) ?

  III.3.2. Thuật toán Vương Hạo (Wong Havard) giải BTA

 * Ý tưởng: Áp dụng chiến lược “Chia để trị” nhằm tách bài toán xuất phát

thành các bài toán con dạng “VÀ” đơn giản hơn. Bài toán ban đầu sẽ được giải khi

và chỉ khi mọi bài toán con sơ cấp giải được.

   Chöông III - Bieåu dieãn vaø xöû lyù tri thöùc  66

* Trước tiên, ta đưa vế  trái VT  (hay vế phải VP) về dạng chuẩn hội (hay

chuẩn tuyển tương ứng) bằng cách:

. Thay các phép toán tương đương  ↔ (nếu có) bởi các phép toán

kéo theo →

  . Thay các phép toán → bởi các phép toán ¬, ∨

. Dùng các luật De Morgan để bỏ các dấu ¬  của nguyên một nhóm

mệnh đề

. Dùng các  luật phân phối  (nếu chưa gặp dạng chuẩn cần tìm) của

phép tuyển đối với phép hội  (hay của phép hội đối với phép tuyển

tương ứng).

 Trong thuật toán Wong, sau khi đưa VT về dạng chuẩn hội và VP về dạng

chuẩn tuyển, ta sẽ sử dụng các qui tắc sau (hãy chứng minh tại sao ta có quyền sử

dụng chúng ? Bài tập).

 - Thủ tục Chuyển(VT,VP): thay các dấu ∧ các nhóm chính bên VT và các

dấu ∨ các nhóm chính bên VP bởi dấu phẩy; chuyển vế các mệnh đề    chính ở dạng

phủ định ¬p từ  vế này sang vế kia và bỏ đi dấu ¬ (chỉ còn p). Nói cách khác, ta

có thể xem dấu phẩy (,) bên VT là phép hội và dấu phẩy (,) bên VP là phép tuyển.

  . Ví dụ 3: Kiểm tra tính hằng đúng của biểu thức lôgic mệnh đề sau:

VT ≡ (¬a) ∧ (¬b ∨ c)   ⇒  (a ∧ b) ∨ (¬b) ∨  c ≡ VP

 Các nhóm chính trong: vế trái VT là (¬a) và (¬b∨c), vế phải VP là (a∧b),

(¬b) và (c). 

  VT ≡ (¬a) ∧ (¬b ∨ c)   ⇒  (a ∧ b) ∨ (¬b) ∨  c ≡ VP

        Chuyển

          ¬a, ¬b ∨ c    ⇒  a ∧ b, ¬b,  c

        Chuyển

           VT ≡  b, ¬b ∨ c    ⇒  a ∧ b,    a,  c ≡ VP

- Hàm lôgic Tách(VT,VP): tách mỗi nhóm tuyển ∨  chính trong VT  (hay

mỗi nhóm hội ∧ chính trong VP) thành nhiều bài toán con dạng VÀ. Mỗi bài toán

con gồm một mệnh đề trong nhóm chính và giữ nguyên các nhóm chính khác. Nếu

tách được thì thủ tục Tách(VT,VP) nhận giá trị  true; nếu ngược lại, nó nhận trị

false. Với ví dụ 3 trên đây, ta có sơ đồ biến đổi như  hình III.3.2.1 sau đây.

- Với mỗi bài toán con, nếu mỗi vế của nó có một mệnh đề đơn đứng độc

lập giống nhau thì nó được chứng minh (hằng đúng). Bài toán xuất phát chỉ được

chứng minh (hằng đúng) khi mọi bài toán con của nó được chứng minh. Nói cách

khác, bài toán xuất phát không hằng đúng nếu có tồn tại một bài toán con của nó   Chöông III - Bieåu dieãn vaø xöû lyù tri thöùc  67

không hằng đúng (không thể áp dụng qui tắc tách cũng như chuyển được nữa và

hai vế không có chung một mệnh đề đơn đứng độc lập nào cả).

¬b ∨ c             VT ≡  b,      ⇒  a ∧ b,    a,  c ≡ VP

     Tách

b, ¬b  ⇒   a ∧ b,    a,  c           b, c  ⇒   a ∧ b,    a,  c

     Chuyển           Tách

        b  ⇒   a ∧ b,  a,  c, b  b, c  ⇒   a,  c      b, c  ⇒   b,  a,  c

     Tách

b         ⇒   a,  c, b  b  ⇒  a, c, b   CM    CM

 CM    CM

(Hình III.3.2.1)

Vậy bài toán nêu ra trong ví dụ 3  là hằng đúng.

 * Thuật toán Vương Hạo

{ VT = VP = ∅ ;

  for  i = 1 to m  do { ChuẩnHội(GTi);   VT  ← VT U {GTi }; }

  for  i = 1 to n   do { ChuẩnTuyển(KLi);  VP  ← VP U {KLi }; }

 P  ← {(VT,VP)};

  while  (P≠∅)  do 

  {    (VT,VP) ←  get(P);

       if   (VT  ∩ VP = ∅) then  

      {  Chuyển(VT,VP);

   if   (VT  ∩ VP = ∅)   then  

       if  (not(Tách(VT,VP))) 

then  exit(“Không thành công”); // BT không hằng đúng

      }

  }

  write(“Thành công”);     // BT hằng đúng

* Nhận xét: 

. Thuật toán Wong dừng sau một số hữu hạn bước và cho ra thông báo

“Thành công” nếu và chỉ nếu từ  các giả thiết GT1, ..., GTm   có thể suy ra

một trong các kết luận KL1, ..., KLn.

  . Nếu số các phép toán liên kết ∨ trong GTi và ∧ trong KLj là M thì thuật

toán sẽ sinh ra từ M đến 2M dòng (VT,VP) ∈  P (bùng nổ tổ hợp nếu M khá

lớn !).   Chöông III - Bieåu dieãn vaø xöû lyù tri thöùc  68

* Ví dụ 4 (biểu thức hằng đúng): Có thể chứng minh rằng: 

   [(a ∧ b  →  c) ∧ (b ∧ c  →  d) ∧ a ∧ b]   ⇒  d  ?

  Đưa VT về dạng chuẩn hội và VP về dạng chuẩn tuyển:

     VT ≡ {¬a ∨ ¬b ∨ c,  ¬b ∨ ¬c ∨ d, a, b}: dạng chuẩn hội

        VP ≡ {d}: dạng chuẩn tuyển

 

Ta có thể biểu diễn quá trình giải của thuật toán Wong thông qua đồ thị suy

diễn hay đồ thị lời giải như sau: 

¬a ∨ ¬b ∨ c       , ¬b ∨ ¬c ∨ d, a, b   ⇒    d

¬a, ¬b ∨ ¬c ∨ d, a, b  →  d;   ¬b, ¬b ∨ ¬c ∨ d, a, b  →   d;          c, ¬b ∨ ¬c ∨ d, a, b  →   d

¬b ∨ ¬c ∨ d, a, b  →   d, a  ¬b  ∨  ¬c ∨  d, a, b → d, b  

 CM            CM  

      

    c, ¬b, a, b  →    d c, ¬c, a, b  →    d  c, d, a, b →  d 

    c, a, b  →    d, b   c, a, b  →    d, c     CM

         

     CM             CM

   (Đồ thị lời giải trong thuật toán Wong)  

Ví dụ 5 *   (biểu thức không hằng đúng): Kiểm tra tính hằng đúng của biểu

thức: 

[(a ∧ b  →  c) ∧ a]   ⇒  c  ?     (1)

  Đưa VT về dạng chuẩn hội và VP về dạng chuẩn tuyển:

     VT ≡ {¬a ∨ ¬b ∨ c, a}: dạng chuẩn hội

        VP ≡ {c}: dạng chuẩn tuyển

  Đồ thị lời giải: 

      ¬a ∨ ¬b ∨ c, a  ⇒   c

¬b     ¬a, a ⇒ c   , a ⇒ c  c, a ⇒ c

         (2)

       a ⇒ c, b

     Chöông III - Bieåu dieãn vaø xöû lyù tri thöùc  69

BT con (2) sai vì không thể tách, chuyển và không có mệnh đề chung. Do

đó bài toán (1) xuất phát cũng sai.

  III.3.3. Thuật toán Robinson giải BTA 

 * Ý tưởng: Sử dụng: 

  . phương pháp chứng minh phản chứng: 

[a → b đúng] ≡ [a ∧¬b  sai hay mâu thuẫn] 

  . nguyên lý hợp giải: 

(¬a ∨ b) ∧ (a ∨ c) ⇒   b ∨ c. 

(Vì sao? Hãy chứng minh).

- Ta thường gặp các trường hợp riêng của nguyên lý này:

. nguyên lý modus ponens:

  a ∧ (¬ a ∨ b) ⇒  b

 . nguyên lý modus tollens: 

(¬b ∧ (a→b)) ⇒ ¬a

 - Để chứng minh từ các giả thiết GT1, ..., GTm suy ra  một trong các kết

luận KL1, ..., KLn, ta chỉ cần lấy phủ định của KL1, ..., KLn đưa về cùng với các giả

thiết:

)KL()GT(P j

mj1

i

mi1

¬ Λ Λ Λ ≡

≤≤ ≤≤

Nếu suy ra được mâu thuẫn  từ  tập các mệnh đề P thì quá trình chứng minh kết

thúc và kết luận BTA là hằng đúng. 

- Để suy ra được mâu thuẫn, Robinson đã đưa ra nguyên lý hợp giải trên

đây để bổ sung thêm càng ngày càng nhiều các biểu thức mệnh đề trung gian mới

cho đến khi nào trong P có 2 mệnh đề đơn đứng độc lập là phủ định của nhau thì

mâu thuẫn xảy ra.

* Thuật toán Robinson (giải BTA)

{ P = ∅;

  for  i = 1 to m  do { GTi ← ChuẩnHội(GTi);     P  ← P U GTi; }

  for  i = 1 to n do {NotKLi  ← ChuẩnHội(¬KLi); P ← P U NotKLi;}

  if  (MâuThuẫn(P)) then exit(“Thành công”); 

 P1 = ∅;

  while  (P≠ P1  and not(MâuThuẫn(P)))  do {P1 =  P; HợpGiải(P);}

  if  (MâuThuẫn(P)) 

  then exit(“Thành công”); 

  else  exit(“Không thành công”);    Chöông III - Bieåu dieãn vaø xöû lyù tri thöùc  70

         }

trong đó, hàm lôgic MâuThuẫn(P) và thủ tục HợpGiải(P) có nội dung như sau:

Function MâuThuẫn(P): boolean

        { for  each p ∈  P do 

   for  each  q ∈  P and q ≠ p  do 

     if   (p = ¬ q  or  q = ¬ p)   then  return(True);

 return(False);

       }

  Procedure HợpGiải(P)

        { for  each p ∈  P do  

  for  each  q ∈  P and q ≠ p  do 

   if   (p = ¬ a ∨ b   and  q = a ∨ c)  then  P  ← P U {b ∨ c};

        } 

* Ví dụ 6 (biểu thức hằng đúng):  Xét bài toán trong ví dụ 4, ta có:

   P ≡  {¬a ∨ ¬b ∨ c, ¬b ∨ ¬c ∨ d, a, b, ¬d}

  Để tiện theo dõi, ta biểu diễn quá trình thực hiện thuật toán qua đồ thị hợp

giải như sau:

  ¬a  ∨  ¬b  ∨  c   a  b  ¬b ∨  ¬c ∨  d       ¬d

   ¬b  ∨  c              ¬c  ∨  d    

               c    ¬ c

          Mâu thuẫn !

* Ví dụ 7 (biểu thức không hằng đúng): Kiểm tra tính hằng đúng của biểu

thức: 

[(a ∧ b  →  c) ∧ a]   ⇒  c  ?    

 Lấy phủ định của kết luận và đưa về dạng chuẩn hội, ta có:

  P ≡  {¬a ∨ ¬b ∨ c, a, ¬c}

  Đồ thị hợp giải:

     ¬a ∨ ¬b ∨ c, a, ¬c

     ¬b ∨ c  ¬a ∨ ¬b

      ¬b

      Không thấy mâu thuẫn cũng không còn cách nào hợp giải mà có thể

sinh ra mệnh đề mới nữa. Vậy bài toán xuất phát sai.   Chöông III - Bieåu dieãn vaø xöû lyù tri thöùc  71

 * Nhận xét: 

   - Thủ tục Robinson sẽ  dừng sau hữu hạn bước  và cho ra thông báo

“Thành công” nếu và chỉ nếu GT1  ∧ ...  ∧ GTm  ⇒  KL1  ∨ ... ∨ KLn.

    - Để chứng minh BTA không hằng đúng, ta phải hợp giải hết tất cả các

khả năng cho đến khi không thể sinh ra thêm biểu thức mệnh đề nào mới

trong P.

   - Cũng như thủ tục Wong H.,  thủ tục Robinson có nhược điểm là tùy theo

thứ tự lấy các cặp mệnh đề để hợp giải  có thể xảy ra hiện tượng tràn bộ

nhớ (do bùng nổ tổ hợp) đối với các bài toán có kích thước lớn.

- Có thể áp dụng một số heuristic cho thuật toán trên để thu được “thuật

giải Robinson”, chẳng hạn: khi áp dụng nguyên lý hợp giải, sau khi thêm

vào tập P mệnh đề (b ∨ c), có thể bỏ hai mệnh đề (¬a ∨ b) và (a ∨ c) ra

khỏi P.

* Ví dụ 8 (biểu thức hằng  đúng nhưng có thể kết luận sai nếu áp dụng

heuristic không đúng): Kiểm tra tính hằng đúng của biểu thức: 

   [ a ∧ (a → b) ∧ (a → c)] ⇒ c     (3)

 Lấy phủ định của kết luận và đưa về dạng chuẩn hội, ta có:

  P ≡  {¬a ∨ b, ¬a ∨ c, a, ¬c}

. Nếu hợp giải trước hai mệnh đề ¬a ∨ b và a để được b rồi loại chúng thì

trong P còn lại:

  P ≡ {b, ¬a ∨ c, ¬c}

Hợp giải tiếp tục thì: P ≡ {b, ¬a}: không có mâu thuẫn cũng không thể hợp

giải được nữa: bài toán sai chăng ? Thật ra, bài toán (3) vẫn đúng !

. Nếu hợp giải trước hai mệnh đề ¬a ∨ c và a để được c rồi loại chúng thì

trong P còn lại:

P ≡ {c, ¬a ∨ b, ¬c}

Khi đó mâu thuẫn xảy ra giữa c và ¬c. Vậy bài toán xuất phát đúng !

 *  Chú ý:  Để tránh tình trạng kết luận sai như trên, khi  thay thuật toán

Robinson thành “thuật giải Robinson”, ta nên chú ý rằng ngoài việc thêm heuristic

trên vào thủ tục hợp giải, ta phải bỏ đi thông báo exit(“Không thành công”). Nếu

xuất hiện mâu thuẫn thì kết luận “Thành công” và dừng. Nếu không xuất hiện mâu

thuẫn  thì không được thông báo exit(“Không thành công”), nghĩa là không được

quyền kết luận gì khi tình trạng này xảy ra !

 * Bài toán B (BTB)

  Input:  - Tập các mệnh đề giả thiết: GT = {g1, ... , gn} = ∧1≤i≤n gi  Chöông III - Bieåu dieãn vaø xöû lyù tri thöùc  72

  - Tập RULE  gồm m  luật có dạng chuẩn  r : p1∧ ... ∧ pn → q 

  - Tập các mệnh đề kết luận: KL = {q1, ... , qk} = ∧1≤i≤k qi

Output: Thông báo “Thành công” nếu mọi qi ∈ KL  có thể suy diễn từ GT 

   nhờ sử dụng tập luật RULE.

 - Để chứng minh các bài toán lôgic mệnh đề, người ta còn dùng phương

pháp suy diễn tiến (hay lùi), bằng cách xuất phát từ giả thiết (hay kết luận tương

ứng), dựa trên các nguyên lý suy diễn: 

   . Modus ponens:  A, A → B

              B

 . Modus tollens: ¬B, A → B

            ¬A

ta thêm vào các mệnh đề mới được chứng minh đúng (hay các mệnh đề cần phải

chứng minh thêm  tương ứng) cho đến khi thu được kết luận (hay giả thiết  tương

ứng) thì dừng.

  III.3.4. Thuật toán suy diễn tiến giải BTB

 * Ý tưởng: Quá trình suy diễn tiến bắt đầu từ tập TrungGian các mệnh đề

đúng xuất phát  (tập giả thiết) và nó  sẽ được “làm nở” dần bằng cách thêm vào

các sự kiện mới đúng nhờ các luật suy diễn trong RULE, cho đến khi các kết luận

cần chứng minh được phát hiện.

 * Thuật toán suy diễn tiến SDT 

{ TrungGian = GT;

 THỎA   = Lọc(RULE, TrungGian); 

           // THỎA là tập các luật r có dạng  p1∧ ... ∧ pn → q mà pi∈TrungGian,∀i=1..n 

  while  (KL ⊄ TrungGian  and  THỎA ≠ ∅)  do

    {  r ← get(THỎA);        // r ∈ THỎA có dạng  r: p1∧ ... ∧ pn → q

      TrungGian ← TrungGian U {q};

  RULE ← RULE \ {r};

  THỎA = Lọc(RULE, TrungGian)

    }

  if  (KL ⊂ TrungGian)  

  then  write(“Thành công”)

  else   write(“Không thành công”);

}

Ví dụ 9 *  : Cho trước tập các sự kiện giả thiết: GT = {a, b}. Sử dụng tập

RULE các luật:

 r1: a → c

 r2: b → d   Chöông III - Bieåu dieãn vaø xöû lyù tri thöùc  73

 r3: a → e

 r4: a ∧ d → e

 r5: b ∧ c → f

 r6: e ∧ f → g

Cần suy ra kết luận: KL = {g}.

. Ta có: TrungGian = {a, b};  RULE = {r1-r6}. Do đó THỎA = {r1,r2,r3}. 

 . Áp dụng luật r1: a → c, ta được TrungGian = {a,b,c} và RULE={r2-r6}. 

   Do đó:  THỎA = {r2, r3, r5}. 

. Lúc này, KL = {g} ⊄ {a, b, c} = TrungGian và THỎA  ≠ ∅.

 Ta lại tiếp tục quá trình, bằng cách chọn r2 để thực hiện, ... 

 Ta minh họa quá trình trên bằng đồ thị suy diễn tiến, trong đó mỗi đỉnh ứng

với tập TrungGian tại thời điểm đang xét và sẽ có một cung đi từ đỉnh TrungGian

đến đỉnh TrungGian U {q} tương ứng với luật r đã được lọc và có dạng r : p1∧ ...

∧ pn → q mà pi ∈ TrungGian, ∀i=1,.., n.

      TrungGian ={a,b} 

            r1 r2      r3

   TrungGian = {a,b,c}  {a,b,d}  {a,b,e}

               r2 r3       r5

TrungGian ={a,b,c,d}  {a,b,c,e}  {a,b,c,f}

            r3       r4    r5 

TrungGian ={a,b,c,d,e}

      r5  r4

TrungGian ={a,b,c,d,e,f}

     r6         r4

     KL ⊂ TrungGian ={a, b, c, d, e, f, g} 

   (Đồ thị suy diễn tiến)

Ví dụ 10 *   (biểu thức không hằng đúng): Kiểm tra tính hằng đúng của biểu

thức:    Chöông III - Bieåu dieãn vaø xöû lyù tri thöùc  74

[(a ∧ b  →  c) ∧ a]   ⇒  c  ?    

 TrungGian = GT = {a}, KL = {c}, RULE = {r1: a ∧ b  →  c }.

 Khi đó: THỎA = ∅ và KL ⊄ TrungGian. Vậy bài toán trên không

hằng đúng.

 * Nhận xét:

    .Với các thứ tự khác nhau để chọn r từ  tập THỎA, ta sẽ có các cơ chế

duyệt theo chiều sâu, theo chiều rộng, ...  trên đồ thị.

. Nếu trong BTB  thay tập kết luận KL = ∧1≤i≤k qi  bởi: KL = V1≤i≤k qi thì

cần hiệu chỉnh thuật toán SDT ra sao ? (Bài tập)

. Để tránh các kết luận sai, với mỗi biến mệnh đề p xuất hiện trong biểu

thức cần chứng minh, bằng các qui tắc biến đổi tương đương lôgic, chuyển

về cùng dạng p hoặc cùng dạng ¬p.

 III.3.5. Thuật toán suy diễn lùi giải BTB

* Với suy diễn lùi, để đưa ra kết luận B ta thử tìm tất cả các luật có dạng:

  A1∧ ... ∧ An  →  B

  Để có B, ta cần chứng minh A1 ,... , An (các kết luận mới được thêm vào tập

kết luận). Quá trình xác định Ai cũng diễn ra tương tự như đối với B. Nếu đến một

lúc nào đó tìm thấy một Aio nào đó không thể dẫn xuất được từ các giả thiết thì ta

quay lui sang luật khác sinh ra B và lại tiếp tục quá trình trên. Nếu không tìm được

Aio như vậy (nghĩa là mọi Ai đều được dẫn xuất từ giả thiết) thì quá trình dẫn xuất

ra B thành công.

  Để thực hiện quá trình quay lui, ta sử dụng hai tập có cấu trúc ngăn xếp

GOAL và VET: GOAL là tập lưu các mệnh đề cần phải chứng minh đến thời điểm

đang xét và VET là tập lưu các luật đã sử dụng để chứng minh các đích (kể cả đích

trung gian). 

 

  * Thuật toán suy diễn lùi SDL

       {1 if  (KL ⊂ GT) 

  then  exit(“Thành công”);

  else {2 GOAL  = ∅; 

VET = ∅; 

CMĐược = True;

   for each  q ∈  KL  do  

GOAL = GOAL  U  {(q,0)};

   repeat     Chöông III - Bieåu dieãn vaø xöû lyù tri thöùc  75

       {3 (f,i) ← get(GOAL);  

    if (f ∉ GT) then

    {4 Tìm_Luật(f, i, RULE, j);     // Tìm luật rj : leftj → f 

       if  (j ≤ m) 

       then { VET = VET  U {(f,j)};

       for each  t ∈leftj\GT   do  GOAL=GOAL U {(t,0)};

     }

       else  {5  back = true;      // dùng biến này để quay lui 

        while  (f ∉ KL  and  back)  do

        {6 repeat {(g,k)←get(VET); 

// Lấy luật rk:leftk→g ra khỏi VET 

// để quay lui đến luật khác mà cũng → g 

         GOAL =  GOAL \ leftk;  

               }

           until  (f ∈ leftk);

// Bỏ hậu quả của việc chọn sai luật r và dẫn đến việc 

// không CM được mệnh đề f được giả định cần CM

           Tìm_Luật(g, k, RULE, s); // Tìm luật rs:lefts → g 

            if  (s ≤ m) 

            then  { for each  t ∈ lefts \ GT   do  

        GOAL = GOAL U {(t,0)};

               VET = VET  U {(g,s)}; back = false;

          } 

            else  f = g;

        }6

        if (f ∈ KL and back) then CMĐược = False;

     }5

    }4

       }3 

   until  (GOAL =  ∅  or  not(CMĐược));

   if   (not(CMĐược))  then  exit(“Không Thành công”)

   else  exit(“Thành công”);

         }2

       }1

 Trong thủ tục trên ta sử dụng thủ tục: Tìm_Luật(f, i, RULE, k)  để tìm xem

có luật  rk  nào kể từ  luật thứ  i+1 trở đi mà cũng suy ra được f (rk:leftk →f). Nếu

không có luật nào như thế thì qui ước lấy k = m+1.

 * Ví dụ 11: Cho trước tập các sự kiện giả thiết  GT = {a, b}. Sử dụng tập

RULE các luật:   Chöông III - Bieåu dieãn vaø xöû lyù tri thöùc  76

 r1.     a ∧ b → c

 r2. a ∧ h → d

 r3. b ∧ c → e

 r4. a ∧ d → m

 r5. a ∧ b → o

 r6. o ∧ e → m

Cần suy ra KL = {m}.

 Ban đầu GOAL = VET = ∅;

 Áp dụng thủ tục Tìm_Luật(m, 0, RULE, j), ta được j = 4 (r4 là luật đầu tiên

sinh ra m). Khi đó VET = {(m,4)}; GOAL  = {(d,0)} (vì a ∈ GT nên chỉ cần xét

(d,0)).

 Ta tiếp tục quá trình và có bảng theo dõi sau:

GOAL              (f,i)  CMĐ j        leftj\GT    VET                       (g,k)  l   leftl\GT  Quaylui

(back) 

{(m,0)} (m,0) 1 true d  {(m,4)} 

{(d,0)}  (d,0)  2 h  {(d,2);(m,4)}

{(h,0)}  (h,0)  7   {(m,4)}                   (d,2)    7  true

∅  d     ∅                    (m,4)   6  o,e   

{(o,0),(e,0)}      {(m,6)}    false(thoát while 6)

{(e,0)}  o  5  ∅  {(o,5),(m,6)}

∅  e  3 c  {(e,3),(o,5),(m,6)}

{(c,0)}  c  1  ∅       {(c,1),(e,3),(o,5),(m,6)}

(GOAL = ∅ : thoát vòng while{2}2   ; CMĐược = True: Thành công !)

Ta có thể biểu diễn quá trình suy diễn lùi trên  đây thông qua  đồ thị

(VÀ/HOẶC) suy diễn lùi như sau:  

      {m}*   

          r4    r6

    {a,d}      {o,e}*  k

  *{a}   {d}    {o}*   {e}*  k

Không cần phần này !  

    r2  r5  r3

     {a,h}    {a,b}*   {b,c}*  k

   *{a}  {h}     *{a}       *{b}    *{b}   {c}*   k

               r1 

            {a,b}*

         *{a}   *{b}

(Đồ thị suy diễn lùi)   Chöông III - Bieåu dieãn vaø xöû lyù tri thöùc  77

 - Ví dụ 12: Cho trước tập các sự kiện giả thiết  GT  =  {a}. Sử dụng tập

RULE các luật:

 r1.     a ∧ b → c

 r2. a ∧ h → d

 r3. b ∧ c → e

 r4. a ∧ d → m

 r5. a ∧ b → o

 r6. o ∧ e → m

Cần suy ra KL = {m}.

 Ban đầu GOAL = VET = ∅. Áp dụng thủ tục Tìm_Luật(m, 0, RULE, j), ta

được j = 4 (r4 là luật đầu tiên sinh ra m). Khi đó VET = {(m,4)}; GOAL  = {(d,0)}

(vì a ∈ GT nên chỉ cần xét (d,0)).

 Ta tiếp tục quá trình, sẽ có bảng theo dõi sau:

GOAL              (f,i)  CMĐ j        leftj\GT    VET                       (g,k)  l   leftl\GT  Quaylui

(back) 

{(m,0)} (m,0) 1 true d  {(m,4)} 

{(d,0)}  (d,0)  2 h  {(d,2);(m,4)}

{(h,0)}  (h,0)  7   {(m,4)}                   (d,2)    7  true

∅  d     ∅                    (m,4)   6  {o,e}

{(o,0),(e,0)}      {(m,6)}     (thoát while 6) false

{(e,0)}  o  5 b  {(o,5),(m,6)}

{(b,0),(e,0)} b  7  ∅  {(m,6)}              (o,5)    7 true

∅  o     ∅             (m,6)   7 

∅  m         false

; do đó: CMĐược= false (và GOAL = ∅): thoát vòng while{ ;    (f = m ∈ KL : thoát while {6}6 2}2

  CMĐược= false: Không thành công !)

Đồ thị suy diễn lùi:  

      {m}      k

          r4    r6

    {a,d}      {o,e} k k

  *{a}   {d}    {o}     {e} k k k

    r2  r5  r3

     {a,h}    {a,b}     {b,c} k k k

           Không cần phần này !

    *{a}       {b}    *{a}  {h}k k     {b}    {c} k k

               r1 

            {a,b}k

      (Đồ thị suy diễn lùi)  *{a}  {b}k

   Chöông III - Bieåu dieãn vaø xöû lyù tri thöùc  78

 * Nhận xét:

   - Quá trình suy diễn lùi  tương tự như quá trình tìm đồ thị con lời giải

trong đồ thị VÀ/HOẶC biểu diễn tập luật.

- Để  tăng hiệu quả  của thủ tục suy diễn lùi có thể  đưa vào 2 tập: tập

ĐÚNG chứa các sự kiện đã được khẳng định là đúng và tập SAI chứa các

sự kiện đã được khẳng định là sai. Mỗi khi lấy một sự kiện (f, i) nào đó ta

cần kiểm tra  trước f ∈ ĐÚNG hay f ∈ SAI ?

III.4. Xử  lý tri thức bất định bằng phương pháp suy diễn logic

 III.4.1. Các cơ chế lập luận với tri thức bất định và không chính xác

- Khái niệm: Trên thực tế, có nhiều mệnh đề được phát biểu không chính xác, mang tính

bất định, chúng không hẳn hoàn toàn đúng cũng không hoàn toàn sai. Tính đúng - sai của

chúng không được xác định và thể hiện rõ ràng như trong lôgic mệnh đề cổ điển. Ta có

thể mở rộng tính đúng sai của mệnh đề p thông qua một hàm μ đo độ đúng của nó: μ (p)

∈ [0; 1].

 

- Mô hình xác suất: Xét P là tập các mệnh đề đóng kín đối với các phép toán  ¬, ∧ (do đó

cả ∨) và ánh xạ (độ đo xác suất) Pr: P → [0, 1] thỏa các tính chất:

  . Pr(p) + Pr(¬p) = 1, ∀ p ∈ P

  . Pr(p ∧ q) = Pr(p).Pr(q), ∀ p, q ∈ P

  . Từ đó: Pr(p ∨ q) = Pr(p) + Pr(q) – Pr(p ∧ q) (Chứng minh ? Bài tập)

Mô hình khái luật -  :

 Biểu diễn mệnh đề:

   . Nếu p thì q bởi luật bình thường:  p → q

   . Nếu p thì thông thường (nói chung, hầu như, …) q bởi khái luật: p→q !

  III.4.2. Phân bố khả xuất của khái luật và các phép toán nối kết 

* Phân bố khả xuất (PBKX) của khái luật:

p p  (hay ¬p): π(p), π( - PBKX của một cặp sự kiện  đối ngẫu nhau p và  ) thỏa: max

(π(p), π( p )) = 1

- PBKX của một sự  kiện p trong khái luật là bộ:  

¬π

π

p)(

)p(

     Nếu ta không thể khẳng định được p là tuyệt đối đúng thì: 

p π(p) = 1,  nhưng  π( ) = λ  (λ ∈ [0, 1))

- PBKX của một khái luật  p  → q !  được biểu diễn bởi ma trận: 

 

)

)⎟

¬→¬π¬→π

→¬π→π

q p(      (p

qp(      

)q

)qp(

   Chöông III - Bieåu dieãn vaø xöû lyù tri thöùc  79

Bài tập

1.

+

Bằng các phép biến đổi tương đương trong lôgic mệnh đề, hãy chứng minh: 

a.  Ta luôn có thể đưa một luật suy diễn ở dạng chuẩn Horn:

p1∧ .... ∧pn     →       q1 ∨ .... ∨ qm      

về dạng chuẩn sơ cấp:

  p1∧ .... ∧pn     →       q  

b.  Nguyên lý chứng minh phản chứng: 

(a → b đúng) ≡  (a ∧ ¬ b sai hay mâu thuẫn)

c.  Nguyên lý hợp giải.

d.  Nguyên lý modus ponens và tollens.

e.  Trong việc chứng minh tự  động tính hằng  đúng của các biểu thức lôgic

mệnh đề, tại sao người ta thường xét bài toán dạng BTA ?

f.  Quy tắc Chuyển và Tách trong thủ tục Vương Hạo.

g.  Phương pháp Qui diễn (Abduction, tương tự): Cho trước: A → B.

  . A ~  A’ ⇒ A’ → B 

  . B ~  B’ ⇒ A → B’ 

  . A ~  A’ & B ~ B’ ⇒ A’ → B’ 

2.

+

Hãy đưa về dạng chuẩn hội và chuẩn tuyển  lần lượt cho vế trái (VT) và vế

phải (VP) của các biểu thức lôgic mệnh đề sau:

a.  [(a ∧ b → c ∨ d) ∧ (c → e) ∧ a] ⇒ [b → e] 

b.  [(a ∧ b → c ∧ d) ∧ (c → e) ∧ a] ⇒ [b ∧ c → e]

c.  [(a ∨ b → c ∧ d) ∧ (c → e) ∧ a] ⇒ [d ∧ c  ∨ b]

d.  [(a ∨ b → c ∨ d) ∧ (c ∧ b → e) ∧ a] ⇒ [b ∧ c → e]

e.  [b ∧ c ∧ ¬e] ⇒ [(a ∧ b ∧ (¬c ∨ ¬d)) ∨ (c ∧ ¬e) ∨ ¬a]

f.  [a→(b→c) ] ⇔ [(a→b)→c] 

g.  {[(a ∧ b → c ∨ d) ∧ (c → e) ∧ a] ∨ [(a ∧ b → e) ∧ a]} ⇒ [b → e]

h.  {[(a ∧ b → c ∧ d) ∧ (c → e) ∧ a] ∨ [(a ∧ b → e ∨ d) ∧ a]} ⇒ [b ∧ c → e]

3.  *Nếu trong bài toán BTB  thay tập kết luận KL = ∧1≤i≤k qi  bởi KL = V1≤i≤k qi thì

cần hiệu chỉnh thuật toán suy diễn tiến SDT ra sao ?

4.

+

Kiểm tra tính hằng đúng của các biểu thức lôgic trong bài tập 2 chương III 

(có thể áp dụng vài heuristics để rút gọn hay chuyển đổi tương đương cho việc

giải trở nên đơn giản hơn) bằng các phương pháp:

i)  biến đổi lôgic 

ii)  phương pháp suy diễn tiến

iii) phương pháp suy diễn lùi

iv) thuật toán Vương Hạo (Wong Havard)

v)  thuật toán  Robinson. Nếu  thay thuật toán Robinson bằng “thuật giải

Robinson“ thì các kết quả có luôn trùng nhau không ? Tại sao?   80

Chương IV

LẬP TRÌNH LÔGIC

IV.1. GIỚI THIỆU NGÔN NGỮ LẬP TRÌNH LÔGIC PROLOG

       IV.1.1. Mở đầu 

 Một trong những mục tiêu lớn và quan trọng của trí tuệ nhân tạo (TTNT) là

mô phỏng trên máy tính những tư duy và hành vi của con người. Trước những năm

1960, khi chưa có nhóm các ngôn ngữ lập trình (NNLT) chuyên phục vụ cho lĩnh

vực TTNT như các NNLT xử lý ký hiệu (LISP- LISt Processing) và lập trình lôgic

(PROLOG – PROgramming in LOGic), những thành tựu lý thuyết trong TTNT

chưa được ứng dụng rộng rãi trong thực tế. Chương này nhằm giới thiệu Prolog,

một trong những NNLT thuộc họ này.

 NNLT Prolog do Alain Colmerauer và nhóm cộng sự thiết kế ra vào năm

1972 tại đại học Marseille ở Pháp. Prolog được xây dựng trên cở sở lý thuyết của

lôgic vị từ, nhằm biểu diễn, mô tả các tri thức cùng các mối quan hệ cơ bản giữa

chúng. Từ đó, khi đã được trang bị sẵn một cơ chế suy luận thông qua một môtơ

suy diễn, Prolog sẽ sản sinh ra và kiểm tra nhiều mối quan hệ hệ quả khác. 

Khác với nhiều NNLT thủ tục cổ  điển truyền thống, Prolog thuộc nhóm

NNLT mô tả. Để giải quyết một bài toán, đặc biệt là các bài toán xử lý ký hiệu

trừu tượng và suy luận lôgic, ta chỉ cần chọn cách biểu diễn phù hợp và mô tả các

đối tượng cùng các tính chất và mối quan hệ cơ bản đã biết giữa chúng. Thông

qua môtơ suy diễn của Prolog, ta sẽ nhận được các câu trả lời liên quan đến các

mối quan hệ nhân quả và các kết luận liên quan đến bài toán cần giải quyết. Khi

tri thức đã biết về bài toán  không tăng thêm, việc  trả lời các kết luận khác liên

quan đến bài toán cần giải là hiển nhiên thông qua các câu hỏi thêm mà không

cần phải lập trình lại như các NNLT kiểu thủ tục. Chính vì vậy, NNLT Prolog

thuộc kiểu hội thoại và một chương trình nguồn trong Prolog thường gọn hơn rất

nhiều so với một chương trình nguồn của các NNLT thủ tục truyền thống khác

như Pascal, C, … nhằm cùng giải quyết một bài toán chuyên biệt trong lĩnh vực

TTNT.

* Ví dụ 1: Để mô tả các sự kiện (fact): “Socrate và Tèo là người”, qui tắc

lôgic (hoặc luật, rule): “hễ bất cứ ai là người thì người đó sẽ phải chết”, ta dùng

hai vị từ chính: LàNgười(Ai), Chết(Ai) như sau:

 LàNgười(“Socrate”). 

 LàNgười(“Tèo”).

Chết(Ai) :- LàNgười(Ai). //hoặc: Chết(Ai) if LàNgười(Ai).

 Dựa trên các tri thức vừa mô tả, ta có thể yêu cầu Prolog trả lời các câu hỏi

liên quan sau:

¾  Goal: LàNgười(“Socrate”) → yes

¾  Goal: LàNgười(“mèo”) → no   81

¾  Goal: Chết(“Socrate”) → yes

¾  Goal: Chết(“mèo”) → no

hoặc:

¾  Goal: LàNgười(Ai) → Ai = “Socrate”, Ai = “Tèo” (2 solutions)

¾  Goal: Chết(Ai) → Ai = “Socrate”, Ai = “Tèo” (2 solutions)

 Các khái niệm chính trong Prolog là: vị từ (predicate), đối tượng, biến, sự

kiện (fact), qui tắc (rule), câu hỏi (đích, mục tiêu: Goal), danh sách. Vài kỹ thuật

thường được dùng trong Prolog là: quay lui (mặc định là tìm kiếm theo chiều sâu

trên cây suy diễn), đệ qui, lát cắt.

    IV.1.2. Vị từ, sự kiện, qui tắc, mục tiêu trong Prolog

 a. Vị từ, đối tượng, hằng, biến

 * Quan hệ giữa các đối tượng trong một bài toán, một mệnh đề được biểu

diễn bằng các vị từ và các đối của vị từ biểu diễn các đối tượng thuộc quan hệ hay

có tính chất nào đó.

  Để chuyển một mệnh đề thông thường sang dạng vị từ (predicate) trong

Prolog, người ta thường chọn động từ chính trong phần vị ngữ làm tên vị từ và các

thành phần khác làm các đối tương ứng của vị từ.

 - Phân loại các đối tượng trong Prolog: hằng, biến hay đối tượng phức hợp

(là một loại đối tượng của một vị từ mà bản thân nó lại là một vị từ khác). 

 - Các kiểu dữ liệu chuẩn (cơ sở) có sẵn trong Prolog: int, real, char, string,

symbol. Ngoài ra, Prolog còn cho phép các kiểu dữ liệu riêng do người dùng định

nghĩa. 

- Kiểu (dữ liệu) của đối tượng có thể thuộc kiểu chuẩn hoặc do người dùng

định nghĩa.

 - Ta qui ước Name là một dãy ký tự (gồm: chữ cái la tinh, chữ số hoặc dấu

gạch dưới “_”) mà ký tự đầu tiên phải là chữ cái. (Do đó, trong Name không được

có khoảng trắng “ “ và không được bắt đầu là chữ số !). 

Khi dùng  Name  để biểu diễn tên của các  đối tượng hay vị từ, người ta

thường dùng một trong hai cách viết sau  để  dễ  đọc và tránh hiểu nhầm nghĩa:

ghế_dài, ghế_đẩu hay ghếDài, ghếĐẩu; suy_luận hay SuyLuận,

so_1_Han_Thuyen hay so_1_HanThuyen, … (cho ví dụ vài dãy ký tự không phải

là một Name ?)

 - Tên của đối tượng là một Name. 

- Hằng là đối tượng mà giá trị của chúng được gán ngay khi khai báo trong

phần constants và không đổi trong suốt chương trình. Tên của hằng là một Name

mà  ký tự  đầu tiên phải là  chữ cái thường (đặc biệt là khi sử dụng giá trị của

chúng). 

- Biến là đối tượng mà giá trị của chúng có thể thay đổi trong chương trình.

Tên của biến (trừ biến vô danh, được ký hiệu là dấu gạch dưới “_”) là một Name

mà ký tự đầu tiên là chữ cái hoa. Biến được phân thành 3 loại:    82

. biến ràng buộc là biến mà tại thời điểm đang xét nó được gán với một giá

trị xác định;

. biến tự do là biến mà tại thời điểm đang xét nó chưa hoặc không được

gán với một giá trị xác định nào cả; do đó, tại những thời điểm khác nhau một

biến có thể chuyển từ tự do sang ràng buộc và ngược lại trong quá trình quay lui

khi tìm kiếm lời giải;

. biến vô danh (được ký hiệu là dấu gạch dưới “_”) là biến mà ta  không

quan tâm (do đó, không cần lưu lại hay ghi nhớ) đến giá trị của nó.

  Chú ý: Các biến trong Prolog khi được sử dụng không cần phải khai báo

trước (vì thông thường, chúng đã được khai báo ngầm trong phần khai báo kiểu

cho các vị từ)

     

b. Sự kiện, qui tắc:

 Các vị từ trong Prolog thường được thể hiện dưới hai dạng: sự kiện hay qui

tắc.

 - Sự kiện (fact) là các mệnh đề hằng đúng và nó thường được thể hiện bằng

các vị từ mà các đối của chúng đều là đối tượng hằng. Chẳng hạn, trong ví dụ 1,

LàNgười(“Socrate”) là một sự kiện.

 - Qui tắc (rule) là một mệnh đề kéo theo đúng và chúng thường được thể

hiện bởi các vị từ mà các đối của chúng chứa các biến. Qui tắc thường gồm hai

phần: PhầnĐầu và PhầnThânQuiTắc, chúng được nối với nhau bởi từ khoá “if”

hoặc “:-“, theo cú pháp sau:

PhầnĐầu if PhầnThânQuiTắc.

hoặc:

PhầnĐầu :- PhầnThânQuiTắc.

(PhầnThânQuiTắc luôn được kết thúc bởi một dấu chấm “.”) Chẳng hạn, trong ví

dụ 1, Chết(Ai) là một qui tắc.

 .  Để tạo nên các qui tắc phong phú, ta có thể dùng thêm trong

PhầnThânQuiTắc các phép toán lôgic để nối kết các mệnh đề: and (hay dấu phẩy

“,” ), or (hay dấu chấm phẩy “;”), not(vị từ).

 . Các vị từ (sự kiện, qui tắc) phải được khai báo kiểu trong phần Predicates

trước khi được liệt kê, định nghĩa và được sử dụng trong phần Clauses và Goal. 

  c. Mục tiêu, câu hỏi:

 Sau khi lập trình, các yêu cầu của người dùng có thể được biết thông qua

các câu hỏi, vị từ đưa vào trong phần mục tiêu Goal. Có hai loại Goal: 

 -  Goal trong (đưa nội dung cần hỏi vào phần Goal trong chương trình

nguồn): chỉ tìm ra lời giải đầu tiên và không tự động xuất chúng ra màn hình. Nếu

muốn xuất chúng, có thể sử dụng vị từ chuẩn xuất dữ liệu: write; nếu muốn  tìm

các lời giải kế tiếp, có thể dùng thêm vị từ chuẩn luôn sai: fail.

- Goal ngoài: tìm và tự động xuất mọi lời giải (ra cửa sổ hội thoại Dialog,

với từ khóa Goal: ở đầu mỗi câu hỏi). Nếu chỉ muốn xuất ra lời giải đầu tiên, có

thể sử dụng vị từ chuẩn lát cắt “!”.    83

   IV.1.3. Cấu trúc chính của một chương trình trong Prolog

 Một chương trình của Prolog thường bao gồm một số các phần chính và

theo thứ tự sau: 

  Constants   % Đoạn khai báo các đối tượng hằng

  Domains   % Đoạn khai báo kiểu các đối tượng riêng của người dùng 

Database   % Đoạn khai báo kiểu cho các vị từ của cơ sở dữ liệu 

Predicates   % Đoạn khai báo kiểu cho vị từ sẽ dùng trong các đoạn sau

Clauses         % Đoạn liệt kê sự kiện và định nghĩa qui tắc đã khai báo

                           trong phần Predicates 

  Goal  % Đoạn đưa vào các vị từ câu hỏi, có thể đặt trước phần 

    Clauses 

  Chú ý: Một chương trình có thể thiếu một số (hoặc tất cả !) phần trên.

  a. Đoạn khai báo các đối tượng hằng: được bắt đầu bởi từ khoá Constants

và theo cú pháp sau:

  TênĐốiTượngHằng = TrịĐốiTượngHằng

   [Name]      

 TrịĐốiTượngHằng có kiểu dữ liệu chuẩn (int, real, char, string, symbol). 

b. Đoạn khai báo kiểu dữ liệu riêng của người dùng: được bắt đầu bởi từ

khoá Domains và theo một trong các cú pháp sau:

  . Dãy TênKiểuDữLiệu = KiểuDữLiệu

   [Name, …] 

trong đó: KiểuDữLiệu  là kiểu dữ liệu chuẩn (int, real, char, string, symbol) hoặc

kiểu dữ liệu riêng của người dùng; 

    . Dãy TênKiểuDanhSách = KiểuPhầnTửChung*

   [Name , …]        [KiểuDữLiệu]

  . TênKiểuĐốiTượngPhứcHợp = Dãy TênVịTừ(KiểuĐối, …)

   [Name]          [Name(KiểuDữLiệu, …); …]

  . file = Dãy TênFileHìnhThức

   [Name; …]

Các tên file của các thiết bị chuẩn sau không cần phải khai báo:  screen,

keyboard, printer.

 c. Đoạn khai báo kiểu cho vị từ sẽ dùng trong các đoạn Goal và Clauses ở

phần tiếp theo được bắt đầu bởi từ khoá Predicates và theo cú pháp sau:

  . TênVịTừ(DãyKiểuĐối) 

    [Name]([KiểuDữLiệu, …])

Đoạn khai báo kiểu cho các vị từ của cơ sở dữ liệu (CSDL) được bắt đầu

bởi từ khoá Database và cũng theo cú pháp như phần Predicates. Điểm khác biệt

của các vị từ khai báo trong  đoạn Database là: các vị từ tương  ứng với chúng   84

trong phần Clauses có các đối là các đối tượng hằng hoặc các biến ràng buộc để

có thể đưa vào CSDL ở bộ nhớ trong theo các vị từ chuẩn riêng biệt.

  Chú ý: Có thể khai báo nhiều vị từ cùng tên nhưng: cùng số đối với kiểu

khác nhau hoặc có  số đối khác nhau hoặc thậm chí không có đối nào ! Khi đó,

chúng phải được khai báo liên tiếp nhau.

d. Đoạn liệt kê các sự kiện và định nghĩa các qui tắc đã khai báo trong

phần Predicates hoặc Database, được bắt đầu bởi từ khoá Clauses theo cú pháp

sau:

 . Liệt kê các sự kiện: 

  TênVịTừ(Dãy TrịHằng).

 . Định nghĩa các qui tắc:

  TênVịTừ(Dãy ĐốiTượng) :- PhầnThânQuiTắc(Dãy ĐốiTượng).

hoặc:

  TênVịTừ(Dãy ĐốiTượng) if  PhầnThânQuiTắc(Dãy ĐốiTượng).

trong đó: PhầnThânQuiTắc có thể gồm các vị từ, mệnh đề được nối kết với nhau

bởi các phép toán lôgic như: and (“,”), or (“;”), not(VịTừ(Đối)), nhưng Đối của

VịTừ trong not phải là hằng hoặc biến ràng buộc. Tất cả các vị từ đều phải được

kết thúc bởi một dấu chấm “.”.

 * Chú ý: 

- Các vị từ cùng tên phải được liệt kê hay định nghĩa liên tiếp nhau.

- Cơ chế tìm kiếm mặc định trong Prolog theo chiều sâu. Thứ tự của các

mệnh đề cùng tên (cùng các đối của chúng) trong phần Clauses có thể có ý nghĩa

khác nhau và ảnh hưởng đến tốc độ tìm kiếm.

- Cần  để ý  đến  thứ tự và  độ  ưu tiên các phép toán lôgic trong

PhầnThânQuiTắc.

- Qui tắc:

 A:- B1; B2.

tương đương với:

 A:- B1.

 A:- B2.

 - Để biểu diễn mệnh đề: B and (C or D), dùng qui tắc sau là sai:

  A:- B, C; D. % hoặc: A:- B, (C;D). cũng sai !

Khi đó, có nhiều cách đúng để biểu diễn chúng:

  C1: A:- B, E.

  E:- C; D.

hoặc C2: A:- B, C.

  A:- B, D.

hoặc C3: A:- B, C; B, D.

 e. Đoạn mục tiêu (đối với Goal trong), để đưa vào các vị từ câu hỏi, kết

hợp với các phép toán lôgic, được bắt đầu bởi từ khóa Goal.   85

 Nếu nhìn chương trình theo quan niệm Top-Down,  đoạn này có thể  đặt

trước phần Clauses. 

 * Ví dụ 2  (minh họa việc dùng Goal trong, Goal ngoài; cơ chế tìm kiếm

nghiệm bội của Goal ngoài): 

  Domains

  Ai = symbol

 Predicates

  Thích(Ai, Ai)

Clauses

  Thích(“A”, b).

  Thích(b, c).

  Thích(X, Z):- Thích(X, Y), Thích(Y, Z).

Goal %trong: đưa vào trong chương trình nguồn

 %Các đối trong vị từ đều ràng buộc:

  Thích(“A”, b). %Kết quả: rỗng

Thích(“A”, c), write(“Đúng”). %Kết quả: Đúng

%Có đối trong vị từ là biến tự do:

Thích(“A”, Ai). %Kết quả: rỗng

Thích(“A”, Ai), write(“A thích: “, Ai, “; “). 

%Kết quả (xuất một lời giải): A thích:  b

Thích(Ai, c), write(Ai, “ thích c; “), fail. 

% Kết quả (xuất nhiều lời giải): b thích c; A thích c

Goal %ngoài: chỉ đưa vào cửa sổ hội thoại

 %Các đối trong vị từ đều ràng buộc:

  > Thích(“A”, b) → yes

%Có đối trong vị từ là biến tự do:

> Thích(“A”, Ai) → Ai=b, Ai=c (2 solutions). 

> Thích(“A”, Ai), !. → Ai=b (1 solutions). 

> Thích(Ai_1, Ai_2) → kết quả là gì ? (Bài tập)

 * Ví dụ 3(minh họa: các phép toán lôgic trong PhầnThânQuiTắc, các qui

tắc cùng tên):

  Domains

  Ai = symbol

 Predicates

Me(Ai, Ai)

  Cha(Ai, Ai)

  Cha(Ai)

  ChaMe(Ai, Ai, Ai)

  ChaHoacMe(Ai, Ai)

  Nam(Ai)

Nu(Ai)

Nguoi(Ai)   86

Dung()

Sai

Clauses

  Cha(ba_1, con_1). 

Cha(ba_1, con_2).

    Cha(Ba):- Cha(Ba, _).

  Me(ma_1, con_1). 

  Me(ma_2, con_2).

  ChaMe(Ba, Ma, Con) :- Cha(Ba, Con), Me(Ma, Con).

  ChaHoacMe(BaMa, Con) :- Cha(BaMa, Con) ; Me(BaMa, Con).

  Nam(con_1).

  Nam(Ai):- Cha(Ai).

  Nguoi(Ai):- Cha(Ai) ; Me(Ai) ; ChaHoacMe(_, Ai). 

  Nu(Ai):- Nguoi(Ai), not(Nam(Ai)).

%  Nu(Ai):- not(Nam(Ai)). % không trả lời được câu hỏi: Nu(Ai) !?

  Dung(). %Hoac: Dung():- true.

  Sai:- not(Dung()).

Goal %ngoài: Cho kết quả là gì với từng goal sau ?

  > Nu(con_2) → ? 

> Nu(Ai) → ?

 * Ví dụ 4(minh họa đối tượng phức hợp; cơ chế tìm kiếm nghiệm bội của

Goal ngoài):

  Domains

  Ai, Ten = symbol

  Gi = Mèo(Ten) ; Bồ(Ten) ; Rỗng

 Predicates

  Thich(Ai, Gi)

Clauses

  Thich(a, Meo(miu)).

  Thich(tu,rong).

  Thich(b, Meo(miu)). Thich(b, Bo(miu)).

  Thich(c, X) :- Thich(a, X) , Thich(b, X). 

Goal %ngoài

  > Thich(c, Gi) → Cho kết quả là gì ? (Bài tập)   87

IV.2. DANH SÁCH, ĐỆ QUI, LÁT CẮT TRONG PROLOG

  Danh sách là một trong những cấu trúc dữ liệu rất quan trọng và được sử

dụng thường xuyên trong  lập trình xử lý ký hiệu bằng Prolog. Đệ qui là kỹ thuật

thường dùng trong Prolog. Do  cơ chế tìm kiếm tự  động mặc  định trong Prolog

theo chiều sâu, nên, trong nhiều tình huống, nếu muốn ngăn chặn việc tìm kiếm

tiếp tục không cần thiết, Prolog cho phép dùng lát cắt để thực hiện việc này.

     IV.2.1. Danh sách 

  a. Định nghĩa: Danh sách là một dãy có thứ tự các phần tử. Một cách đệ

qui, ta có thể định nghĩa danh sách là:

 - rỗng (không có phần tử nào), hoặc:

 - gồm một phần tử đầu và danh sách đuôi

  b. Khai báo, biểu diễn danh sách: 

 TênKiểuDanhSách = KiểuChungCácPhầnTử*

trong đó: KiểuChungCácPhầnTử có thể là kiểu dữ liệu chuẩn, kiểu do người dùng

định nghĩa, kiểu phức hợp, hoặc thậm chí là danh sách.

- Danh sách rỗng: []

- Biểu diễn danh sách theo kiểu đệ qui:

 BiếnKiểuDS = [PhầnTửĐầu | DanhSáchĐuôi]

- Biểu diễn danh sách theo kiểu liệt kê: 

 [PhầnTửThứNhất , PhầnTửThứHai | DSáchCònLại]

Về mặt lý thuyết, có thể biểu diễn danh sách bằng vị từ hoặc cây, biểu đồ

ngang.

* Ví dụ 1: Tìm phần tử đầu và danh sách đuôi của các danh sách sau: 

[a, b, c], [a], [[1, 2], [3], [4,5]] , [] ? (Bài tập)

     IV.2.2. Đệ qui - Cơ chế quay lui và tìm kiếm nghiệm bội trong Prolog

  a. Cơ chế quay lui và tìm kiếm nghiệm bội: Minh họa qua ví dụ:

* Ví dụ 2:

%trace QHệ_1_2

 QHệ_1(a, b).

QHệ_1(a, c).

QHệ_2(d, b).

QHệ_2(e, c).

QHệ_2(d, c).   88

QHệ_2(e, b).

QHệ_1_2(B, M, C) :- QHệ_1(B, C), QHệ_2(M, C).

Goal % ngoài

> QHệ_1_2(a, e, Ai) → Ai=b, Ai = c

  b. Định nghĩa: Một thao tác đệ qui F(x) trên tập D gồm 2 phần :

 . Điều kiện dừng: Một thao tác sơ cấp (đã biết cách thực hiện trực tiếp) trên

tập con Xo ⊂ D

 . Một lời gọi đệ qui F(H(x)), với H(x)∈D, sao cho sau một số hữu hạn lần

gọi đệ qui sẽ phải dẫn đến điều kiện dừng (H(H(…H()…)) ∈ Xo, ∀x∈D\Xo).

 

  * Ví dụ 3(thao tác đệ qui thiếu điều kiện dừng): 

 Ba(B, C):- Con(C, B).

 Con(C, B):- Ba(B, C).

  Goal: > Ba(a,b) → ?

 c. Kỹ thuật khử đuôi đệ qui bằng cách dùng biến phụ: 

* Ví dụ 4: Tính chiều dài của một danh sách. Do một vị từ có thể có nhiều

tác dụng, nên chú thích thêm dòng vào-ra (I, o) để chỉ các cách thức sử dụng vị từ

cho đúng.

  Domains

  ptu = real

  ds = ptu*

  i = integer

 Predicates

  ChieuDai(ds,i) % (i, i) – (i, o) – (o, i), ! – (o, o), readchar(_)

  ChieuDaiPhu(ds, i, i)  % dòng vào-ra: (i, i, i) – (i, i, o)

% ChieuDaiPhu(i,0,_): luôn khởi tạo biến thứ 2 là 0 

Clauses

  ChieuDai([], 0).

  ChieuDai([_|Duoi], Kq) :- ChieuDai(Duoi,KqPhu), Kq = KqPhu + 1.

  ChieuDaiPhu([], Kq, Kq).

  ChieuDaiPhu([_|Duoi], Phu, Kq) :- PhuMoi=Phu+1, 

ChieuDaiPhu(Duoi, PhuMoi, Kq).

Goal %ngoài

  > ChieuDai([1,2,3], 3) → ? > ChieuDai([1,2,3], Kq) → ?

> ChieuDaiPhu([1,2,3], 0, 3) → ?

  > ChieuDaiPhu([1,2,3], 0, Kq) → ?

  > ChieuDaiPhu(Nao, 0, 1) → ?   89

 

  Bài tập: Viết 2 qui tắc đệ qui (có và không có đệ qui đuôi) tính giai thừa

của một số tự nhiên.

  Chú ý: Thứ tự các vị từ cùng tên trong phần Clauses có thể có ý nghĩa khác

nhau, đặc biệt là các vị từ đệ qui. Chẳng hạn, xem tác dụng của 2 cách viết qui tắc

kiểm tra danh sách có 1 phần tử sau (và sửa lại cho đúng: bài tập):

Cách 1:

 DS1PTu([_|D]) :-  DS1PTu(D).

 DS1PTu([]).

Cách 2:

DS1PTu([]).

DS1PTu([_|D]) :-  DS1PTu(D).

với cách hỏi:  DS1PTu(Nào) ?

 d. Vài thao tác đệ qui cơ bản trên danh sách: 

* Ví dụ 5: Lập qui tắc kiểm tra  quan hệ thuộc giữa một phần tử và một

danh sách: Thuoc(Ptu, DSach) % (i, i) – (o, i) – (i, o) ?

Thuoc(PT, [PT|_]).

Thuoc(PT, [_|DSDuoi]) :- Thuộc(PT, DSDuoi).

* Ví dụ 6: Lập qui tắc nối hai danh sách: Noi(DSach, DSach, DSach) % (i,

i, i) – (i, i, o) – (o, o, i) – (i, o, i) – (o, i, i) ?

Noi([], L, L).

Noi([PT|L1], L2, [PT|L3]) :- Noi(L1, L2, L3).

ThuocMoi(PT, DS) :- Noi(_, [PT|_], DS).

     IV.2.3. Lát cắt (!)

  Lý do dùng lát cắt: Do cơ chế tìm kiếm tự động mặc định trong Prolog theo

chiều sâu, nhưng trong nhiều bài toán với các đặc trưng riêng, ta không cần cơ chế

quay lui hoặc tìm tiếp lời giải kế tiếp. Khi đó, ta có thể dùng lát cắt để đạt được

mục đích đó. 

 a. Cú pháp, tác dụng của lát cắt: Dùng dấu chấm than “!” để biểu diễn lát

cắt.  Lát cắt là vị từ luôn đúng và có tác dụng ngăn: cơ chế quay lui và tiếp tục tìm

kiếm lời giải đối với các vị từ cùng tên với những vị từ đứng trước lát cắt trong

một mệnh đề nào đó. Điều đó dẫn đến việc tiết kiệm thời gian lẫn bộ nhớ khi dùng

lát cắt thích hợp.

    90

Ta minh họa lát cắt qua vài trường hợp sau:

 - Đối với qui tắc không đệ qui:

   VịTừ_1 :- a, !, b.     % (1)

    VịTừ_1 :- c.      % (2)

 Khi a sai thì vị từ c được thực hiện. Khi a đúng, sẽ gặp “!” và thực hiện

tiếp b (kể cả nghiệm bội của b, nếu b đúng), nhưng sẽ không bao giờ tìm tiếp lời

giải cho a và (kể cả trường hợp b sai) không quay xuống VịTừ_1 thứ hai trong (2)

để kiểm tra c , với các trị trong đối của VịTừ_1 tương ứng với lần gọi đó.

 - Đối với qui tắc đệ qui:

   VịTừ_2.       

    VịTừ_2 :- a, !, VịTừ_2.     % (3)

    VịTừ_2 :- b.      % (4)

 Khi a sai, (4) sẽ  được kiểm tra. Khi  a  đúng, VịTừ_2 trong (4) sẽ không

được kiểm tra ứng với lần gọi đó, mặc dù VịTừ_2 trong thân qui tắc của (3) là sai

và sẽ không bao giờ tìm tiếp lời giải cho a .    

 b. Các tình huống có thể sử dụng lát cắt:

  α- Chỉ tìm lời giải đầu tiên ở Goal ngoài. 

β- Trong các bài toán chỉ có duy nhất một lời giải, sau khi tìm được lời giải

đầu tiên, ta dùng lát cắt để dừng ngay quá trình tìm kiếm.

  δ- Ta muốn dừng khi một mệnh đề sai (nhưng không cần tìm cách khác để

thỏa mãn mệnh đề), hoặc thay cho vị từ not, bằng cách sử dụng tổ hợp lát cắt và vị

từ fail phù hợp.

  γ- Dùng lát cắt trong các chương trình “Sinh và Thử”.

 

 * Ví dụ 7 (minh họa vài tình huống dùng lát cắt):

  Domains

  i, ptu = integer

 Predicates

  RangBuoc(ptu)  % minh họa tình huống δ

  GiaiThua(i, i) % minh họa tình huống β

  ThuongNguyen(i, i, i) % hai đối đầu ∈N, minh họa tình huống γ

  Sinh_1_So(i)

Clauses

       % RangBuoc(X) :- not(free(X)). % sai khi X là biến không ràng buộc

  RangBuoc(X) :- free(X), !, fail.

  RangBuoc(_).

  GiaiThua(0, 1) :- !.

  GiaiThua(N, Kq) :- N1 = N-1, GiaiThua(N1, Kq1), Kq = Kq1*N.

  ThuongNguyen(B, C, T) :- Sinh_1_So(T), T*C<=B, (T+1)*C>B, !.          91

  Sinh_1_So(0).

  Sinh_1_So(Sau) :- Sinh_1_So(Truoc), Sau = Truoc + 1.

.

 * Ví dụ 8 (Bài tập: tìm các lỗi sai, hạn chế và mở rộng các trường hợp có

dòng vào-ra tốt hơn): Tính số cha và mẹ của mỗi người.

  Domains

  Ai = symbol

  i = integer

 Predicates

  SoChaMe(Ai, i)

Clauses

  SoChaMe(“Adam”, 0).

  SoChaMe(“Eva”, 0).

  SoChaMe(_,2).

  % Gợi ý: SoChaMe(“Adam”, 0): !. hoặc: SoChaMe(“Adam”, BN): !, BN=0., … 

  * Chú ý: 

- Phép toán X = Y có thể hiểu theo hai nghĩa:

  . Phép so sánh nếu cả X và Y là biến (hay biểu thức) ràng buộc;

  . Phép gán nếu chỉ một trong hai vế là biến tự do, còn vế kia là biểu

thức hay biến ràng buộc (và sẽ vô nghĩa, bị lỗi khi cả hai vế đều không ràng buộc!)

 - Có thể thay phép toán = bởi vị từ Bằng(X,X).

 * Gợi ý tạo các lệnh cấu trúc như các NNLT cổ điển:

 

- if (ĐK) then A;

    else B;

    Neu(DK) :- !, A.

  Neu( _ ) :- B.

 - switch (BT)

    { case a1: A1; break;

       case a2: A2; break;

  …

       case an: An; break;

       default: B; break;

    }; 

  %Gọi sử dụng: Switch(BT)

  Switch(a1) :- !, A1.

   Switch(a2) :- !, A2.

   … 

   Switch(an) :- !, An.

  Switch( _ ) :- B.   92

 - repeat A until (DK);

  Repeat :- Lap, A, DK, !.

  Lap.

Lap :- Lap.

 - while (ĐK) do A;

  While :- ĐK, !, A, While. 

  While.

 - for (i=Bdau; i<=Kthuc; i = i+Step) do A(i);

  % Gọi sử dụng: For(Bdau, Kthuc, Step)

  For(I, N, Step) :- Dấu(Step, D), D*(I-N) > 0, !.

   For(I, N, Step) :- A(i), I_Sau = I+Step, For(I_Sau, N, Step).

IV.3. CÁC VÍ DỤ

       IV.3.1. Bài toán “Tháp Hà Nội” (Minh họa vị từ đệ qui)

  Ví dụ 1: Có 3 cọc A, B, C. Trên cọc A có n đĩa tròn chồng lên nhau, với

đường kính khác nhau, sao cho đĩa bé luôn nằm trên đĩa lớn; hai cọc B, C trống.

Tìm cách chuyển n đĩa từ A đến C với số lần di chuyển đĩa ít nhất (2n

-1), sao cho:

mỗi lần chỉ di chuyển 1 đĩa bé nhất trên cùng của một cọc tùy ý sang một cọc khác

để đĩa bé luôn nằm trên đĩa lớn. 

%Program Thap_HaNoi

Domains

 i  = integer

 s = symbol

Predicates

 HaNoi(i, i)

 ChuyenDia(i, i, s, s, s, i) 

Clauses

 HaNoi(N, SoLanDiChuyen) :- ChuyenDia(N, N, a, c, b, SoLanDiChuyen).

 ChuyenDia(1, Nhan, Tu, Den, _, 1) :- !, nl, 

write(“Chuyen dia: “, Nhan, “ tu “, Tu, “ den “, Den).

  ChuyenDia(N, Nhan, Tu, Den, Tgian, Tong):- 

N1=N-1, NhanPhu=Nhan-1,

  ChuyenDia(N1, NhanPhu, Tu, TGian, Den, Tong1),

  ChuyenDia(1, Nhan, Tu, Den, Tgian, 1),

  ChuyenDia(N1, NhanPhu, TGian, Den, Tu, Tong2),   93

Tong = Tong1 + Tong2 + 1.

Goal %trong

 makewindow(1, 3, 7, “Thap Ha Noi”, 0, 0, 25, 80), 

write(“Nhap so dia n (1<=n<=12): “), readint(N), HaNoi(N, Tong),

write(“Tong so lan di chuyen dia: “, Tong).. 

  Kết quả của chương trình với n=2 là:

  Chuyen dia: 1 tu a den b

  Chuyen dia: 2 tu a den c

  Chuyen dia: 1 tu b den c

  Tong so lan di chuyen dia: 3

       IV.3.2. Bài toán xử lý vi phân ký hiệu (Minh họa bài toán xử lý ký hiệu):

Tính đạo hàm của một hàm số theo một biến bất kỳ.

%Program Xử lý vi phân ký hiệu

Domains

 KyHieu = symbol

 So = real

BieuThuc = bien(KyHieu); hang(So);

       cong(BieuThuc,BieuThuc); tru(BieuThuc,BieuThuc); 

   nhan(BieuThuc,BieuThuc); chia(BieuThuc,BieuThuc);

       cos(BieuThuc); sin(BieuThuc);

   tg(BieuThuc); cotg(BieuThuc); 

   ln(BieuThuc); log(BieuThuc,BieuThuc); 

   exp(BieuThuc); mu(BieuThuc,BieuThuc);

luyThua(BieuThuc,BieuThuc)      

Predicates

 d(BieuThuc,KyHieu,BieuThuc)

Clauses

 d(hang(_),_,hang(0)).

 d(bien(X),X,hang(1)):-!.

 d(bien(_),_,hang(0)).

 d(cong(U,V),X,cong(U1,V1)):- d(U,X,U1), d(V,X,V1).

 d(tru(U,V),X,tru(U1,V1)):- d(U,X,U1), d(V,X,V1).

 d(nhan(U,V),X,cong(nhan(U1,V),nhan(U,V1))):- d(U,X,U1), d(V,X,V1).

 d(chia(U,V),X,chia(tru(nhan(U1,V),nhan(U,V1)),nhan(V,V))):-

  d(U,X,U1), d(V,X,V1).

 d(sin(U),X,nhan(cos(U),U1)) :- d(U,X,U1). 

 d(cos(U),X,tru(hang(0),nhan(sin(U),U1))) :- d(U,X,U1).

 d(tg(U),X,chia(U1, mu(cos(U),hang(2)))) :- d(U,X,U1).   94

d(cotg(U),X,nhan(hang(-1),chia(U1, mu(sin(U),hang(2))))) :- d(U,X,U1).

 d(ln(U),X,chia(U1,U)):-d(U,X,U1).

 d(log(U,V),X, Kq) :- d(chia(ln(V),ln(U)),X,Kq).

 d(exp(U),X,nhan(exp(U),U1)) :- d(U,X,U1).

 d(mu(U,V),X,nhan(mu(U,V),W)):- d(nhan(V,ln(U)),X,W).  

  

         d(luyThua(U,hang(A)),X,nhan(hang(A),nhan(U1,luyThua(U,hang(A_1))))):- 

  A_1 = A-1, d(U,X,U1).

Goal %ngoài: tính vi phan của x.sin(x)

 d(nhan(bien(x),sin(bien(x))),x, Kq).

Kết quả (chưa rút gọn) của chương trình là:

Kq=cong(nhan(hang(1),sin(bien(x))),nhan(bien(x),

         nhan(cos(bien(x)),hang(1))))

       IV.3.3. Bài toán suy luận lôgic (Minh họa lập trình cho bài toán lập luận

lôgic)

  Ví dụ 2: Trên một  đảo nọ, chỉ có hai nhóm người: một nhóm chỉ gồm

những người luôn nói thật và nhóm còn lại chỉ gồm những người luôn nói láo. Một

nhà thông thái  đến  đảo này và gặp ba người A, B, C. A nói: “Trong ba người

chúng tôi chỉ có đúng một người nói thật”, C nói: “Cả ba người chúng tôi đều nói

láo”. Hỏi trong ba người A, B, C, ai nói thật, ai nói láo ?

 Ta biểu diễn mỗi phương án của bài toán bằng danh sách [A, B, C], sao cho

mỗi phần tử chỉ gồm một trong hai trạng thái 0, 1, tương ứng với trạng thái nói láo

hoặc nói thật. Ta mô tả câu nói của A và C như sau: nếu A nói thật thì Tổng =

A+B+C = 1, nếu A nói láo thì Tổng ≠ 1; nếu C nói thật thì Tổng = A+B+C = 0,

nếu C nói láo thì Tổng > 0.  

%Program Noi lao, noi that

Domains

   i, ptu = integer

   ds  = ptu*

Predicates

    TaoDS(i,ds)

    Quet(ptu)

    Giai(i,ds)

    SuKien(symbol,ds)

    Tong(ds,integer)   

Viet(ds,i) 

Clauses

 Quet(0).   95

 Quet(1).

 TaoDS(N,[]) :- N <= 0, !.

 TaoDS(N,[Dau|Duoi]) :- Quet(Dau),N1 = N - 1, TaoDS(N1,Duoi).

 Giai(N,DS) :- TaoDS(N,DS), SuKien("A",DS), SuKien("C",DS).

 SuKien("A",DS) :- DS = [1,_,_], Tong(DS,1).

 SuKien("A",DS) :- DS = [0,_,_], Tong(DS,So), So <> 1.

 

 SuKien("C",DS) :- DS = [_,_,1], Tong(DS,0).  % phi li !

 SuKien("C",DS) :- DS = [_,_,0], Tong(DS,So), So > 0.

 

 Tong([],0) :- !.

 Tong([Dau|Duoi],Kq) :- Tong(Duoi,KqDuoi), Kq = KqDuoi + Dau.

 

 Viet([],_) :- nl,!.

 Viet([1|D],I):-!,I1=I+1,I2 =I1+64,char_int(C,I2), write(C," noi that

"),

   Viet(D,I1).

 Viet([0|D],I):-!,I1=I+1,I2 =I1+64,char_int(C,I2), write(C," noi lao

"),

   Viet(D,I1).   

Goal % trong

 Giai(3,DS),Viet(DS,0),nl. 

Kết quả của chương trình là:

 A noi that

 B noi lao

 C noi lao

   96

IV.4. PHỤ LỤC:  VÀI VỊ TỪ CHUẨN CỦA PROLOG

IV.4.1. NHẬP VÀ XUẤT DỮ LIỆU

Các vị từ xuất và nhập dữ liệu xét ở đây sẽ được thao tác từ các thiết bị vào ra

hiện thời, ngầm định là màn hình và bàn phím.

1.  Các vị từ nhập dữ liệu

Các ký hiệu sẽ sử dụng trong biểu diễn cú pháp các vị từ có ý nghĩa như sau:

Sau tên vị từ các biến có các kiểu được liệt kê tiếp theo. Sau đó, dòng vào ra đối

với các biến của vị từ sẽ được chỉ ra, trong đó các biến có vị trí tương ứng với ký

tự:

. ‘o’ phải là biến tự do (sau khi vị từ được thực hiện các kết qủa của nó sẽ được  

gán vào các biến này);

. ‘i’ phải là biến ràng buộc hoặc đối tượng hằng (chúng cung cấp dữ liệu ban

đầu để các vị từ này thực hiện).

a)  readln(StringVariable), (string) – (o): trong đó StringVariable là biến tự

do có kiểu string. Vị từ này có tác dụng đọc một chuỗi từ thiết bị vào hiện

thời (và tất nhiên được gán cho biến StringVariable) cho đến khi gặp ký tự

xuống dòng (phím Enter, có mã ASCII tương ứng là ‘\13’) được ấn.

b)  readint(IntgVariable), (integer) – (o): đọc một số nguyên từ thiết bị vào

hiện thời cho đến khi gặp ký tự xuống dòng được ấn.

c)  readreal(RealVariable), (real) – (o): đọc một số thực từ thiết bị vào hiện

thời cho đến khi gặp ký tự xuống dòng được ấn.

d)  readchar(CharVariable), (char) – (o): đọc một ký tự (nhưng không xuất

hiện ký tự trên màn hình) từ thiết bị vào hiện thời và không cần kết thúc

bằng ký tự xuống dòng.

e)  file_str(DosFileName, StringVariable), (string, string) – (i, o) (i, i):

chuyển đổi nội dung văn bản giữa file có tên thực là DosFileName và biến

chuỗi có tên StringVariable (cho đến khi gặp ký tự kết thúc file eof, tương

ứng với tổ hợp phím Ctl-z hay mã ASCII là ‘\26’).

f)  inkey(CharVariable), (char) – (o):  đọc một ký tự từ bàn phím (gán cho

biến CharVariable). Vị từ này chỉ đúng khi có một ký tự được  ấn từ bàn

phím.

g)  keypressed: kiểm tra việc một phím được bấm hay chưa nhưng không đọc

ký tự này vào. Vị từ này chỉ đúng khi có một ký tự được ấn từ bàn phím.   97

h)  keypressed: kiểm tra việc một phím được bấm hay chưa nhưng không đọc

ký tự này vào. Vị từ này chỉ đúng khi có một ký tự được ấn từ bàn phím.

2.  Các vị từ xuất dữ liệu

a)  write(Variable|Constant * ): trong đó dấu * chỉ ra rằng các biến Variable

(ràng buộc) hay hằng Constant có thể được lặp lại và chúng được viết cách

nhau bằng các dấu phẩy ‘,’. Vị từ này có tác dụng xuất đến thiết bị ra một

số đối tượng.

b)  nl: xuống dòng.

c)  writef(FormatString, Variable|Constant *): xuất ra một số  đối có  định

khuôn dạng theo cú pháp sau: %-m_p$, trong đó:

. ‘-’: canh lề bên trái, nếu không có dấu ‘-’ sẽ là canh lề bên phải;

. ‘m’: xác định độ rộng vùng nhỏ nhất cho đối;

. ‘p’: xác định độ chính xác các số sau dấu chấm thập phân hay số

lớn  nhất các ký tự được in ra của chuỗi;

. ‘$’ là một trong các ký hiệu quy ước chuẩn:

 d: số thập phân dạng chuẩn.

 x: số thập lục phân.

 s: chuỗi (symbols hay strings).

 c: ký tự (chars hay integers).

 g: số thực dạng ngắn nhất ngầm định.

 e: số thực dạng mũ.

 f: số thực dạng dấu phẩy động.

d)  “

”: ký tự xuống dòng.

“\t”: ký tự lùi vào một số khoảng trắng.

nn”: ký tự đưa vào bảng mã ASCII tương ứng.

IV.4.2. CÁC THAO TÁC FILE

a)  openread(SymbolicFileName, DosFileName), (file, string) – (i, i): mở

một file trên đĩa DosFileName để đọc và gán cho tên file hình thức (trong

phần khai báo kiểu file) SymbolicFileName.

b)  openwrite(SymbolicFileName, DosFileName), (file, string) – (i, i): mở

một file trên đĩa để ghi và gán cho tên file hình thức; nếu file đó đã tồn tại

thì nó sẽ được xóa trước khi gọi.

c)  openappend(SymbolicFileName, DosFileName), (file, string) – (i, i):mở

một file trên đĩa để nối thêm dữ liệu và gán cho tên file hình thức.   98

d)  openmodify(SymbolicFileName, DosFileName), (file, string) – (i, i):mở

một file trên đĩa để sửa đổi (đọc và ghi) và gán cho tên file hình thức. Khi

dùng vị từ này, nên kết hợp với vị từ filepos để cập nhật file tại vị trí tùy

chọn.

e)  readdevice(SymbolicFileName), (file) – (i) (o): đặt lại hay hiển thị thiết bị

đọc vào file đang mở (để đọc hay sửa đổi), ngầm định là bàn phím.

f)  writedevice(SymbolicFileName), (file) – (i) (o): đặt lại hay hiển thị thiết

bị ghi vào file đang mở (để đọc ghi hoặc nối thêm hay sửa đổi), ngầm định

là màn hình.

g)  closefile(SymbolicFileName), (file) – (i):  đóng file (dù nó  đã mở hay

chưa).

h)  filepos(SymbolicFileName, FilePosition, Mode), (file, real, integer) – (i, i,

i) (i, o, i): đưa con trỏ đến hay trả lại giá trị của vị trí FilePosition trong file

tính từ chế độ Mode: 0: đầu file, 1: vị trí hiện tại trong file, 2: cuối file.

i)  eof(SymbolicFileName), (file) – (i): kiểm tra con trỏ có  ở cuối file hay

không. Vị từ này đúng nếu con trỏ ở vị trí cuối file.

j)  existfile(DosFileName), (string) – (i): vị từ này thành công nếu file có tên

chỉ ra tồn tại trong thư mục hiện thời.

k)  deletefile(DosFileName), (string) – (i): xóa file có tên chỉ ra.

l)  renamefile(OldDosFileName, NewDosFileName), (string, string) – (i, i):

đổi tên file cũ thành tên file mới.

m) disk(DosPath), (string) – (i) (o): thiết lập hay trả lại  ổ đĩa và đường tìm

kiếm thư mục mặc định.

IV.4.3. CÁC THAO TÁC ĐỐI VỚI MÀN HÌNH VÀ CỬA SỔ

1.  Màn hình

Màu nền  Giá trị  Màu chữ  Giá trị

Black  0  Black  0

Blue  16  Blue  1

Green  32  Green  2

Cyan  48  Cyan  3

Red  64  Red  4

Magenta  80  Magenta  5

Brown  96  Brown  6   99

White  112  White  7

    Grey  8

    Light Blue  9

    Light Green  10

    Light Cyan  11

    Light Red  12

    Light Magenta  13

    Yellow  14

    White (High Intensity)  15

a)  attribute(Attr), (integer) – (i) (o): cho (đặt hay trả lại) giá trị thuộc tính của

màn hình. Giá trị đặc trưng cho thuộc tính của màn hình, dựa vào bảng trên

đây, được tính như sau:

1.  Chọn một màu chữ và một màu nền;

2.  Cộng những giá trị nguyên tương ứng với màu trong bảng 1;

3.  Cộng thêm giá trị đó với 128 nếu muốn các chữ hiển thị nhấp nháy.

b)  scr_char(Row, Column, Char), (integer, integer, char) – (i, i, i) (i, i, o):

cho ký tự Char trên màn hình tại vị trí có tọa độ (Row, Column).

c)  scr_attr(Row, Column, Char), (integer, integer, char) – (i, i, i) (i, i, o):

cho thuộc tính của màn hình tại vị trí có tọa độ (Row, Column).

d)  filed_str(Row, Column, Length, String), (integer, integer, integer, string)

– (i, i, i, i) (i, i, i, o): cho chuỗi String trên màn hình tại một vùng bắt đầu ở

vị trí có tọa độ (Row, Column) và có độ dài là Length ký tự.

e)  filed_attr(Row, Column, Length, Attr), (integer, integer, integer, integer)

– (i, i, i, i) (i, i, i, o): cho thuộc tính của màn hình tại một vùng bắt đầu ở vị

trí có tọa độ (Row, Column) và có độ dài là Length ký tự.

f)  cursor(Row, Column), (integer, integer) – (i, i) (i, o): dịch con trỏ đến vị

trí có tọa độ (Row, Column) hay trả lại tọa độ của con trỏ hiện thời.

2.  Hệ thống cửa sổ

a)  makewindow(WindowNo, ScrAtt, FrameAtt, FrameStr, Row, Column,

Height, Width), (integer, integer, integer, string, integer, integer, integer,

integer) – (i, i, i, i, i, i, i, i) (o, o, o, o, o, o, o, o): xác định một vùng màn

hình làm cửa sổ.   100

b)  makewindow(WindowNo, ScrAtt, FrameAtt, FrameStr, Row, Column,

Height, Width, ClearWindow, FrameStrPos, BorderChars), (integer,

integer, integer, string, integer, integer, integer, integer, integer, integer,

string) – (i, i, i, i, i, i, i, i, i, i) (o, o, o, o, o, o, o, o, o, o): xác định một vùng

màn hình làm cửa sổ với các thuộc tính sau:

ClearWindow  = 0  không xóa cửa sổ sau khi tạo,

   = 1  xóa cửa sổ sau khi tạo;

FrameStrPos = 255    canh tít ở giữa,

   <> 255 đặt tít tại vị trí FrameStrPos;

BorderChars một chuỗi gồm 6 ký tự để vẽ khung:

   ký tự thứ 1: góc trên bên trái,

   ký tự thứ 2: góc trên bên phải,

   ký tự thứ 3: góc dưới bên trái,

   ký tự thứ 4: góc dưới bên phải,

   ký tự thứ 5: đường kẻ ngang,

   ký tự thứ 6: đường kẻ dọc.

c)  shiftwindow(WindowNo), (integer) – (i) (o): đổi cửa sổ làm việc đến cửa

sổ thứ WindowNo (vẫn giữ nguyên trạng thái của cửa sổ trước đó) hay trả

lại số của cửa sổ đang làm việc.

d)  gotowindow(WindowNo), (integer) – (i): di chuyển nhanh đến cửa sổ thứ

WindowNo mà không lưu nội dung của cửa sổ cũ vào bộ đệm cửa sổ.

e)  resizewindow: điều chỉnh lại kích thước cửa sổ như kiểu thực đơn.

f)  resizewindow(StartRow, NoOfRows, StartCol, NoOfCols), (integer,

integer, integer, integer) – (i, i, i, i): điều chỉnh lại kích thước cửa sổ một

cách trực tiếp.

g)  colorsetup(Main_Frame), (integer) – (i): thay đổi màu cửa sổ hay khung

tùy theo giá trị của:

Main_Frame  = 0   đổi màu cửa sổ

   = 1   đổi màu khung

h)  existwindow(WindowNo), (integer) – (i): vị từ này thành công nếu tồn tại

cửa sổ số WindowNo.

i)  removewindow: loại bỏ cửa sổ hiện thời.

j)  removewindow(WindowNo, Refresh), (integer, integer) – (i, i): loại bỏ

cửa sổ số WindowNo với chế độ xóa nền hay không xóa nền (nếu Refresh

bằng 1 hoặc 0).

k)  clearwindow: xóa cửa sổ hiện thời về màu nền của nó.   101

l)  window_str(ScreenString), (string) – (i) (o): chuyển đổi nội dung giữa cửa

sổ hiện thời và chuỗi ScreenString.

m) window_at(Attribute), (integer) – (i): xác định thuộc tính cho cửa sổ hiện

thời.

n)  scroll(NoOfRows, NoOfCols), (integer, integer) – (i, i): cuộn nội dung của

cửa sổ lên (hay xuống) X=NoOfRows dòng tùy theo nó > 0 (hay < 0) và

sang trái (hay phải) X=NoOfCols cột tùy theo nó > 0 (hay < 0).

o)  framewindow(FrameAttr), (integer) – (i): thay đổi thuộc tính của khung

cửa sổ.

p)  framewindow(FrameAttr, FrameStr, FrameStrPos, FrameTypeStr),

(integer, string, integer, integer) – (i, i, i, i): thay đổi thuộc tính của khung

cửa sổ theo các chế độ sau:

FrameAttr       = thuộc tính khung

FrameStr       = tít của khung

FrameStrPos       = nhận giá trị từ 0 đến độ rộng cửa sổ. Nó đặt vị trí

cho tít trên khung. Tít sẽ đặt ở giữa nếu nó bằng

255

FrameTypeStr     = một chuỗi gồm 6 ký tự để chỉ dạng của khung.

IV.4.4. CÁC VỊ TỪ CHUẨN KHÁC

1.  Thao tác chuỗi

a)  frontchar(String, FrontChar, RestString), (string, char, string) – (i, o, o)

(i, i, o) (i, o, i) (i, i, i) (o, i, i): tách một chuỗi thành ký tự đầu và chuỗi còn

lại.

b)  fronttoken(String, Token, RestString), (string, string, string) – (i, o, o) (i,

i, o) (i, o, i) (i, i, i) (o, i, i): tách một chuỗi thành một ‘từ’ và chuỗi còn lại.

c)  frontstr(Length, InpString, StartString, RestString), (integer, string,

string, string) – (i, i, o, o): tách từ một chuỗi cho trước InpString ra một

chuỗi con StartString bắt đầu từ ký tự thứ nhất có độ dài là Length và chuỗi

còn lại RestString.

d)  concat(String1, String2, String3), (string, string, string) – (i, i, o) (i, o, i)

(o, i, i) (i, i, i): nối String1 và String2 thành String3: String3 = String1 +

String2.

e)  strlen(String, Length), (string, integer) – (i, i) (i, o) (o, i): chiều dài của

String là Length. Với dòng vào ra (o, i) ta được một chuỗi gồm Length ký

tự trắng.   102

f)  isname(StringParam), (string) – (i): kiểm tra chuỗi có là một ‘name’ (một

dãy các ký tự chữ hoặc số hoặc dấu ‘_’ nhưng bắt đầu bằng chữ hay ký tự

‘_’ trong Turbo Prolog không.

2.  Chuyển đổi kiểu

a)  char_int(CharParam, IntgParam), (char, integer) – (i, o) (o, i) (i, i):

chuyển đổi giữa một ký tự và mã ASCII tương ứng với nó.

b)  str_int(StringParam, IntgParam), (string, integer) – (i, o) (o, i) (i, i):

chuyển đổi giữa một chuỗi và một số nguyên tương ứng với nó.

c)  str_char(StringParam, CharParam), (string, char) – (i, o) (o, i) (i, i):

chuyển đổi giữa một chuỗi và một ký tự tương ứng với nó.

d)  str_real(StringParam, RealParam), (string, real) – (i, o) (o, i) (i, i):

chuyển đổi giữa một chuỗi và một số thực tương ứng với nó.

e)  upper_lower(StringInUpperCase, StringInLowerCase), (string, string) –

(i, o) (o, i) (i, i): chuyển đổi giữa một chuỗi ký tự hoa và một chuỗi ký tự

thường.

f)  upper_lower(CharInUpperCase, CharInLowerCase), (char, char) – (i,

o) (o, i) (i, i): chuyển đổi giữa một ký tự hoa và một ký tự thường.

3.  Thao tác dối với cơ sở dữ liệu trong

a)  consult(DosFileName), (string) – (i): gọi ra và bổ sung một file cơ sở dữ

liệu trong (file đó được khởi tạo và lưu một cơ sở dữ liệu trong không tên

bằng vị từ save(?)).

b)  consult(DosFileName, InternalDatabaseName), (string,

InternalDatabaseName) – (i, i): gọi ra và bổ sung một file cơ sở dữ liệu

trong (file đó được khởi tạo và lưu một cơ sở dữ liệu trong không tên bằng

vị từ save(?, ?)).

c)  save(DosFileName), (string) – (i): lưu mọi mệnh đề của cơ sở dữ liệu trong

không tên.

d)  save(DosFileName, InternalDatabaseName), (string,

InternalDatabaseName) – (i, i): lưu mọi mệnh đề của cơ sở dữ liệu trong có

tên.

e)  assert(Term), (InternalDatabaseDomain) – (i) hay  asserta(Term),

(InternalDatabaseDomain) – (i): chèn một sự kiện vào đuôi một cơ sở dữ

liệu trong không tên.

f)  assertz(Term), (InternalDatabaseDomain) – (i): chèn một sự kiện vào đuôi

một cơ sở dữ liệu trong không tên.   103

g)  retract(Term), (InternalDatabaseDomain) – (_): vị từ không tất  định

(nondeterm) này loại bỏ sự kiện chỉ ra của cơ sở dữ liệu trong không tên.

h)  retract(Term, InternalDatabaseDomain), (InternalDatabaseDomain) –

(_, i): vị từ không tất định (nondeterm) này loại bỏ sự kiện chỉ ra của cơ sở

dữ liệu trong có tên.

i)  retractall(Term), (InternalDatabaseDomain) – (_): vị từ này loại bỏ mọi sự

kiện của cơ sở dữ liệu trong không tên. Nên sử dụng biến vô danh trong vị

từ này. Vị từ này không bao giờ sai.

j)  retractall(_, InternalDatabaseDomain), (_, InternalDatabaseDomain) –

(_, i): vị từ này loại bỏ mọi sự kiện của cơ sở dữ liệu trong có tên. Nên sử

dụng biến vô danh trong vị từ này.

4.  Các lệnh liên quan đến hệ điều hành DOS

a)  system(DosCommandString), (string) – (i): thực hiện một lệnh của hệ

điều hành DOS trong môi trường Turbo Prolog.

b)  dir(Path, Filespec, Filename), (string, string, string) – (i, i, o): xem các

files trong thư mục và chọn một file bằng menu để xem nội dung.

c)  exit: thoát ra một chương trình và trở về môi trường làm việc của Prolog.

5.  Các lệnh linh tinh khác

a)  random(RealVariable), (real) – (o): tạo một số ngẫu nhiên thực trong

khoảng [0, 1).

b)  random(MaxValue, RandomInt), (integer, integer) – (i, o): tạo một số

nguyên ngẫu nhiên trong khoảng [0, RandomInt).

c)  sound(Duration, Frequency), (integer, integer) – (i, i): tạo ra âm thanh có

trường độ Duration và tần số Frequency.

d)  beep: tạo ra một tiếng còi ngắn.

e)  date(Year, Month, Day), (integer, integer, integer) – (o, o, o) (i, i, i): cho

ngày, tháng, năm của đồng hồ hệ thống.

f)  time(Hours, Minutes, Seconds, Hundredths), (integer, integer, integer,

integer) – (o, o, o, o) (i, i, i, i): cho giờ của đồng hồ hệ thống.

g)  trace(on/off), (string) – (o) (i): đặt chế độ lần vết hay không cho chương

trình.

h)  findall(Variable, Atom, ListVariable): ghi các giá trị của biến trong Atom

trùng tên với Variable vào danh sách ListVariable.

i)  not(Atom): vị từ này có giá trị logic phủ  định của vị từ Atom. Lưu ý:

không được dùng biến tự do trong vị từ Atom.   104

j)  free(Variable): vị từ này chỉ đúng khi Variable là biến tự do.

k)  bound(Variable): vị từ này chỉ đúng khi Variable là biến ràng buộc.

l)  fail: vị từ này luôn luôn sai.

m) true: vị từ này luôn luôn đúng.

n)  Các phép toán số học trong Turbo Prolog: +, -, *, /, mod, div.

o)  Các phép toán quan hệ trong Turbo Prolog (có giá trị không chỉ đối với

các số mà còn đối với các ký tự, chuỗi, symbol): >, <, =, >=, <=, <>, ><.

p)  Các hàm trong Turbo Prolog: sin, cos, tan, arctan, ln, log, exp, sqrt,

round (số nguyên làm tròn), trunc (số nguyên chặt cụt), abs.

   105

BÀI TẬP CHƯƠNG IV

(Phần bài tập làm quen với vị từ trong Prolog)

Bài 1:

Xét cây gia phả sau:

Ông2 Bà2 Bà1

Bố1Mẹ2 Chú Bố2Mẹ1 Dì

Contrai1 Congái1 Contrai3 Contrai2 Congái2 Congái3

Cây  được mô tả bởi các vị từ quan hệ sau: cha(Cha, Con), mẹ(Mẹ, Con),

nam(DO), nữ (DB).

Hãy liệt kê các quan hệ còn lại như: anhEm(Ng1, Ng2), con(Con, Cha, Mẹ),

chú(Chú, Cháu), bà(Bà, Cháu), anhEmHọ(Ng1, Ng2)... từ những vị từ trên.

Suy ra mối quan hệ giữa hai người bất kỳ.

Bài 2:

Một dịch vụ du lịch phục vụ những chuyến đi trong 1 tuần hoặc 2 tuần ở Rome,

London, Tunis.

Trong một quyển sách giới thiệu về dịch vụ du lịch, với mỗi địa điểm có giá cả

đi lại (tùy vào thời gian của chuyến đi) và chi phí cho 1 tuần, tùy vào địa điểm và

mức độ tiện nghi: khách sạn, nhà trọ, cắm trại ngoài trời.

a)  Nhập vào các sự kiện mô tả quyển sách trên (giá cả do bạn nhập vào).

b)  Hãy mô tả quan hệ ChuyếnĐi(TP, SốTuần, TiệnNghi, ChiPhí).

c)  Mô tả quan hệ ChuyếnĐi_ÍtTốnKém(TP, SốTuần, TiệnNghi, ChiPhí,

ChiPhíMax) sao cho chi phí của chuyến đi thấp hơn ChiPhíMax.

Bài 3:

Trong một dịch vụ tư vấn hôn nhân, liệt kê một danh sách các cặp xứng đôi có

thể làm quen theo các nhận định sau:

-  nam(n, t, c, a), nữ(n, t, c, a).

 n: tên; t: chiều cao.

 c: màu tóc (vàng, hung, nâu, hạt dẻ).

 a: tuổi (trẻ, trung niên, già).   106

-  SởThích(n, m, l, s) (sở thích) có nghĩa là: Người n thích loại nhạc m

(classique, pop, jazz), thích văn học loại l (phiêu lưu, văn học viễn tưởng,

trinh thám), và chơi môn thể thao s(quần_vợt, bơi, chạy bộ).

-  tìm(n, t, c, a): Người n tìm bạn có các tính chất t, c, a.

Hai người x và y gọi là hợp nhau nếu có cùng ngoại hình (t, c, a) và sở thích về:

l, m, s.

a)  Nhập dữ liệu về các khách hàng.

b)  Mô tả qui tắc hợp_ngoạihình(x, y) và cùng_sở thích(x, y).

c)  Viết chương trình xác định những cặp xứng đôi.

(Phần bài tập về danh sách, đệ qui, lát cắt)

Bài 4:

Xây dựng các vị từ đệ quy (có hoặc không sử dụng nhát cắt và sửa đổi phần

đuôi của đệ quy, nếu có thể) để:

a)  Tìm (hoặc xóa) phần tử thứ 1, thứ 2, phần tử cuối cùng trong một danh

sách.

b)  Tìm (hoặc lấy ra) phần tử đầu tiên trong danh sách trùng với một giá trị

cho trước.

c)  Tìm và lấy ra tất cả các phần tử trong danh sách trùng với một giá trị cho

trước.

d)  Tính giá trị trung bình cộng của một danh sách kiểu số thực.

e)  Xuất ra tất cả các cặp phần tử kế tiếp nhau của danh sách.

f)  Tạo danh sách tất cả các phần tử ở vị trí chẵn của danh sách dữ liệu L:

•  Giữ nguyên trật tự xuất hiện trong L.

•  Đảo ngược trật tự.

g)  Viết chương trình trộn(u, v, w) nhận xen kẽ từng phần của u và v để tạo

thành danh sách w.

h)  Tạo danh sách chứa các số chẵn của một danh sách số nguyên cho trước.

i)  Đảo ngược trật tự mọi phần tử của một danh sách.

j)  Xuất ra mọi hoán vị của những phần tử  của danh sách cho trước.

k)  Xây dựng vị từ Thuộc trong đó có thêm một đối để trả về trị TRUE nếu

phần tử x thuộc danh sách l cho trước và FALSE trong trường hợp ngược

lại.

l)  Xây dựng qui tắc NằmNgoài(x, L) để kiểm tra xem x không thuộc danh

sách L.

m) Xây dựng qui tắc KhácNhau(L) để kiểm tra danh sách L không có phần tử

trùng nhau.

n)  Xây dựng qui tắc PhầnĐầu(m, L) để kiểm tra danh sách m là phần đầu của

danh sách L.

o)  Xây dựng qui tắc NằmTrongLiênTiếp(m, L) để kiểm tra mọi phần tử của

m đều nằm liên tiếp trong danh sách L.   107

p)  Xây dựng qui tắc PhânHoạch(L, u, v, w) để phân hoạch danh sách L thành

3 danh sách u, v, w.

q)  Thêm một phần tử vào cuối danh sách.

r)  Tìm và xóa phần tử thứ k của danh sách l cho trước.

s)  Chèn một phần tử x sau phần tử thứ k của danh sách L (0 <= k <=

length(L)).

t)  Xây dựng danh sách mà các phần tử là căn bậc hai của các phần tử tương

ứng trong danh sách L các số thực cho trước.

Bài 5:

Tìm n bộ 3 số tự nhiên (x, y, z) đầu tiên thỏa tính chất Pythagore sau:

 0 < x < y < z

 x2 2

 + y  = z

2

 UCLN(x, y, 1): Ước chung lớn nhất của x và y bằng 1.

Bài 6 (+):

Tìm nk số tự nhiên đầu tiên   thỏa:  n aaa ,...,, 21

∑ =

=

n

i

k

i n aaaa

1

21 ..  (k = 3, 4, 5)  

3 3

 VD:    k = 3, 153 = 1  + 5  + 33

.

Bài 7:

Kiểm tra số tự nhiên N có phải là số nguyên tố không? Tìm n số nguyên tố

đầu tiên.

Bài 8:

Tìm các số nguyên tố <= n ( sàng Eratosthene).

Bài 9:

a)  Nhập 2 danh sách số nguyên dương không có phần tử trùng lặp từ bàn

phím.

b)  Xây dựng các phép toán  và các quan hệ trên tập hợp: Hội, Giao, Hiệu,

Thuộc, Bao hàm.

Bài 10:

Cài  đặt các thuật toán sắp xếp: chọn, chèn, tráo  đổi  đơn giản và sắp xếp

nhanh.

Bài 11 (+):

Các đỉnh của một đồ thị được đánh số từ 0 -> n. Mỗi cung của đồ thị tương

ứng với qui tắc Cung(i, j) nối gốc i với ngọn j.

Xây dựng qui tắc ĐườngĐi(x, y, L) với L là danh sách biểu diễn đường đi

không lặp từ x đến y.

Bài 12 (+):

Cài đặt cây nhị phân tìm kiếm và các thao tác cơ bản.

Bài 13:

Giải quyết bài toán “Tháp Hà Nội” bằng đệ quy.

Bài 14 (*): (8 hậu)

Đặt 8 con hậu lên bàn cờ sao cho không con nào ăn con nào.   108

Bài 15 (*): (Dominos)

Có một danh sách các quân cờ đôminô. Mỗi quân cờ đôminô được biểu diễn

bởi Cặp(i, j) ứng với 2 con số của nó. Người ta tìm cách sắp xếp chúng theo tính

chất sau: Mỗi con đôminô có thể xếp vào một trong hai đầu của hàng đã xếp.

Xuất phát từ một con đôminô trong số các con được cho, hãy xây dựng một

dãy hợp lệ chứa tất cả các đôminô.

Bài 16 (*):

Giải quyết bài toán ma phương cấp n lẻ.

(Phần bài tập về trò chơi, suy luận lôgic)

Bài 17: (Ngựa vằn)

Có 5 người quốc tịch khác nhau sống trong 5 căn nhà màu khác nhau. Mỗi

người có một con thú riêng, một thức uống riêng, và hút thuốc của những hãng

khác nhau.

-  Người Anh sống trong căn nhà màu đỏ.

-  Con chó thuộc về người Tây Ban Nha.

-  Người uống cà phê trong căn nhà màu xanh lá cây.

-  Người Ukrain uống trà.

-  Ngôi nhà xanh lá cây ở cạnh ngôi nhà trắng, bên phải.

-  Người hút thuốc OldGold nuôi những con sên.

-  Người uống sữa ở ngôi nhà chính giữa.

-  Người hút thuốc Kool sống trong căn nhà màu vàng.

-  Người Nauy sống trong căn nhà đầu tiên bên trái.

-  Người hút thuốc Chesterfield sống cạnh người có nuôi cáo.

-  Người hút thuốc Kool sống cạnh người có nuôi ngựa.

-  Người hút thuốc Gitanes uống rượu.

-  Người Nhật hút thuốc Craven

-  Người Nauy sống cạnh ngôi nhà màu xanh da trời.

Hỏi: Ai uống nước? Ai là chủ con ngựa vằn?

Bài 18 (*): (Trò chơi tốt đen và tốt trắng)

Bắt  đầu từ 3 tốt  đen và 3 tốt trắng  đã  được sắp xếp và cách nhau bởi một

khoảng trắng.

VD:   BBB_NNN.

Mỗi lần đánh chỉ có 1 trong 4 phép dịch chuyển sau được thi hành:

Tốt đen trượt sang trái:

BNB_NNB -> BNBN_NB

Tốt đen nhảy sang trái:

BN_BNNB -> BNNB_NB

Tốt trắng trượt sang phải:

BNB_NNB -> BN_BNNB

Tốt trắng nhảy sang phải:

BBN_NNB -> B_NBNNB   109

Viết 1 chương trình để chuyển từ trạng thái đầu sang trạng thái kết (mọi tốt

trắng đều nằm bên phải).

Bài 19 (*):

Cho 1 biểu thức với phép * (Nhan) và + (Cong). Hãy sửa đổi để được 1 tổng

các tích bằng cách thêm vào dấu ngoặc.

VD: a * (b + c) + (d + e) * (f + g) = (((((a * b + a * c) + d * f) + e * f) + d * g)

+ e * g)

Có nghĩa là:

Cong(Nhan(a, Cong(b,  c), Nhan(Cong(d, e), Cong(f, g)))) cho ra

Cong(Cong(Cong(Cong(Cong(Nhan(a, b), Nhan(a, c)), Nhan(d, f)), Nhan(e, f)),

Nhan(d, g)), Nhan(e, g))

Hãy xây dựng chương trình trên theo 2 giai đoạn:

-  Phân phối Nhan sao cho không có Nhan nào có Cong làm đối.

-  Chuyển thành cây sao cho nhánh phải của Cong không chứa Cong khác,

nhánh phải của Nhan không chứa Nhan khác.

Bài 20 (*):

Cho biểu thức dạng And(p, q), Or(p, q), Not(q), trong đó p, q là biến hoặc là

biểu thức cùng dạng. Ta có:

And(Or(p, q), r)   <=>   Or(And(p, r), And(q, r))

And(p, Or(q, r))   <=>   Or(And(p, q), And(p, r))

Not(Or(p, q))       <=>   And(Not(p), Not(q))

Not(And(p, q))     <=>   Or(not(p), not(q))

Not(Not(p))          <=>   p

Hãy chuyển biểu thức theo yêu cầu sau:

Mỗi nút Or chỉ có tổ tiên là Or.

Mỗi nút And chỉ có tổ tiên là And.

a)  Viết chương trình thực hiện công việc trên.

b)  Thay đổi chương trình để mỗi nhánh trái Or không chứa nút Or khác, và

mỗi nhánh trái And không chứa And khác.

Bài 21 (*): (Những ông chồng hay ghen)

Có ba cặp vợ chồng phải qua sông nhưng chỉ có một chiếc thuyền chỉ chở

được tối đa hai người. Hãy lập trình cho các chuyến đi để sao cho không có bà vợ

nào ở trên bờ hay trên thuyền với những người đàn ông X khác mà không có chồng

mình hoặc không có đồng thời với vợ của X.

Bài 22 (*): (Sói, dê và bắp cải)

Có một con sói, một con dê và một bắp cải phải qua sông. Chỉ có một người

chèo thuyền, chỉ chở được mỗi lần một trong số chúng. Nếu không có mặt người

chèo thuyền thì dê sẽ ăn bắp cải và sói sẽ ăn thịt dê. Hãy lập trình cho các chuyến

đi.

   110

TÀI LIỆU THAM KHẢO

                                      

[1] Bạch Hưng Khang, Hoàng Kiếm. Trí tuệ nhân tạo - Các phương pháp và

ứng dụng. NXB Khoa học Kỹ thuật, 1989.

[2] Hoàng Kiếm. Giải một bài toán trên máy tính như thế nào (tập 1 và 2).

NXB Giáo dục, 2001.

[3] Nguyễn Thanh Thủy. Trí tuệ nhân tạo - Các phương pháp giải quyết vấn

đề và kỹ thuật xử  lý tri thức. NXB Giáo dục, 1995. 

[4] A. Thayse.  Approche logique de l’intelligence artificielle (T1, T2, T3).

Dunod, Paris, 1990. 

[5]. Ivan Bratko, Prolog - Programming for artificial intelligence, Addison-

Wesley, 1990. 

Bạn đang đọc truyện trên: Truyen2U.Pro