THUẬT TOÁN A* VÀ CÁC TÍNH CHẤT LIÊN QUAN 1.1 Tổng quan về các thuật toán tìm đường đi trên đồ thị Trong lý thuyết đồ thị, bài toán đường đi ngắn nhất nguồn đơn là bài toán tìm một đường
Trang 1BÁO CÁO MÔN HỌC THUẬT TOÁN VÀ PHƯƠNG PHÁP
GIẢI QUYẾT VẤN ĐỀ
ĐỀ TÀI :
TÌM HIỂU VÀ ỨNG DỤNG THUẬT TOÁN A* VÀ BÀI TOÁN
TÌM KIẾM ĐƯỜNG ĐI TRÊN ĐỒ THỊ
Trang 21.2 Tại sao phải cần đến thuật toán A*? 3
1.3 Tổng Quan thuật toán A* 3
1.4 Ý tưởng trực quan thuật toán A* 4
1.5 Mô tả thuật toán A* 5
1.6 Các tính chất thuật toán A* 6
1.7 Độ phức tạp thuật toán 7
1.8 So sánh thuật toán A* với các thuật toán khác 7
1.9 Tối ưu hóa thuật toán A* 11
II HIỆN THỰC THUẬT TOÁN A* 13
2.1 Áp dụng thuật toán A* đối với bài toán 8-puzzle 13
Mục tiêu, nội dung 13
Cài đặt và hướng dẫn sử dụng 13
Thử nghiệm 16
Kết luận 18
2.2 XÂY DỰNG CHƯƠNG TRÌNH TÌM KIẾM TRÊN ĐỒ THỊ (VIẾT BẰNG C#) 18
Mục tiêu, nội dung 18
Hướng dẫn sử dụng: 19
III KẾT LUẬN CHUNG 22
IV.TÀI LIỆU THAM KHẢO 23
Trang 3THUẬT TOÁN A* VÀ BÀI TOÁN TÌM KIẾM ĐƯỜNG ĐI TRÊN ĐỒ THỊ
I THUẬT TOÁN A* VÀ CÁC TÍNH CHẤT LIÊN QUAN
1.1 Tổng quan về các thuật toán tìm đường đi trên đồ thị
Trong lý thuyết đồ thị, bài toán đường đi ngắn nhất nguồn đơn là bài toán tìm
một đường đi giữa hai đỉnh sao cho tổng các trọng số của các cạnh tạo nên đường đi
đó là nhỏ nhất Định nghĩa một cách hình thức, cho trước một đồ thị có trọng số (nghĩa
là một tập đỉnh V, một tập cạnh E, và một hàm trong số có giá trị thực f : E → R), cho
trước một đỉnh v thuộc V, tìm một đường đi P từ v tới mỗi đỉnh v' thuộc V sao cho
là nhỏ nhất trong tất cả các đường nối từ v tới v' Bài toán đường đi ngắn
nhất giữa mọi cặp đỉnh là một bài toán tương tự, trong đó ta phải tìm các đường đi
ngắn nhất cho mọi cặp đỉnh v và v'
Trang 4Hình 2 Đường đi trong thực tế
Các thuật toán quan trọng nhất giải quyết bài toán này là:
Thuật toán Dijkstra — giải bài toán nguồn đơn nếu tất cả các trọng số đều không âm
Thuật toán này có thể tính toán tất cả các đường đi ngắn nhất từ một đỉnh xuất phát
cho trước s tới mọi đỉnh khác mà không làm tăng thời gian chạy
Thuật toán Bellman-Ford — giải bài toán nguồn đơn trong trường hợp trọng số có thể
có giá trị âm
Giải thuật tìm kiếm A* giải bài toán nguồn đơn sử dụng heuristics để tăng tốc độ tìm
kiếm
Thuật toán Floyd-Warshall — giải bài toán đường đi ngắn nhất cho mọi cặp đỉnh
Thuật toán Johnson — giải bài toán đường đi ngắn nhất cho mọi cặp đỉnh, có thể
nhanh hơn thuật toán Floyd-Warshall trên các đồ thị thưa
Lý thuyết nhiễu (Perturbation theory); tìm đường đi ngắn nhất địa phương (trong
trường hợp xấu nhất)
Trang 5Một bài toán có liên quan là bài toán người bán hàng - bài toán tìm đường đi ngắn nhất
đi qua tất cả các đỉnh, mỗi đỉnh đúng một lần, và trở về đỉnh xuất phát Đây là bài
toán NP-đầy đủ, do đó khó có khả năng tồn tại một cách giải hiệu quả
Trong tư duy của ngành mạng hay viễn thông, bài toán đường đi ngắn nhất đôi khi
được gọi là bài toán đường đi có độ trễ nhỏ nhất (min-delay path problem) và thường
được gắn với một bài toán đường đi rộng nhất (widest path problem) ví dụ đường đi
rộng nhất trong các đường đi ngắn nhất (độ trễ nhỏ nhất) hay đường đi ngắn nhất trong
các đường đi rộng nhất
1.2 Tại sao phải cần đến thuật toán A*?
Các thuật toán cổ điển như Dijkstra được xây dựng trên các mô hình thuần tuý lý
thuyết, trong đó đồ thị được xem là tập hợp các đỉnh và cung nối giữa các đỉnh đó,
ngoài ra, không có bất kì thông tin gì bổ sung cho bài toán Với mô hình lý thuyết
thuần tuý như vậy, trên một đồ thị có thể tồn tại những đặc điểm ít khi xảy ra trong
thực tế, chẳng hạn: có thể tồn tại một cung có trọng số lớn hơn tổng trọng số của tất cả
các cung còn lại trên đồ thị, hoặc một đồ thị chẳng có cung nào Quay lại với câu hỏi
của chúng ta “Tại sao phải cần đến A*?”, câu trả lời thật đơn giản: “để giải quyết các
bài toán tìm đường trong thực tế!”
1.3 Tổng Quan thuật toán A*
Trong khoa học máy tính, A* (đọc là A sao) là một thuật toán tìm kiếm trong đồ thị
Thuật toán này tìm một đường đi từ một nút khởi đầu tới một nút đích cho trước (hoặc
tới một nút thỏa mãn một điều kiện đích) Thuật toán này sử dụng một "đánh giá
heuristic" để xếp loại từng nút theo ước lượng về tuyến đường tốt nhất đi qua nút đó
Thuật toán này duyệt các nút theo thứ tự của đánh giá heuristic này Do đó, thuật toán
A* là một ví dụ của tìm kiếm theo lựa chọn tốt nhất (best-first search)
Thuật toán A* được mô tả lần đầu vào năm 1968 bởi Peter Hart, Nils Nilsson,
và Bertram Raphael Trong bài báo của họ, thuật toán được gọi là thuật toán A; khi sử
dụng thuật toán này với một đánh giá heuristic thích hợp sẽ thu được hoạt động tối ưu,
Trang 6Năm 1964, Nils Nilsson phát minh ra một phương pháp tiếp cận dựa trên khám phá để
tăng tốc độ của thuật toán Dijkstra Thuật toán này được gọi là A1 Năm 1967 Bertram
Raphael đã cải thiện đáng kể thuật toán này, nhưng không thể hiển thị tối ưu Ông gọi
thuật toán này là A2 Sau đó, trong năm 1968 Peter E Hart đã giới thiệu một đối số
chứng minh A2 là tối ưu khi sử dụng thuật toán này với một đánh giá heuristic thích
hợp sẽ thu được hoạt động tối ưu Chứng minh của ông về thuật toán cũng bao gồm
một phần cho thấy rằng các thuật toán A2 mới là thuật toán tốt nhất có thể được đưa ra
các điều kiện Do đó ông đặt tên cho thuật toán mới là A *(A sao, A-star)
1.4 Ý tưởng trực quan thuật toán A*
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 (informed tìm kiếm thuật toán), 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
1.5 Mô tả thuật toán A*
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
Trang 7queue) Thứ tự ưu tiên gán cho một đường đi đượ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ừ 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 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)
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
Trang 8"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 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
1.6 Các tính chất thuật toán A*
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 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 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 trong đó là nút tiếp theo
của :
A* còn có tính chất hiệu quả một cách tối ưu (optimally efficient) với mọi hàm
heuristic , có nghĩa là không có thuật toán nào cũng sử dụng hàm heuristic đó mà chỉ
phải mở rộng ít nút hơn A*, trừ khi có một số lời giải chưa đầy đủ mà tại đó dự đoán
chính xác chi phí của đường đi tối ưu
Quan hệ với tìm kiếm chi phí đều (uniform-cost search)[sửa | sửa mã nguồn]
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 với mọi
1.7 Độ phức tạp thuật toán
Trang 9Độ 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:
trong đó là heuristic tối ưu, nghĩa là hàm cho kết quả là chi phí chính xác để đi từ 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 (Russell và Norvig 2003, tr
101)
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 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*)
Một thuật toán tìm kiếm có thông tin khác cũng có tính chất tối ưu và đầy đủ nếu đánh
giá heuristic của nó là thu nạp được (admissible) Đó là tìm kiếm đệ quy theo lựa chọn
tốt nhất (recursive best-first search - RBFS)
1.8 So sánh thuật toán A* với các thuật toán khác
Thuật toán tìm đường Dijkstra đòi hỏi chi phí về thời gian là O(n2) cho việc tìm đường
đi giữa hai cặp đỉnh bất kì; thuật toán Floy-Bellman đòi hỏi chi phí O(n) nhưng lại bắt
buộc phải tìm đường đi giữa mọi cặp đỉnh trong đồ thị, dẫn đến tổng chi phí thời gian
của cả thuật toán là O(n3), như vậy những thuật toán này thích hợp với các đồ thị
không quá lớn (từ vài trăm tới không quá vài ngàn đỉnh) Trong khi đó, bài toán tìm
đường trong thực tế thường làm việc với đồ thị có vài chục ngàn đỉnh; bù lại, các bài
toán như vậy lại có thêm thông tin phụ giúp chúng ta định hướng tốt hơn trong quá
trình tìm lời giải Vấn đề ở đây là sử dụng các thông tin định hướng đó như thế nào Ví
dụ trên bài toán tìm đường, chúng ta cần tìm đường đi từ Hà Nội vào TP Hồ Chí
Minh, mọi người đều có xu hướng đi về phía Nam, vì TP Hồ Chí Minh ở phía Nam
Hà Nội, không mấy ai nghĩ đến chuyện đi lên Lạng Sơn rồi tìm đường về TP Hồ Chí
Trang 10Minh (theo cách tìm kiếm mù của thuật toán Dijkstra) Một bài toán khác, trong các trò
chơi chiến thuật thời gian thực, cần đi từ điểm A đến điểm B Ở đây không thể áp dụng
các thuật toán tìm đường thông thường vì bản đồ của trò chơi đôi khi có kích thước lên
đến 512 x 512 ô, tương đương với một đồ thị thưa có 262.144 đỉnh; nhóm quân thường
có xu hướng đi thẳng về hướng B và nếu gặp chướng ngại vật thì men theo chướng
ngại vật
Thông tin định hướng không nhất thiết chỉ là thông tin về “hướng” như hai ví dụ trên
Trong từng bài toán, thông tin này ở những hình thức khác nhau, chúng thay đổi muôn
hình muôn vẻ, bản thân việc sử dụng các thông tin này như thế nào để xây dựng hàm
lượng giá cũng là vấn đề lớn và thú vị
Như đã đề cập ở trên, A* là thuật toán dựa trên Dijkstra, vì vậy cũng như Dijkstra, tư
tưởng tìm đường của A* dựa trên chiến lược tìm kiếm theo chiều rộng Gần như có sự
tương đương 1-1 giữa các bước thực hiện của cả hai thuật toán Trước khi xem xét
thuật toán, ta quy ước cho bài toán tìm đường đi ngắn nhất trên đồ thị G:
Thuật toán Dijkstra
Trang 11Sự khác biệt duy nhất của hai thuật toán ở điểm chọn đỉnh k để mở rộng tập close
Trong chiến lược tìm kiếm theo chiều rộng, việc lựa chọn trạng thái để mở rộng tìm
kiếm đóng vai trò tối quan trọng Với một trạng thái được lựa chọn tốt, có thể tìm thấy
lời giải của bài toán chỉ sau số bước rất ít so với việc lựa chọn trạng thái mở rộng một
cách ngẫu nhiên (lựa chọn mù) Theo chứng minh lý thuyết, nếu các trọng số của đồ
thị G đều là dương (l[i,j] > 0 với mọi i¹j) và v[i] là lượng giá dương thấp hơn đường đi
ngắn nhất từ đỉnh i đến u0 (0 < v[i] < d[i] với mọi i), thì thuật toán A* luôn cho kết
quả đúng và không bao giờ yêu cầu nhiều thời gian hơn thuật toán Dijkstra Có hai kết
quả dễ thấy; thứ nhất, thuật toán Dijkstra có thể xem là trường hợp “suy biến” của A*
trong trường hợp v[i] = 0; thứ hai, nếu v[i] là ước lượng đúng thì thuật toán A* có độ
phức tạp thời gian là tuyến tính
Ta xem 2 ví dụ sau để biết được rằng việc tìm kiếm đường đi trong thuật toán A* dễ
dàng hơn nhiều so với thuật toán Dijsktra
Ví dụ 1: Tìm kiếm đường đi thông thường trong đồ thị
Hình 4.Tìm kiếm đường đi trong thuật toán Dijsktra
Trang 12Hình 5.Tìm kiếm đường đi trong thuật toán A*
Ví dụ 2: tìm kiếm đường đi trong đồ thị nếu có chướng ngại vật
Hình 6.Tìm kiếm đường đi nếu có chướng ngại vật trong thuật toán Dijsktra
Trang 13Hình 7.Tìm kiếm đường đi nếu có chướng ngại vật trong thuật toán A*
Rõ ràng ta thấy thuật toán A* có đường đi rất tối ưu và tiết kiệm chi phí tốt hơn nhiều
so với thuật toán Dijsktra
1.9 Tối ưu hóa thuật toán A*
1 Đơn giản hóa không gian tìm kiếm
- A: hình chữ nhật hoặc lưới lục giác
- B: Tầng Polygonal thực tế
- C: Tầng Polygonal Đại diện
- D: Địa điểm Tầm nhìn
Trang 142.phân cấp đường dẫn tìm kiếm
- Tìm mối liên hệ giữa các đường đi
- Tìm mối liên hệ chính trong mỗi đường đi
3 Đánh giá cao chi phí cho Heuristic
- Hãy nhớ rằng các chức năng?
PathCostEstimate (StartLoc, GoalLoc, Agent)
- Phương pháp thông thường:
(khoảng cách bản đồ thực tế) X (chi phí tối thiểu cho mỗi địa hình đơn vị)
- Theo so với hơn Ước tính: (Show Demo)
• đánh giá thấp - Kết quả cuối cùng đã chứng minh được tối ưu hóa
• Đánh giá quá cao - nhanh hơn tốc độ tiếp cận (Nếu không có ->không có điểm kết
thúc)
4 Sử dụng các cấu trúc dữ liệu
- Phân bố không gian cho các nút
- Các nút cha cho các điểm được xét đến
- Ưu tiên dùng Heap cho tập Open (Hãy nhớ rằng các hoạt động của tìm nút với f
thấp nhất (x) trong Open?)
- Cây nhị phân tìm kiếm, Cây cân bằng
Trang 15II HIỆN THỰC THUẬT TOÁN A*
Trong bài tiểu luận môn học “thuật toán và phương pháp giải quyết vấn đề”, em xin
đưa ra 2 hiện thực thuật toán A* như sau:
Áp dụng thuật toán A* đối với bài toán 8-puzzle
Xây dựng chương trình tìm kiếm trên đồ thị (viết bằng C#)
2.1 Áp dụng thuật toán A* đối với bài toán 8-puzzle
Mục tiêu, nội dung
Thuật toán được cài đặt để giải quyết các bài toán tìm kiếm mù bằng phương pháp
A*, cụ thể trong chương trình này là để giải quyết bài toán 8puzzle