Thuật toán A* được mô tả lần đầu tiên 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, do đó mà có tên A*.
Thuật toán 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
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).
a- Ý tưởng trực quan
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 mất nhiều thời gian.
b- Mô tả thuật toán
Thuật toán A* dựa trên thuật toá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. 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:
- s = đỉnh xuất phát. - t = đỉnh kết thúc.
- close = tập các đỉnh đã được tính toán chính xác đường đi ngắn nhất. - open = tập các đỉnh còn lại.
- s(i,j) = trọng số của cung (i,j).
- d(i) = khoảng cách nhỏ nhất từ i đến s.
- v(i) = khoảng cách min ước lượng từ i đến s.
Khi đó, nhiệm vụ đầu tiên của các thuật toán tìm đường đi ngắn nhất là phải tìm được giá trị d(t).
d(i) = + ∞ với ∀i ∈ [1..n] close = [s]
open = [1..n] – [s] k = s
repeat
{sửa đổi ước lượng min} Với ∀i ∈ open
d(i) = min {d(i), d(k) + a(k, i)} {mở rộng tập close}
Chọn k ∈ open để ∀i ∈ open có (d(i) + v(i)) ≤ (d(k) + v(k))
open = open – [k] close = close + [k] Until (t ∈ close) ;
c- Thuật toán A* sử dụng đánh giá hàm heuristic
Đánh giá hàm heuristic có thể dùng để kiểm soát trạng thái của thuật toán A*:
Ở một mức độ nào đó, nếu hàm h(n) = 0 thì f(n) = g(n), thuật toán A* bây giờ là thuật toán Dijkstra, vẫn đảm bảo tìm kiếm đường đi ngắn nhất.
Nếu h(n) ≤ chi phí di chuyển từ n đến đích, A* đảm bảo tìm kiếm đường
đi ngắn nhất; h(n) càng nhỏ, thuật toán A* càng được mở rộng, tạo ra chi phí nhỏ hơn.
Nếu h(n) = chi phí di chuyển đến đích (điểm mục tiêu) A* sẽ là đường đi ngắn nhất và không bao giờ mở rộng hơn nữa, thời gian nhanh hơn. Mặc dù không thể thực hiện điều này trong tất cả các trường hợp, nhưng có thể tạo sự
chính xác trong một vài trường hợp đặc biệt. Tốt cho việc biết được đầy đủ các thông tin khi đó A* đầy đủ hơn.
Nếu h(n) lớn hơn chi phí di chuyển từ n đến đích, A* không đảm bảo sẽ tìm
được đường đi ngắn nhất nhưng nó có thể mất ít thời gian hơn (chạy nhanh hơn). Trong một chừng mực nào đó, nếu h(n) quá lớn so với g(n), khi đó f(n) ≈
h(n), khi đó A* trở thành BFS (best - first search).
Chú ý: về mặt kỹ thuật mà nói, thuật toán A* có thể gọi đơn giản là thuật toán A nếu Heuristic là một đánh giá thấp hơn chi phí thực tế. Tuy nhiên, có thể
gọi A* bởi vì việc thực hiện hoàn toàn giống nhau và những chương trình game thì không phân biệt A hay A*.
ta có thể quyết định cái gì chúng ta muốn thoát khỏi A*. Chính xác hơn là tại một điểm xác định, chúng ta sẽ tìm được đường đi ngắn nhất rất nhanh. Nếu chúng ta quá chậm, sau đó tiếp tục tìm kiếm đường đi ngắn nhất, và nó sẽ nhanh hơn. Nếu nó quá lớn, chúng ta có thể bỏ qua đường đi ngắn nhất và A* sẽ nhanh hơn.
Trong trò chơi, đặc tính này của A* có thể rất hữu dụng. Ví dụ, chúng ta có thể tìm thấy trong một số tình huống, chúng ta sẽ có đường đi “good” hơn là
đường đi “path”. Để cân bằng giữa g(n) và h(n) chúng ta có thể thay đổi cả hai.
Tính nhanh chóng và độ chính xác:
Khả năng của A* khác nhau là dựa cơ bản vào đánh giá Heuristic và hàm chi phí có thể có ích trong một bài toán. Việc cân bằng giữa tính nhanh chóng và
độ chính xác có thể được xem là một kỳ công làm cho trò chơi nhanh hơn. Đối với hầu hết các games hay bài toán, chúng ta không thực sự cần thiết giữa 2
điểm có đường đi ngắn nhất. Chúng ta chỉ cần vài điều mang tính chất gần hơn. Tất cả những gì chúng ta cần chỉ có thể phụ thuộc vào những gì đang diễn ra trong trò chơi và làm thế nào để cho một máy tính có thể chạy nhanh hơn.
Việc chọn lựa giữa tính nhanh chóng và tính chính xác không thay đổi. Chúng ta có thể lựa chọn sự năng nổ dựa vài tốc độ CPU, sự phân chia thời gian tìm kiếm đường đi, số đơn vị trên bản đồ, đơn vị quan trọng, kích cỡ nhóm, độ
khó và một vài yếu tố khác. Cách mà có thể cân bằng chức năng là xây dựng hàm đánh giá heuristic mà nó có chi phí nhỏ nhất để di chuyển trên lưới không gian là 1 và tiếp đến là xây dựng hàm tỉ lệ:
g’(n) = 1 + alpha * (g(n) - 1) (2.2)
Nếu alpha = 0, chúng ta luôn luôn xác định được hàm chi phí. Trong trường hợp này, chi phí địa hình hoàn toàn bị bỏ qua và A* làm việc ở mức độ đơn giản với những lưới không gian (spatial grids). Nếu alpha = 1, hàm chi phí nguồn sẽđược sử dụng và chúng ta có thể đạt được lợi ích tối thiểu từ A*.
Chúng ta cũng có thể xem xét việc chuyển đổi từ đánh giá heuristic trả về
chi phí vận chuyển nhỏ nhất tuyệt đối về chi phí vận chuyển nhỏ nhất mong đợi. Ví dụ, nếu như hầu hết bản đồ của chúng ta là vùng cỏ với chi phí vận chuyển là 2 nhưng một số nơi là những con đường với chi phí vận chuyển là 1, khi đó chúng ta có thể xem xét đánh giá heuristic như là không có con đường nào cả và trả về trị khoảng cách 2*.
Scale:
Để cộng 2 giá trị này, yêu cầu g(n) và h(n) phải có cùng “tỉ lệ” (scale). Ví dụ: g(n) được tính theo đơn vị thời gian (chẳng hạn giờ), h(n) tính theo đơn vị
mét, thì A* sẽ được tính bằng cách xem xét g hay h cái nào “lớn” hơn, cái nào “nhỏ” hơn để xác định.
d- Tính chất
* Cũng như tìm kiếm theo chiều rộng BFS, A* là thuật toán NPC (NP- 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) (2.4)
* Thuật toán A* còn có tính chất hiệu quả một cách tối ưu với mọi hàm heuristic h, có nghĩa là không có thuật toán nào cũng sử dụng hàm heuristic đó mà chỉ cần 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
đó h 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)
* 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ằng hàm h(n) = 0 với mọi n.
e- Hàm Heuristic
Là hàm ứng với mỗi trạng thái hay mỗi sự chọn lựa một giá trị có ý nghĩa
đối với vấn đềđể dựa vào giá trị hàm này ta chọn lựa hành động.
• Các nguyên lý của hàm heuristic
¾ Vét cạn thông minh:
Cách hành động tương tự như vét cạn để tìm kiếm trong một không gian lớn nhưng có sự hạn chế vùng không gian tìm kiếm và có sự định hướng để nhanh chóng tìm đến mục tiêu.
- Tạo miền D’ rất nhỏ so với D. - Vét cạn trên D’.
¾ Nguyên lý thứ tự
Trong quá trình hành động để thực hiện việc chọn lọc các cách làm các trạng thái ta có thể dựa trên một thứ tự hợp lý để giải pháp
đạt tính hiệu quả cao. ¾ Nguyên lý tham lam
Trong nhiều vấn đề cần phải đạt đến một mục tiêu tối ưu, toàn cục mà không nhìn thấy được toàn bộ quá trình hành động. Hơn nữa, trong từng bước ta phải lựa chọn hành động dựa trên những thông tin cục bộ. Khi đó, trong từng bước của quá trình hành động người ta dựa trên mục tiêu tối ưu toàn cục để định ra mục tiêu cục bộ và dựa theo đó chọn lựa hành động.
• Heuristic chính xác
Nếu hàm heuristic đúng bằng khoảng cách đường đi tối ưu, A* sẽ mở
rộng thêm vài nút nữa, khi đó, nếu h(n) hợp với g(n) giá trị của hàm f không thay đổi dọc theo đường đi. Tất cảc các nút trên đường đi sai sẽ có giá trị f cao hơn so với các nút trên đường đi đúng (tối ưu). A* sẽ không được xem xét tại những nút làm cho giá trị f lớn cho đến khi xét đến những nút làm cho f có giá trị nhỏ vá khi đó luôn luôn có đường đi ngắn nhất.
- Tính lại hàm heuristic chính xác: để xây dựng hàm heuristic chính xác, ta tính lại đường đi ngắn nhất giữa các cặp điểm. Điều này thì không khả thi cho hầu hết các bản đồ game. Tuy nhiên, có nhiều cách để đánh giá gần
đúng cho hàm heuristic này:
• Kết hợp một lưới thô trên đỉnh một lưới mịn. Tính lại đường đi ngắn nhất giữa mỗi cặp lưới.
• Tính lại đường đi ngắn nhất giữa các cặp điểm waypoints.
- Việc thêm hàm heuristic h’ để đánh giá chi phí di chuyển từ bất kỳ điểm liên quan nào đến waypoints. Cái sau cùng cũng có thểđược tính lại nếu chúng ta mong muốn sẽđạt được cái tốt hơn. Hàm heuristic trong trường hợp này sẽ là:
h(n) = h’(s, w1) + distance (w1, w2), h’(w2, t) (2.5)
Với: w1, w2 là các điểm waypoints; s: điểm bắt đầu; t: điểm đích
f- Đánh giá thuật toán
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) – h*(x)⎜≤ O(log h*(x)) (2.6)
Trong đó h* 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 logarit của "heuristic hoàn hảo" h * - hàm trả về khoảng cách thực từ x tới đích [10].
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*).