0
Tải bản đầy đủ (.pdf) (66 trang)

Thuật giải AT

Một phần của tài liệu CHUẨN MÃ DỮ LIỆU DES VÀ XÂY DỰNG CHƯƠNG TRÌNH ỨNG DỤNG (Trang 41 -41 )

Thuật giải AT là một phương pháp tìm kiếm theo kiểu BFS với độ tốt của nút là giá trị hàm g – tổng chiều dài con đường đã đi từ trạng thái bắt đầu đến trạng thái hiện tại.

Thuật giải AT

1. Đặt OPEN chứa trạng thái khởi đầu.

2. Cho đến khi tìm được trạng thái đích hoặc không còn nút nào trong OPEN, thực hiện :

2.a. Chọn trạng thái (Tmax) có giá trị g nhỏ nhất trong OPEN (và xóa Tmax khỏi OPEN)

2.b. Nếu Tmax là trạng thái kết thúc thì thoát.

2.c. Ngược lại, tạo ra các trạng thái kế tiếp Tk có thể có từ trạng thái Tmax. Đối với mỗi trạng thái kế tiếp Tk thực hiện :

g(Tk) = g(Tmax) + cost(Tmax, Tk); Thêm Tk vào OPEN.

Vì chỉ sử dụng hàm g (mà không dùng hàm ước lượng h’) f để đánh giá độ tốt của một trạng thái nên ta cũng có thể xem AT chỉ là một thuật toán. 2.2.5. Thuật giải AKT (Algorithm for Knowlegeable Tree Search)

Thuật giải AKT mở rộng AT bằng cách sử dụng thêm thông tin ước lượng h’. Độ tốt của một trạng thái f là tổng của hai hàm g và h’.

Thuật giải AKT

1. Đặt OPEN chứa trạng thái khởi đầu.

2. Cho đến khi tìm được trạng thái đích hoặc không còn nút nào trong OPEN, thực hiện :

2.a. Chọn trạng thái (Tmax) có giá trị f nhỏ nhất trong OPEN (và xóa Tmax khỏi OPEN)

2.b. Nếu Tmax là trạng thái kết thúc thì thoát.

2.c. Ngược lại, tạo ra các trạng thái kế tiếp Tk có thể có từ trạng thái Tmax. Đối với mỗi trạng thái kế tiếp Tk thực hiện :

g(Tk) = g(Tmax) + cost(Tmax, Tk); Tính h’(Tk)

f(Tk) = g(Tk) + h’(Tk); Thêm Tk vào OPEN. 2.2.6. Thuật giải A*

Thuật toán A* là thuật toán sử dụng kỹ thuật tìm kiếm tốt nhất đầu tiên với hàm đánh giá f(u).

Để thấy được thuật toán A* làm việc như thế nào, ta xét đồ thị không gian trạng thái trong hình 2.7. Trong đó, trạng thái ban đầu là trạng thái A, trạng thái đích là B, các số ghi cạnh các cung là độ dài cung đó, các số cạnh các đỉnh là giá trị của hàm h.Đầu tiên, phát triển đỉnh A sinh ra các đỉnh con C, D, E và F. Tính giá trị của hàm f tại các đỉnh này ta có:

g(C) = 9, f(C) = 9 + 15 = 24, g(D) = 7, f(D) = 7 + 6 = 13, g(E) = 13, f(E) = 13 + 8 = 21, g(F) = 20, f(F) = 20 +7 = 27 Như vậy đỉnh tốt nhất là D (vì f(D) = 13 là nhỏ nhất). Phát triển D, ta nhận được các đỉnh con H và E. Ta đánh giá H và E (mới): g(H) = g(D) + Độ dài cung (D, H) = 7 + 8 = 15, f(H) = 15 + 10 = 25. Đường đi tới E qua D có độ dài: g(E) = g(D) + Độ dài cung (D, E) = 7 + 4 = 11.

Vậy đỉnh E mới có đánh giá là f(E) = g(E) + h(E) = 11 + 8 = 19. Trong số các đỉnh chờ phát triển, thì đỉnh E với đánh giá f(E) = 19 là đỉnh tốt nhất. Phát triển đỉnh này, ta nhận được các đỉnh con của nó là K và I. Chúng ta tiếp tục quá trình trên cho tới khi đỉnh được chọn để phát triển là đỉnh kết thúc B, độ dài đường đi ngắn nhất tới B là g(B) = 19. Quá trình tìm kiếm trên được mô tả bởi cây tìm kiếm trong hình 2.8, trong đó các số cạnh các đỉnh là các giá trị của hàm đánh giá f(u).

procedure A*; begin

1. Khởi tạo danh sách L chỉ chứa trạng thái ban đầu; 2. loop do

2.1 if L rỗng then

{thông báo thất bại; stop};

2.2 Loại trạng thái u ở đầu danh sách L; 2.3 if u là trạng thái đích then

{thông báo thành công; stop} 2.4 for mỗi trạng thái v kề u do {g(v) ←g(u) + k(u,v);

f(v) ←g(v) + h(v); Đặt v vào danh sách L;}

2.5 Sắp xếp L theo thứ tự tăng dần của hàm f sao cho trạng thái có giá trị của hàm f nhỏ nhất

ở đầu danh sách; end;

Một số nhận xét về thuật toán A*:

Người ta chứng minh được rằng, nếu hàm đánh giá h(u) là đánh giá thấp nhất (trường hợp đặc biệt, h(u) = 0 với mọi trạng thái u) thì thuật toán A* là thuật toán tối ưu, tức là nghiệm mà nó tìm ra là nghiệm tối ưu. Ngoài ra, nếu độ dài của các cung không nhỏ hơn một số dương δ nào đó thì thuật toán A* là thuật toán đầy đủ theo nghĩa rằng, nó luôn dừng và tìm ra nghiệm.

Chúng ta chứng minh tính tối ưu của thuật toán A*.

Hình 2.9: Đỉnh lá n của cây tìm kiếm nằm trên đường đi tối ưu

Giả sử thuật toán dừng lại ở đỉnh kết thúc G với độ dài đường đi từ trạng thái ban đầu u0 tới G là g(G). Vì G là đỉnh kết thúc, ta có h(G) = 0 và f(G) = g(G) + h(G) = g(G). Giả sử nghiệm tối ưu là đường đi từ u0 tới đỉnh kết thúc G1 với độ dài l. Giả sử đường đi này “thoát ra” khỏi cây tìm kiếm tại đỉnh lá n (xem hình 2.9). Có thể xảy ra hai khả năng: n trùng với G1 hoặc không. Nếu n là G1 thì vì G được chọn để phát triển trước G1, nên f(G) ≤ f(G1), do đó g(G) ≤ g(G1) = l. Nếu n ≠ G1 thì do h(u) là hàm đánh giá thấp, nên f(n) = g(n) + h(n) ≤ l. Mặt khác, cũng do G được chọn để phát triển trước n, nên f(G) ≤f(n), do đó, g(G) ≤l. Như vậy, ta đã chứng minh được rằng độ dài của đường đi mà thuật toán tìm ra g(G) không dài hơn độ dài l của đường đi tối ưu. Vậy nó là độ dài đường đi tối ưu.

- Trong trường hợp hàm đánh giá h(u) = 0 với mọi u, thuật toán A* chính là thuật toán tìm kiếm tốt nhất đầu tiên với hàm đánh giá g(u) mà ta đã nói đến.

- Thuật toán A* đã được chứng tỏ là thuật toán hiệu quả nhất trong số các thuật toán đầy đủ và tối ưu cho vấn đề tìm kiếm đường đi ngắn nhất.

Chương 3: XÂY DỰNG CHƯƠNG TRÌNH THỬ NGHIỆM

Giống như các ngành khoa học khác, trí tuệ nhân tạo cũng được chia thành các ngành con với nhiều ứng dụng khác nhau. Một ứng dụng quan trọng của trí tuệ nhân tạo là xây dựng thiết kế các trò chơi. Hầu hết các trò chơi đều được áp dụng một tập hợp các luật chơi rõ ràng. Điều này làm phát sinh không gian tìm kiếm trở nên dễ dàng và giải phóng các nghiên cứu khỏi những lý thuyết mơ hồ và phức tạp vốn có trong các bài toán phức tạp ít cấu trúc.

3.1. Giới thiệu bài toán

Bài toán gồm một hình vuông kích thước n×n ô, có n2-1 ô có số, mỗi ô có 1 số từ 1 đến n2-1, một ô còn trống. Mỗi lần chỉ di chuyển được một ô nằm cạnh ô trống về phía ô trống. Vấn đề từ một trạng thái ban đầu bất kỳ, bằng những thao tác dịch chuyển lên trên, xuống dưới, sang trái, sang phải hãy đưa hình vuông về dạng ô trống ở góc phải phía dưới và thứ tự các ô theo thứ tự tự nhiên, theo thứ tự từ trái sang phải từ trên xuống dưới.

Cơ sở của trò chơi xếp hình là dựa vào bài toán trên. Đây là nột trò chơi phổ biến, nó giúp rèn luyện tính kiên trì, phát huy tối đa trí tưởng tượng và tư duy hình học của người chơi phù hợp với mọi lứa tuổi, mọi giới.

Ví dụ: Bài toán 8 số:

Chúng ta có bảng 3×3 và tám quân mang số hiệu từ 1 đến 8 được xếp vào 8 ô, còn lại một ô trống, chẳng hạn như trong hình 3.1 bên trái. Trong trò chơi này, bạn có thể dịch chuyển các quân ở cạnh ô trống tới ô trống đó. Vấn đề là tìm ra một dãy các dịch chuyển để biến đổi trạng thái ban đầu (hình 3.1 bên trái) thành một tình huống xác định nào đó, chẳng hạn như trạng thái trong hình 3.1 bên phải. Trong bài toán này, trạng thái ban đầu là hình 3.1 bên trái, còn trạng thái kết thúc ở hình 3.1 bên phải.

Quy tắc dịch chuyển các quân: 1 7 4

3 5

8 6 2

Hình 3.2: Một trạng thái của bài toán 8 số

- Nếu ô trống nằm ở hàng 1 thì không dịch chuyển lên trên.

- Nếu ô trống nằm ở hàng cuối thì không dịch chuyển xuống dưới. - Nếu ô trống nằm ở cột đầu tiên thì không dịch chuyển sang trái. - Nếu ô trống nằm ở cột cuối cùng thì không dịch chuyển sang phải. - Nếu dịch lên hoặc dịch xuống thì vị trí của cột không thay đổi. - Nếu dịch sang trái hoặc sang phải thì vị trí của hàng không thay đổi.

Ví dụ:

Hình 3.3: Phát triển một trạng thái

Ta có bốn toán tử: up (đẩy quân lên trên), down (đẩy quân xuống dưới), left (đẩy quân sang trái), right (đẩy quân sang phải). Chẳng hạn, từ trạng thái ban đầu (hình 3.1 bên trái) ta chỉ có thể áp dụng các toán tử down, left, right (hình 3.3).

Trong thực tế bài toán n2-1 số có thể mở rộng thành nhiều bài toán. Trong chương trình này em sẽ mở rộng bài toán thành các bài toán:

- Chọn kích cỡ của ván chơi 8 ô (3×3), 16 ô (4×4), 25 ô (5×5), 36 ô (6×6). - Bài toán ghép tranh (image): mỗi ô số được thay bằng một mảnh ghép của một bức tranh, nhiệm vụ của chúng ta là sắp xếp những mảnh ghép này thành một bức tranh hoàn chỉnh.

- Bài toán sắp xếp tranh có đánh số (image – number). - Bài toán sắp xếp số (number).

3.2. Giải quyết bài toán

3.2.1. Áp dụng giải thuật A* (Astar)

Cho đến nay, ngoại trừ 2 giải pháp vét cạn và tìm kiếm heuristic, người ta vẫn chưa tìm được một thuật toán chính xác, tối ưu để giải bài toán này. Tuy nhiên, cách giải theo thuật giải A* lại khá đơn giản và thường tìm được lời giải. Để giải quyết bài toán n2-1 số ta quy bài toán thành giải quyết bài toán 8 số.

Ví dụ bài toán 8 số:

Hình 3.4: Một trạng thái ban đầu và trạng thái kết thúc của bài toán 8 số

Tại mỗi thời điểm ta chỉ có tối đa 4 ô có thể di chuyển. Vấn đề là tại thời điểm đó, ta sẽ chọn lựa di chuyển ô nào? Chẳng hạn, ở hình 3.4 ta nên di chuyển (1), (2), (6) hay (7)? Bài toán này hoàn toàn có cấu trúc thích hợp để có thể giải bằng A* (tổng số trạng thái có thể có của bàn cờ là n2! với n là kích thước bàn cờ vì mỗi trạng thái là một hoán vị của tập n2 con số).

Tại một trạng thái đang xét Tk, đặt d(i,j) là số ô cần di chuyển để đưa con số ở ô (i,j) về đúng vị trí của nó ở trạng thái đích.

Hàm ước lượng h’ tại trạng thái Tk bất kỳ bằng tổng của các d(i,j) sao cho vị trí (i,j) không phải là ô trống.

Như vậy đối với trạng thái ở hình 3.4, hàm f(Tk) sẽ có giá trị là Fk=2+1+3+1+0+1+2+2=12

3.2.2. Áp dụng giải thuật A* lặp sâu dần (IDAstar) Giải thuật IDA*: Giải thuật IDA*:

Function IDA*(problem) return một dãy giải pháp Inputs: problem, một bài toán, vấn đề

Các biến địa phương: giới hạn-f, giới hạn chi phí f hiện thời Root, một nút

Root – make – node (trạng thái ban đầu[problem]) Giới hạn – f – chi phí – f(root)

Loop do

Giải pháp, giới hạn – f – DFS-contour(root, giới hạn-f) If giải pháp khác rỗng then return giải pháp

If giới hạn – f = ∞ then return thất bại; end

Function dfs-contour (root, giới hạn-f) returns một dãy giải pháp và một giới hạn chi phí f mới.

Input: node, một nút

Giới hạn – f, giới hạn f chi phí hiện thời

Các biến địa phương: next-f, giới hạn chi phí f của contour tiếp theo, ban đầu là ∞ If chi phí – f[node] > giới hạn-f then return rỗng, chi phí-f[node]

If goal-test[problem](state[node]) then return node, giới hạn-f For mỗi nút s trong successor(node) do

Giải pháp, f-mới-dfs-contour(s, giới hạn-f)

If giải pháp khác rỗng then return giải pháp, giới hạn-f Next-f ←MIN(next-f, new-f); end

Lặp sâu dần là một kỹ thuật rất hiệu quả để giảm bộ nhớ. Trong giải thuật này mỗi phép lặp là một phép tìm kiếm theo chiều sâu. Phép tìm kiếm theo chiều sâu được sửa đổi để sử dụng một giới hạn chi phí f thay vì một giới hạn độ sâu. Như vậy mỗi vòng lặp mở rộng các nút bên trong đường viền của chi phí f hiện tại, nhìn vào đường viền để tìm đường viền tiếp theo ở đâu. Khi phép tìm kiếm bên trong một đường viền đã cho đã hoàn thành, vòng lặp mới lại bắt đầu sử dụng một chi phí f mới cho đường viền tiếp theo. IDA* là hoàn thành và tối ưu với cung một dự báo cho trước như phép tìm kiếm A*, nhưng do nó là phương pháp tìm kiếm theo chiều sâu, nó chỉ yêu cầu không gian bộ nhớ tỷ lệ với đường đi dài nhất mà nó khám phá. Nếu là chi phí nhỏ nhất và f* là chi phí giải pháp tối ưu, thì trong trường hợp tồi nhất IDA* sẽ yêu cầu bf*/ nút lưu trữ. Trong hầu hết các trường hợp bd là sự ước đoán rất tốt đối với yêu cầu về dung lượng lưu trữ.

3.3. Xây dựng chương trình

Chương trình dùng một mảng hai chiều để lưu một trạng thái của trò chơi. Mỗi trạng thái đó còn có các thuộc tính khác như:

- Chi phí đi từ trạng thái khởi đầu đến trạng thái hiện tại.

- Chỉ số ước lượng Heuristic, là chi phí để đi đến đích từ trạng thái đó. - Chỉ số f là tổng hai chi phí bên trên.

- Và trạng thái cha của trạng thái hiện tại.

Khi một trạng thái được tạo ra thì các chỉ số phía trên cũng được tính toán là gán vào cho trạng thái. Bên cạnh đó khi trạng thái thay đổi thì các chỉ số cũng được tính toán hoặc xử lý lại cho phù hợp.

Chương trình xây dựng lớp PuzzleGame để xử lý trò chơi và đưa ra một danh sách chứa các trạng thái để trò chơi có thể đi đến đích.

Thuật toán A*

public void solveAstar() { //Giải quyết A* KQ.clear();

long startTime = System.currentTimeMillis();

initialnode.f = initialnode.h = initialnode.estimate(goalnode); initialnode.g = 0;

FRINGE.add(0, initialnode); // cho nút đầu đầu tiên vào FRINGE total_nodes = 0;

count = 0; while (true) {

if (FRINGE.isEmpty() || !(Main.window.issolu)) //điều kiện dừng { FRINGE.clear(); M.clear(); Stop = "stop"; return; }

if (System.currentTimeMillis()- startTime > 180000) //điều kiện dừng { FRINGE.clear(); M.clear(); Stop = ":D \n"; return; } lowIndex = 0; fmin = FRINGE.elementAt(0).f;

for (int i = 0; i < FRINGE.size(); i++) // tìm nút có f nhỏ nhất trong FRINGE { number = FRINGE.elementAt(i).f; if (number < fmin) {

lowIndex = i; //vị trí nút có fmin trong FRINGE fmin = number;

} }

n = FRINGE.elementAt(lowIndex); //xét nút n có fmin

FRINGE.removeElement(n); //xóa nút đã xét trong FRINGE

if(n.h == 0) //if (n.equals(goalnode)) //kiểm tra nút đang xét có phải là đích

{

long endTime = System.currentTimeMillis(); time_solve = endTime - startTime;

total_nodes = count + FRINGE.size(); AddKQ(n); //đưa kết quả vào trong KQ FRINGE.clear();

M.clear(); return; }

M = n.successors(); // sinh tâp trạng thái con của n if(n.Parent != null)

{

{

if(isKT(n.Parent,M.elementAt(i))) // xóa trạng thái con của n trùng với trạng thái Cha(n)

M.remove(i); }

}

for (int i = 0; i < M.size(); i++) // tính thông số của các trạng thái con { Node s = M.elementAt(i); s.g = n.g + s.cost; s.h = s.estimate(goalnode); s.f = s.g + s.h; tempNode = (Node)M.elementAt(i);

tempNode.Parent = n; // đặt n là cha các trạng thái con

FRINGE.add(0, M.elementAt(i)); // thêm các trạng thái con vào FRINGE

}

count++; //tăng số nút đã duyệt }

}

Thuật toán IDA*

public void solveIDAstar() { //giải quyết IDA* KQ.clear();

long startTime = System.currentTimeMillis();

initialnode.f = initialnode.h = initialnode.estimate(goalnode); initialnode.g = 0;

cutOff = 0; total_nodes = 0; count = 0; while (true) {

while (FRINGE.size() != 0) //quá trình tìm kiếm sâu dần { if (FRINGE.isEmpty() || !(Main.window.issolu)) { FRINGE.clear(); M.clear(); VISIT.clear(); Stop = "stop"; return; }

if (System.currentTimeMillis()- startTime > 180000) //điều kiện dừng { FRINGE.clear(); M.clear(); Stop = ":D \n"; return; }

n = FRINGE.elementAt(0); //lấy nút đầu tiên trong FRINGE

if(n.f <= cutOff) count++; //nếu nút n có f ko vượt ngưỡng cutOff thì n là nút đã được xét

FRINGE.removeElement(n); //xóa nút n

{

long endTime = System.currentTimeMillis(); time_solve = endTime - startTime;

total_nodes = count + FRINGE.size() + VISIT.size(); AddKQ(n); FRINGE.clear(); VISIT.clear(); M.clear(); return; }

if (n.f <= cutOff) //nếu nút n có f ko vượt ngưỡng cutOff thì xét n {

M = n.successors(); // sinh các trạng thái con của n if(n.Parent!=null)

{

for(int i = 0; i < M.size(); i++) {

if(isKT(n.Parent,M.elementAt(i))) // xóa trạng thái con của n trùng với trạng thái Cha(n)

M.remove(i); }

}

for (int i = 0; i < M.size(); i++) {

Node s = M.elementAt(i); s.g = n.g + s.cost;

s.f = s.g + s.h; tempNode = (Node)M.elementAt(i); tempNode.Parent = n; FRINGE.add(0, M.elementAt(i)); } }

Một phần của tài liệu CHUẨN MÃ DỮ LIỆU DES VÀ XÂY DỰNG CHƯƠNG TRÌNH ỨNG DỤNG (Trang 41 -41 )

×