Thuật toán tìm kiếm A*

Một phần của tài liệu Các thuật toán tìm đường đi ngắn nhất trong đồ thị lý thuyết, thuật toán và ứng dụng (Trang 40 - 51)

Xét bài toán tìm đường - bài toán mà A* thường được dùng để giải. A* xây dựng tăng dần tất cả các tuyến đường từ điểm xuất phát cho tới khi nó tìm thấy một đường đi chạm tới đích. Tuy nhiên, cũng như tất cả các thuật toán tìm kiếm có thông tin, nó chỉ xây dựng các tuyến đường "có vẻ" dẫn về phía đích. Để biết những tuyến đường nào có khả năng sẽ dẫn tới đích, A* sử dụng một "đánh giá heuristic" về khoảng cách từ điểm bất kỳ cho trước tới đích. Trong trường hợp tìm đường đi, đánh giá này có thể là khoảng cách đường chim bay - một đánh giá xấp xỉ thường dùng cho khoảng cách của đường giao thông. Điểm khác biệt của A* đối với tìm kiếm theo lựa chọn tốt nhất là nó còn tính đến khoảng cách đã đi qua. Điều đó làm cho A* "đầy đủ" và "tối ưu", nghĩa là, A* sẽ luôn luôn tìm thấy đường đi ngắn nhất nếu tồn tại một đường đi như thế. A* không đảm bảo sẽ chạy nhanh hơn các thuật toán tìm kiếm đơn giản hơn. Trong một môi trường dạng mê cung, cách duy nhất để đến đích có thể là trước hết phải đi về phía xa đích và cuối cùng mới quay lại. Trong trường hợp đó, việc thử các nút theo thứ tự "gần đích hơn thì được thử trước" có thể gây tốn thời gian.

Mô tả thuật toán:

Mô phỏng thuật toán tìm kiếm A* ứng dụng trong tìm đường đi từ một nút bắt đầu đến một nút mục tiêu trong chuyển động robot. Các vòng rỗng đại diện cho các nút trong tập mở, nghĩa là những nút đợi và những vòng tròn tô màu là thuộc tập đóng. Màu trên mỗi nút tô màu tượng trưng cho khoảng cách từ nút đó đến điểm xuất phát: càng xanh thì càng xa. Đầu tiên ta có thể thấy A* di chuyển theo hướng thẳng đến mục tiêu, sau khi gặp vật cản, nó sẽ tìm các đường thay thế từ các nút chờ trong tập mở.

Số hóa bởi Trung tâm Học liệu - ĐHTN http://www.lrc-tnu.edu.vn/

A* lưu giữ một tập các lời giải chưa hoàn chỉnh, nghĩa là các đường đi qua đồ thị, bắt đầu từ nút xuất phát. Tập lời giải này được lưu trong một hàng đợi ưu tiên (priority queue). Thứ tự ưu tiên gán cho một đường đi x được quyết định bởi hàm

f(x) = g(x) + h(x).

Trong đó, g(x) là chi phí của đường đi cho đến thời điểm hiện tại, nghĩa là tổng trọng số của các cạnh đã đi qua. h(x) là hàm đánh giá heuristic về chi phí nhỏ nhất để đến đích từ x. Ví dụ, nếu "chi phí" được tính là khoảng cách đã đi qua, khoảng cách đường chim bay giữa hai điểm trên một bản đồ là một đánh giá heuristic cho khoảng cách còn phải đi tiếp.

Hàm f(x) có giá trị càng thấp thì độ ưu tiên của x càng cao (do đó có thể sử dụng một cấu trúc heap tối thiểu để cài đặt hàng đợi ưu tiên này)

function A*(điểm_xuất_phát,đích)

var đóng:= tập rỗng

var q:= tạo_hàng_đợi(tạo_đường_đi(điểm_xuất_phát))

while q không phải tập rỗng

var p:= lấy_phần_tử_đầu_tiên(q)

var x:= nút cuối cùng của p

if x in đóng continue if x = đích return p bổ sung x vào tập đóng foreach y in các_đường_đi_tiếp_theo(p) đưa_vào_hàng_đợi(q, y) return failure

Trong đó, các_đường_đi_tiếp_theo(p) trả về tập hợp các đường đi tạo bởi việc kéo dài p thêm một nút kề cạnh. Giả thiết rằng hàng đợi được sắp xếp tự động bởi giá trị của hàm f.

"Tập hợp đóng" (đóng) lưu giữ tất cả các nút cuối cùng của p (các nút mà các đường đi mới đã được mở rộng tại đó) để tránh việc lặp lại các chu trình (việc này

Số hóa bởi Trung tâm Học liệu - ĐHTN http://www.lrc-tnu.edu.vn/

cho ra thuật toán tìm kiếm theo đồ thị). Đôi khi hàng đợi được gọi một cách tương ứng là "tập mở". Tập đóng có thể được bỏ qua (ta thu được thuật toán tìm kiếm theo cây) nếu ta đảm bảo được rằng tồn tại một lời giải hoặc nếu hàm các_đường_đi_tiếp_theo được chỉnh để loại bỏ các chu trình.

Các tính chất

Cũng như tìm kiếm theo chiều rộng (breadth-first search), A* là thuật toán đầy đủ (complete) theo nghĩa rằng nó sẽ luôn luôn tìm thấy một lời giải nếu bài toán có lời giải.

Nếu hàm heuristic h có tính chất thu nạp được (admissible), nghĩa là nó không bao giờ đánh giá cao hơn chi phí nhỏ nhất thực sự của việc đi tới đích, thì bản thân A* có tính chất thu nạp được (hay tối ưu) nếu sử dụng một tập đóng. Nếu không sử dụng tập đóng thì hàm h phải có tính chất đơn điệu (hay nhất quán) thì A* mới có tính chất tối ưu. Nghĩa là nó không bao giờ đánh giá chi phí đi từ một nút tới một nút kề nó cao hơn chi phí thực. Phát biểu một cách hình thức, với mọi nút x,y trong đó y là nút tiếp theo của x:

h(x) g(y) – g(x) + h(y)

Quan hệ với tìm kiếm chi phí đều (uniform-cost search)

Thuật toán Dijkstra là một trường hợp đặc biệt của A* trong đó đánh giá heuristic là một hàm hằng h(x) = 0 với mọi x.

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

Độ phức tạp thời gian của A* phụ thuộc vào đánh giá heuristic. Trong trường hợp xấu nhất, số nút được mở rộng theo hàm mũ của độ dài lời giải, nhưng nó sẽ là hàm đa thức khi hàm heuristic h thỏa mãn điều kiện sau:

|h(x) - ℎ∗(x)| O(log ℎ∗(x))

trong đó ℎ∗là heuristic tối ưu, nghĩa là hàm cho kết quả là chi phí chính xác để đi từ

x tới đích. Nói cách khác, sai số của h không nên tăng nhanh hơn lôgarit của "heuristic hoàn hảo" ℎ∗ - hàm trả về khoảng cách thực từ x tới đích.Vấn đề sử dụng bộ nhớ của A* còn rắc rối hơn độ phức tạp thời gian. Trong trường hợp xấu nhất, A* phải ghi nhớ số lượng nút tăng theo hàm mũ. Một số biến thể của A* đã được

Số hóa bởi Trung tâm Học liệu - ĐHTN http://www.lrc-tnu.edu.vn/ (adsbygoogle = window.adsbygoogle || []).push({});

phát triển để đối phó với hiện tượng này, một trong số đó là A* lặp sâu dần (iterative deepening A*), A* bộ nhớ giới hạn (memory-bounded A* - MA*) và A* bộ nhớ giới hạn đơn giản (simplified memory bounded A*).

Chƣơng III : ĐƢỜNG ĐI NGẮN NHẤT GIỮA TẤT CẢ CÁC

CẶP ĐỈNH

Trong chương này, chúng ta sẽ xét bài toán tìm đường đi ngắn nhất giữa tất cả các cặp đỉnh trong một đồ thị. Bài toán này có thể được đặt ra khi xây dựng một bảng khoảng cách giữa tất cả các cặp thành phố cho một bản đồ các tuyến đường nào đó.

Cũng như trong chương II, chúng ta được cho một đồ thị định hướng có trọng số với một hàm trọng số ánh xạ các cạnh vào tập các số thực. Chúng ta muốn tìm, với mỗi cặp đỉnh , một đường đi ngắn nhất (trọng số nhỏ nhất) từ u đến v, trong đó trọng số của một đường đi là tổng các trọng số của các cạnh tạo thành nó. Thông thường chúng ta sẽ đưa ra kết quả dưới dạng bảng, phần tử ở dòng u, cột v sẽ là trọng số của một đường đi ngắn nhất từ u đến v.

Chúng ta có thể giải bài toán tìm đường đi ngắn nhất giữa tất cả các cặp đỉnh bằng cách chạy thuật toán tìm đường đi ngắn nhất từ một đỉnh lần, một lần cho mỗi đỉnh được xem như đỉnh nguồn.Nếu tất cả các cạnh là không âm, chúng ta có thể sử dụng thuật toán Dijkstra. Nếu chúng ta sử dụng cài đặt mảng tuyến tính các hàng đợi ưu tiên nhỏ nhất, thời gian chạy sẽ là . Cài đặt min- heap nhị phân của hàng đợi ưu tiên nhỏ nhất sẽ cho một thuật toán chạy trong thời gian là một sự cải tiến nếu như đồ thị thưa. Một lựa chọn khác là có thể cài đặt hàng đợi ưu tiên nhỏ nhất với một heap Fibonaci, cho ta một thuật toán chạy

trong thời gian ).

Nếu trong đồ thị có các cạnh trọng số âm, chúng ta không thể sử dụng thuật toán Dijkstra được nữa. Thay vào đó, chúng ta phải chạy thuật toán chậm hơn Bellman- Ford một lần cho mỗi đỉnh. Thời gian chạy sẽ là, trên một đồ thị dày sẽ là O(V4

). Trong chương này, chúng ta sẽ tìm hiểu làm thế nào có thể giải bài toán này tốt hơn.

) , (V E Gw:ER V v u,  | |V ) ( ) (V3 VE OV3 O   ) lg (V E V O E V V V O( 2lg 

Số hóa bởi Trung tâm Học liệu - ĐHTN http://www.lrc-tnu.edu.vn/

Chúng ta cũng sẽ nghiên cứu mối liên hệ giữa bài toán đường đi ngắn nhất giữa tất cả các cặp đỉnh với phép nhân ma trận và nghiên cứu cấu trúc đại số của nó.

Không giống thuật toán tìm đường đi ngắn nhất từ một đỉnh với giả thiết rằng ta biểu diễn đồ thị bằng danh sách kề, hầu hết các thuật toán trong chương này sử dụng biểu diễn bằng ma trận kề. (Thuật toán Johnson cho đồ thị thưa sử dụng các danh sách kề). Để cho tiện lợi, chúng ta giả sử rằng các đỉnh được đánh số 1, 2, ..., |V|, sao cho ma trận n n W biểu diễn trọng số của các cạnh của một đồ thị định hướng n đỉnh G = (V, E). Nghĩa là W = (wij), trong đó

Các cạnh trọng số âm được cho phép, nhưng lúc này chúng ta giả sử đồ thị vào không chứa các chu trình với trọng số âm.

Bảng ra của các thuật toán tìm đường đi ngắn nhất giữa tất cả các cặp đỉnh trong chương này được biểu diễn là một ma trận A cấp n trong đó chứa trọng số của một đường đi ngắn nhất từ đỉnh i đến đỉnh j. Nghĩa là nếu chúng ta kí hiệu (i,j) là trọng số đường đi ngắn nhất từ đỉnh i đến đỉnh j (như trong chương 2), thì

khi kết thúc thuật toán.

Để giải bài toán đường đi ngắn nhất giữa tất cả các cặp đỉnh trên một ma trận kề vào, chúng ta cần phải tính không chỉ các trọng số đường đi ngắn nhất mà còn phải tính cả ma trận đỉnh trước ⊓ = (𝜋𝑖𝑗), trong đó 𝜋𝑖𝑗 bằng NIL nếu i = j hoặc không có đường đi từ i đến j. Trong trường hợp ngược lại, 𝜋𝑖𝑗 là đỉnh trước của j trên một đường đi ngắn nhất nào đó từ i. Cũng giống như đồ thị con đỉnh trước G trong chương 2 là một cây đường đi ngắn nhất với một đỉnh nguồn cho trước, đồ thị con sinh bởi dòng thứ i của ma trận ⊓ cũng là cây đường đi ngắn nhất với gốc là i. Với mỗi đỉnh i  V, chúng ta định nghĩa đồ thị con đỉnh trước của G cho i G,i = (V,i, E,i). V,i = {j V: ij NIL} {i} ) (dij Ddij ) , ( dij i j  0

trọng số của cạnh định hướng (i,j)

ij w      Nếu ij và (i,j)  E Nếu i = j Nếu ij và (i,j)  E (3.1)

Số hóa bởi Trung tâm Học liệu - ĐHTN http://www.lrc-tnu.edu.vn/

E,i = {(ij,j): j V,i – {i}}

Nếu G,i là một cây đường đi ngắn nhất, thì thủ tục sau đây, là một phiên bản có sửa đổi đôi chút của thủ tục PRINT-PATH, in ra một đường đi ngắn nhất từ đỉnh i

đến đỉnh j.

PRINT-ALL-PAIRS-SHORTEST-PATH(, i, j) 1 if i = j

2 then in ra i 3 else ifij = NIL

4 then in ra “không có đường đi từ” i “đến” j

5 else PRINT-ALL-PAIRS-SHORTEST-PATH(, i,ij) 6 in ra i.

Đƣờng đi ngắn nhất và phép nhân ma trận

Trước khi đi vào chi tiết, chúng ta cần phải thiết lập một số quy ước cho biểu diễn ma trận kề. Thứ nhất, chúng ta giả sử rằng đồ thị vào G = (V, E) có n đỉnh, n = |V|. Thứ hai, chúng ta sẽ sử dụng quy ước kí hiệu ma trận bằng các chữ cái hoa, chẳng hạn như W, L, D và các phần tử của nó là các chữ cái thường với chỉ số dưới, chẳng hạn như wij, lij hoặc dij. Một vài ma trận sẽ có chỉ số trên trong ngoặc, chẳng hạn L(m) = ( ( )m (adsbygoogle = window.adsbygoogle || []).push({});

ij

l ), hoặc D(m) = ( ( )m ij

d ), để chỉ ra các quá trình lặp. Cuối cùng, cho trước một ma trận A cấp n, chúng ta sẽ giả sử rằng giá trị của n được lưu trữ trong rows[A].

Phần này giới thiệu một thuật toán quy hoạch động cho bài toán tìm đường đi ngắn nhất giữa tất cả các cặp đỉnh trên một đồ thị định hướng G = (V,E). Mỗi vòng lặp chính của thuật toán động sẽ gọi một thủ tục tương tự như thủ tục nhân ma trận, và thuật toán giống như một thuật toán lặp lại các phép nhân ma trận. Chúng ta sẽ bắt đầu bằng phát triển một thuật toán với thời gian chạy O(𝑉4) cho bài toán đường đi ngắn nhất giữa tất cả các cặp đỉnh và sau đó cải tiến nó thành một thuật toán với thời gian chạy O(𝑉3lgV).

Số hóa bởi Trung tâm Học liệu - ĐHTN http://www.lrc-tnu.edu.vn/

Cấu trúc của một đƣờng đi ngắn nhất

Chúng ta bắt đầu bằng cách đặc tả một nghiệm tối ưu. Với bài toán tìm đường đi ngắn nhất giữa tất cả các cặp đỉnh trên một đồ thị G = (V,E), chúng ta đã chứng minh (bổ đề 2.1) rằng mọi đường đi con của một đường đi ngắn nhất đều là đường đi ngắn nhất. Giả sử rằng đồ thị được biểu diễn bởi một ma trận kề W = (𝑤𝑖𝑗w). Xét một đường đi ngắn nhất p từ đỉnh i đến đỉnh j, và giả sử p chứa nhiều nhất m cạnh. Giả sử rằng đồ thị không có chu trình trọng số âm, m là hữu hạn. Nếu i = j, khi đó p có trọng số 0 và không có cạnh nào. Nếu các đỉnh i và j là phân biệt, thì chúng ta sẽ phân tích đường đi p thành  p' 

i k j, trong đó đường đi p’ bây giờ chứa ít nhất m – 1 cạnh. Theo bổ đề 2.1, p’ là một đường đi ngắn nhất từ i đến k và do

vậy 𝛿 𝑖, 𝑗 = 𝛿 𝑖, 𝑘 + 𝑤𝑘𝑗

Một nghiệm đệ quy cho bài toán tìm đƣờng đi ngắn nhất giữa tất cả các cặp đỉnh

Bây giờ, đặt (m)

ij

l là trọng số nhỏ nhất của một đường đi bất kì từ đỉnh i đến đỉnh

j chứa nhiều nhất m cạnh. Khi m = 0, có một đường đi ngắn nhất từ i đến j không chứa cạnh nào chỉ nếu i = j. Do vậy

(0) ij l = 0 𝑛ế𝑢 𝑖 = 𝑗 𝑛ế𝑢 𝑖 ≠ 𝑗 Với m ≥ 1, chúng ta tính (m) ij l là số nhỏ nhất trong các số: 𝑙𝑖𝑗(𝑚 −1) (trọng số của đường đi ngắn nhất từ i đến j chứa nhiều nhất m-1 cạnh) và trọng số nhỏ nhất của đường đi bất kì từ i đến j chứa nhiều nhất m cạnh thu được bằng cách duyệt qua tất cả đỉnh trước có thể k của j. Như vậy, chúng ta định nghĩa một cách đệ quy

(m) ij l = min ( 1) ( 1) 1 ( m ,min{ m } ) ij ik kj k n llw    = ( 1) 1 min{ikm kj} k nlw    (3.2)

Để có đẳng thức sau, chúng ta chú ý 𝑤𝑖𝑗 = 0 với mọi j.

Trọng số đường đi ngắn nhất thực sự (i,j) sẽ là gì? Nếu đồ thị không chứa chu trình với trọng số âm, với mọi cặp đỉnh ij mà thoả mãn (i,j)<, tồn tại một đường đi ngắn nhất từ i đến j là một đường đi đơn giản và chứa nhiều nhất n - 1

Số hóa bởi Trung tâm Học liệu - ĐHTN http://www.lrc-tnu.edu.vn/

số nhỏ hơn một đường đi ngắn nhất từ i đến j. Trọng số đường đi ngắn nhất do vậy được cho bởi

(i,j) = ( 1) ( ) ( 1)

...

n m n

ij ij ij

l  ll   (3.3)

Tính trọng số đƣờng đi ngắn nhất từ dƣới lên

Giả sử dữ liệu vào của chúng ta là W = (𝑤𝑖𝑗), bây giờ chúng ta tính một dãy các ma trận L(1), L(2), ..., L(n-1), trong đó với m = 1, 2, ..., n-1, chúng ta có

L(m) = (𝑙𝑖𝑗 𝑚 ).

Ma trận cuối cùng L(n-1) chứa trọng số đường đi ngắn nhất thực sự. Quan sát thấy rằng (𝑙𝑖𝑗 1 ) = wij với mọi đỉnh i, j V và do vậy 𝐿(1) = W.

Trung tâm của thuật toán là thủ tục sau đây, trong đó, cho trước một ma trận 𝐿(𝑚−1)và W, trả về ma trận 𝐿(𝑚). Nghĩa là, nó mở rộng đường đi ngắn nhất đã được tính cho đến thời điểm hiện tại bằng cách xét độ dài đường đi có thể dài thêm một cạnh. EXTEND-SHORTEST-PATHS(L, W) 1. n  rows[L] 2. đặt L’ = (l’ij) là một ma trận nn (adsbygoogle = window.adsbygoogle || []).push({});

Một phần của tài liệu Các thuật toán tìm đường đi ngắn nhất trong đồ thị lý thuyết, thuật toán và ứng dụng (Trang 40 - 51)