Thuật toán tìm kiếm theo chiều rộng (Breadth First Search)

Một phần của tài liệu Một số thuật toán tìm đường đi ngắn nhất và xây dựng ứng dụng GAME PIKACHU (Trang 32)

a. Tư tưởng thuật toán

Kỹ thuật tìm kiếm rộng là tìm kiếm trên tất cả các nút của một mức trong không gian bài toán trước khi chuyển sang các nút của mức tiếp theo. Kỹ thuật tìm kiếm rộng bắt đầu từ mức thứ nhất của không gian bài toán, theo hướng dẫn của luật trọng tài, chẳng hạn “đi từ trái sang phải”. Nếu không thấy

A C D B E G F

33

lời giải tại mức này, nó chuyển xuống mức sau để tiếp tục… đến khi tìm được lời giải nếu có.

Tìm kiếm theo chiều rộng là thuật toán tìm kiếm trên tất cả các nút của một mức trong không gian bài toán trước khi di chuyển sang các nút của mức tiếp theo. Tìm kiếm theo chiều rộng chủ yếu được sử dụng để tìm đường đi ngắn nhất theo số cạnh giữa hai đỉnh của đồ thị.

b. Phân tích thuật toán

Thuật toán tìm kiếm theo chiều rộng thực ra là sự cải biến về thứ tự duyệt đỉnh trên đồ thị của tìm kiếm theo chiều sâu bằng cách thay vì dùng một ngăn xếp (STACK) thì ta lại dùng một hàng đợi (QUEUE) để kết nạp đỉnh được thăm.

Như vậy, đỉnh được duyệt càng sớm sẽ càng sớm trở thành duyệt xong (cơ chế First In First Out - Vào trước ra trước).

c. Thuật toán

Thuật toán tìm kiếm theo chiều rộng được mô tả qua thủ tục sau:

Procedure Bread_First_Search

Begin

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

If L rỗng then

{thông báo tìm kiếm thất bại; stop}; Loại trạng thái u ở đầu danh sách L; If u là trạng thái kết thúc then

{thông báo tìm kiếm thành công, stop}; For mỗi trạng thái v kề u do {

Đặt v vào cuối danh sách L; Father(v) <-u}

34

Gọi b là nhân tố nhánh. Giả sử rằng, nghiệm của bài toán là đường đi có độ dài d. Vì nhiều nghiệm có thể được ra tại một đỉnh bất kỳ ở mức d của

cây tìm kiếm, do đó số đỉnh cần xem xét để tìm nghiệm là: 1 + b + b2 +… + bd-1 + k

Trong đó k có thể là 1, 2, …, bd. Do đó số lớn nhất các đỉnh cần xem xét là:

1 + b + b2 +… + bd

Vì cần lưu vào danh sách L tất cả các đỉnh của cây tìm kiếm ở mức d,

số các đỉnh này là bd.

Như vậy, độ phức tạp thời gian của thuật toán tìm kiếm theo chiều rộng là O(bd). Độ phức tạp không gian là O(bd)

d. Cách biểu diễn trạng thái

Giải thuật tìm kiếm theo chiều rộng là giải thuật tìm kiếm theo mức của cây. Giải thuật bắt đầu từ nút gốc của cây tìm kiếm qua tất cả các nút ở mức tiếp theo, nếu chưa tìm thấy thì vẫn tiếp tục tìm kiếm qua tất cả các nút ở mức sâu hơn, cứ như vậy cho đến khi tìm thấy nút đích thì dừng thủ tục tìm kiếm và thiết lập đường lời giải bắt đầu từ nút gốc thông qua các nút liên tiếp đến nút đích.

Ví dụ: Cho không gian trạng thái của bài toán với các trạng thái được đánh nhãn bằng các chữ cái A, B, C, D, E, F, G. Bằng thuật toán tìm kiếm theo chiều rộng tìm đường đi từ A đến G.

Theo sơ đồ biểu diễn trạng thái của bài toán (hình 2.5) thuật toán tìm kiếm theo chiều rộng bắt đầu duyệt đỉnh A, đi theo lần lượt các đỉnh con, rồi lần lượt tìm kiếm các đỉnh con phía dưới. Thứ tự duyệt các đỉnh là: A, B, C, E, D, F, G.

Quá trình duyệt các đỉnh diễn ra như sau: Sau khi duyệt đỉnh A, lần lượt duyệt hết các đỉnh con của A là B, C, E theo các cạnh tương ứng. Sau khi duyệt xong các đỉnh con của A, ta lần lượt duyệt các đỉnh con của B là D, F và lần lượt duyệt các đỉnh con của C là G. Vì E không có đỉnh con nên quay

35

về duyệt các đỉnh con của các đỉnh B, C. Các đỉnh D, F, G đều không có đỉnh con nên thuật toán kết thúc, tất cả các đỉnh của đồ thị đều đã được duyệt đến. 2.3.4. Thuật toán tìm kiếm sâu dần

a. Tư tưởng thuật toán

Tìm kiếm sâu dần là kết hợp của tìm kiếm rộng và tìm kiếm sâu trên cơ

sở cho biết mức sâu n rồi tìm kiếm rộng ứng với mức sâu đó.

Kỹ thuật tìm kiếm sâu dần là thực hiện việc tìm kiếm với mức độ sâu ở

mức giới hạn d nào đó. Nếu không tìm ra nghiệm ta tăng độ sâu lên d+1 và lại tìm kiếm theo độ sâu tới mức d+1. Quá trình trên được lặp lại với d lần lượt là

1, 2,… đến độ sâu nào dó mà bài toán yêu cầu .

Kỹ thuật tìm kiếm sâu dần thường được thực hiện khi cây tìm kiếm chứa nhánh vô hạn, và nếu sử dụng tìm kiếm theo độ sâu ta có thể mắc kẹt ở một nhánh nào đó (thuật toán không dừng) và không tìm ra nghiệm.

Vấn đề với giải thuật tìm kiếm với giới hạn độ sâu (DLS). Nếu tất cả

các lời giải (các nút đích) nằm ở độ sâu l, thì giải thuật tìm kiếm sâu dần thất

bại (không tìm được lời giải). b. Giải thuật tìm kiếm sâu dần

Áp dụng giải thuật tìm kiếm sâu dần đối với các đường đi (trong cây) có độ dài <=1.

Nếu thất bại (không tìm được lời giải) tiếp tục áp dụng giải thuật tìm kiếm sâu dần đối với các đường đi có độ dài <=2.

Nếu thất bại (không tìm được lời giải) tiếp tục áp dụng giải thuật tìm kiếm sâu dần đối với các đường đi có độ dài <=3.

Tổng quát, giải thuật tiếp tục như vậy cho đến khi: + Tìm được lời giải

36 c. Thuật toán

Thuật toán tìm kiếm sâu dần (Iterative_Depening_Search) sẽ sử dụng thuật toán tìm kiếm sâu hạn chế (Depth_ Limited_Search) như thủ tục con. Đó

là thủ tục tìm kiếm theo độ sâu, nhưng chỉ tới độ sâu d nào đó rồi quay lên. Trong thủ tục tìm kiếm sâu hạn chế, d là tham số độ sâu, hàm depth ghi

lại độ sâu của mỗi đỉnh.

Thủ tục tìm kiếm sâu hạn chế

Procedure Depth_Limited_search(d);

Begin

Khởi tạo danh sách L chỉ chứa trạng thái ban đầu u0; Depth(u0)←0;

Loop do

If L then

{thông báo thất bại; stop}; Loại trạng thái u ở đầu danh sách L; If u là trạng thái kết thúc then

{thông báo thành công; stop}; if depth(u) <= d then

for mỗi trạng thái v kề u do

{đặt v vào đầu danh sách L; Depth(v) ← depth(u) + 1}; End; Thủ tục tìm kiếm sâu dần Procedure Depth_Deepening_Search; Begin For d←0 to max do {Depth_limited_Search(d); If thành công then exit}

37 Độ phức tạp thuật toán

Mỗi lần gọi thủ tục tìm kiếm sâu hạn chế tới mức d, nếu cây tìm kiếm có nhân tố nhánh b thì số đỉnh cần phát triển là:

1 + b + b2 +… + bd

Nếu nghiệm ở độ sâu d thì trong tìm kiếm sâu dần gọi thủ tục sâu hạn chế với độ sâu lần lượt là 0, 1, 2,… , d. Do đó các đỉnh ở mức 1 phải phát triển lặp d lần, các đỉnh ở mức 2 lặp d-1 lần ,…, các đỉnh ở mức d lặp 1 lần. Như vậy tổng số đỉnh cần phát triển trong tìm kiếm sâu dần là:

(d+1)1 + db + (d-1)b2 + … + 2bd-1 + 1bd

Vậy tìm kiếm sâu dần có độ phức tạp thời gian là O(bd) (như tìm kiếm theo chiều rộng) và độ phức tạp không gian là O(bd) (như tìm kiếm theo chiều sâu) 2.3.5. Thuật toán Dijkstra tìm đường đi ngắn nhất

a. Tư tưởng thuật toán

Thuật toán Dijkstra cho phép tìm đường đi ngắn nhất từ một đỉnh s đến

các đỉnh các đỉnh còn lại của đồ thị và chiều dài (trọng số) tương ứng. Phương

pháp của thuật toán là xác định tuần tự đỉnh có chiều dài đến s theo thứ tự tăng

dần. Thuật toán được xây dựng trên cơ sở gán cho mỗi đỉnh các nhãn tạm thời. Nhãn tạm thời của các đỉnh cho biết cận trên của chiều dài đường đi ngắn nhất

từ s đến đỉnh đó. Nhãn các đỉnh sẽ biến đổi trong các bước lặp, mà ở mỗi bước

lặp sẽ có một nhãn tạm thời trở thành chính thức. Nếu nhãn của một đỉnh nào

đó trở thành chính thức thì đó cũng chính là chiều ngắn nhất của đường đi từ s

đến đỉnh đó.

b. Phân tích thuật toán

Có thể giải bài toán bằng cách xác định một tập hợp S chứa các đỉnh mà khoảng cách ngắn nhất từ đỉnh đó đến đỉnh nguồn v đã biết. Khởi đầu

S=V. Sau đó tại mỗi bước ta sẽ thêm vào S các đỉnh mà khoảng cách từ nó

đến v là ngắn nhất. Với giả thiết rằng mỗi cung có một giá trị không âm thì ta

38

đã tồn tại mảng C, tức là C[i,j] bằng giá trị (có thể xem là độ dài) của cung

(i,j). Nếu i và j không có cung nối thì ta cho C[i,j]=G. Dùng một mảng D

gồm n phần tử để lưu độ dài của đường đi ngắn nhất từ v đến mỗi đỉnh của đồ thị. Khởi đầu thì giá trị này chính là độ dài cạnh (v,i), tức D[i]=C[v,i]. Tại mỗi bước của giải thuật thì D[i] sẽ lưu độ dài đường đi ngắn nhất từ đỉnh v đến đỉnh i, đường đi này chỉ đi qua các đỉnh đã có mặt trong S.

Dùng ma trận kề để biểu diễn đồ thị C=(cij), cij = trọng số của cung (i,j), cij =∞ nếu không có cung (i,j). Một mảng d[] để ghi các độ dài đường đi ngắn nhất từ s tới đỉnh i đang có. Xuất phát d[s] =0 và d[i] =cij nếu i kề s, d[j] =+

∞ nếu j không kề s.

c. Giải thuật tìm đường đi ngắn nhất

Có rất nhiều giải thuật đã được phát triển để giải bài toán tìm đường đi ngắn nhất giữa một cặp đỉnh. Giải thuật Dijkstra là một giải thuật để giải bài toán tìm đường đi ngắn nhất nguồn đơn trên một đồ thị có trọng số cạnh mà tất cả các trọng số đều không âm. Nó xác định đường đi ngắn nhất giữa hai

đỉnh cho trước, từ đỉnh a đến đỉnh b.

Ở mỗi đỉnh v, giải thuật Dijkstra xác định 3 thông tin: kv, dv và pv.

kv: mang giá trị boolean xác định trạng thái được chọn của đỉnh v. Ban đầu ta khởi tạo tất cả các đỉnh v chưa được chọn, nghĩa là: kv = false, v V.

dv: là chiều dài đường đi mà ta tìm thấy cho đến thời điểm đang xét từ a đến v. Khởi tạo, dv = , v V \{a}, da = 0.

pv: là đỉnh trước của đỉnh v trên đường đi ngắn nhất từ a đến b. Đường đi ngắn nhất từ a đến b có dạng {a,...,pv,v,...,b}. Khởi tạo, pv = null, v V.

Các bước giải thuật:

Bước 1: Khởi tạo: Đặt kv:=false v V; dv:= ,v V \ {a}, da:=0. Bước 2: Chọn vV sao cho kv = false và dv = min {dt / t V, kt = false}. Nếu dv=∞ thì kết thúc, không tồn tại đường đi từ a đến b.

39

Bước 3: Đánh dấu đỉnh v, kv=true.

Bước 4: Nếu v=b thì kết thúc và db là độ dài đường đi ngắn nhất từ a đến b. Ngược lại quay lại bước 5.

Bước 5: Với mỗi đỉnh u kề với v mà ku=false, kiểm tra: Nếu du > dv + w(v,u) thì du:= dv + w(v,u). Ghi nhớ đỉnh v: pu:= v. Quay lại bước 2.

d. Thuật toán

Thuật toán Dijkstra có thể được mô tả qua thủ tục sau:

Đầu vào: Đồ thị có hướng G=(V,E) với n đỉnh, sV là đỉnh xuất phát, a[u,v] V, ma trận trọng số. Giả thiết a[u,v]>=0, u,vV.

Đầu ra: Khoảng cách từ đỉnh s đến tất cả các đỉnh còn lại d[v], vV.

Procedure Dijkstra; Begin For vV do Begin d[v]:=a[s,v] ; truoc [v]:=s; end; d[s]:=0; T:=V\{s}; while T≠Ø do begin Tìm đỉnh uT thỏa mãn d[u]=min {d[z], zT} T:=T\{u}; For v T do

If d[v]>d[u] + a[u,v] then

Begin d[v]:=d[u] + a[u,v]; Truoc[v]:=u; end; end; end;

40 Độ phức tạp của thuật toán Dijkstra:

- Ở mỗi bước lặp để tìm ra điểm u cần thực hiện O(n) phép toán, để gán

nhãn lại cũng cần thực hiện môt số lượng phép toán cũng là O(n). Thuật toán cần thực hiện n-1 bước lặp. Vậy độ phức tạp của thuật toán là O(n2).

41

CHƯƠNG 3: XÂY DỰNG ỨNG DỤNG GAME PIKACHU

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

Game Pikachu là game có nguồn gốc từ Nhật Bản, gồm các hình giống nhau được xếp trên một hình chữ nhật gồm các ô vuông bằng nhau xếp gần nhau. Nhiệm vụ của người chơi là phải tìm được các hình giống nhau trong hình chữ nhật đó.

Game Pikachu là một game mô phỏng đơn giản, cách chơi khá dễ dàng. Game được coi là một trong những game giải trí, thư giãn. Hiện game Pikachu đã có mặt trên rất nhiều các thiết bị di động, từ những điện thoại java tới những điện thoại chạy hệ điều hành Android, Windows Phone, ios... Đã có rất nhiều các phiên bản game Pikachu mô phỏng và được cải tiến, thay các nhân vật trong game.

Game Pikachu là một trò đòi hỏi trí tuệ con người, sự nhanh mắt, nhanh tay và sự chuẩn xác với việc sử dụng chuột máy tính để tìm ra các ô vuông có kí hiệu trên nó giống nhau và làm cho các ô vuông đó ẩn đi. Người chơi sẽ chiến thắng khi nối được tất cả các hình với nhau. Càng lên cao thì độ khó của trò chơi sẽ càng tăng vì vậy đòi hỏi người chơi phải thật nhanh tay và nhanh mắt mới dành được điểm số cao.

Mục đích của game Pikachu là tìm ra đường đi ngắn nhất giữa hai hình giống nhau bằng tối đa ba đường thẳng, tìm hết được trước thời gian quy định là thắng.

3.2. Cấu trúc game Pikachu Luật chơi: Luật chơi:

Game mô phỏng game Pikachu, nhiệm vụ của người chơi là chọn hai hình giống nhau, đường đi rẽ ít nhất hai lần và được nối với nhau bởi tối đa ba đoạn thẳng.

42

- Người chơi phải ẩn hết hình như trong thời gian quy định, nếu không thì người chơi sẽ thua.

Cấu trúc game Pikachu

- Giao diện game sẽ là những ô vuông có tất cả 16x9 ô vuông. Ma

trận 16x9 là để hiển thị trên màn hình, ma trận trong game là 18x11 ô. - Chọn 26 giá trị cho các ô vuông đó

Như vậy để cho game hiển thị những ô vuông nào trên màn hình thì sử dụng mảng hai chiều 18x11 để lưu giá trị từng ô.

Trong game mỗi loại hình xuất hiện ít nhất bốn lần và số lần xuất hiện phải là chẵn. Như vậy mỗi giái trị sẽ xuất hiện trong ma trận 18x11 và ô nào trống sẽ có giá trị là -1. -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 4 13 25 20 14 14 15 19 19 16 24 14 6 11 1 13 -1 -1 16 21 7 8 17 14 11 14 2 6 10 15 9 8 18 2 -1 -1 17 13 10 6 18 8 4 21 6 25 10 18 7 17 5 19 -1 -1 16 9 25 13 15 12 7 20 8 5 14 10 21 10 21 16 -1 -1 24 13 18 25 9 24 13 3 11 8 10 11 20 18 3 24 -1 -1 12 11 3 9 20 5 12 22 11 8 2 2 15 1 4 5 -1 -1 3 20 18 7 7 26 15 12 12 20 12 19 5 9 22 17 -1 -1 1 5 15 4 16 3 31 7 26 17 17 19 1 26 3 1 -1 -1 22 9 4 23 22 19 1 6 23 4 26 2 2 23 6 23 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1

Hình 3.1: Ma trận biểu diễn giá trị của các ô trong game

43 3.3. Cách xây dựng game Pikachu 3.3.1. Cơ sở lý thuyết

Các đường đi trong game Pikachu:

A A A B B B A A A A B B B B A A A A B B B B

Hình 3.2: Một số đường đi trong game Pikachu

Ý tưởng: Xuất phát từ 2 điểm A và B. Làm thế nào để nối A với B bằng tối đa là 3 đường thẳng?

Từ điểm A duyệt đi tất cả các hướng có thể đi (các ô trống xung quanh).Tại B ta cũng duyệt các hướng tương tự A.

Một phần của tài liệu Một số thuật toán tìm đường đi ngắn nhất và xây dựng ứng dụng GAME PIKACHU (Trang 32)