Tôi muốn hiểu ở cấp độ cơ bản theo cách mà hoạt động tìm đường A * hoạt động. Bất kỳ mã hoặc triển khai mã psuedo cũng như trực quan hóa sẽ hữu ích.
Tôi muốn hiểu ở cấp độ cơ bản theo cách mà hoạt động tìm đường A * hoạt động. Bất kỳ mã hoặc triển khai mã psuedo cũng như trực quan hóa sẽ hữu ích.
Câu trả lời:
Có hàng tấn ví dụ mã và giải thích về A * được tìm thấy trực tuyến. Câu hỏi này cũng đã nhận được rất nhiều câu trả lời tuyệt vời với rất nhiều liên kết hữu ích. Trong câu trả lời của tôi, tôi sẽ cố gắng cung cấp một ví dụ minh họa về thuật toán, có thể dễ hiểu hơn mã hoặc mô tả.
Để hiểu A *, tôi khuyên bạn trước tiên hãy xem thuật toán của Dijkstra . Hãy để tôi hướng dẫn bạn qua các bước thuật toán của Dijkstra sẽ thực hiện cho tìm kiếm.
Nút bắt đầu của chúng tôi là A
và chúng tôi muốn tìm đường dẫn ngắn nhất tới F
. Mỗi cạnh của biểu đồ có chi phí di chuyển được liên kết với nó (ký hiệu là các chữ số màu đen bên cạnh các cạnh). Mục tiêu của chúng tôi là đánh giá chi phí di chuyển tối thiểu cho mỗi đỉnh (hoặc nút) của biểu đồ cho đến khi chúng tôi đạt được nút mục tiêu.
Đây là điểm khởi đầu của chúng tôi. Chúng tôi có một nút danh sách để kiểm tra, danh sách này hiện tại là:
{ A(0) }
A
có chi phí 0
, tất cả các nút khác được đặt thành vô cùng (trong một triển khai điển hình, đây sẽ là một cái gì đó int.MAX_VALUE
tương tự hoặc tương tự).
Chúng tôi lấy nút có chi phí thấp nhất từ danh sách các nút của chúng tôi (vì danh sách của chúng tôi chỉ chứa A
, đây là ứng cử viên của chúng tôi) và truy cập tất cả các hàng xóm của nó. Chúng tôi đặt chi phí của mỗi người hàng xóm là:
Cost_of_Edge + Cost_of_previous_Node
và theo dõi nút trước đó (hiển thị dưới dạng chữ nhỏ màu hồng bên dưới nút). A
bây giờ có thể được đánh dấu là đã giải quyết (màu đỏ) để chúng ta không truy cập lại. Danh sách các ứng cử viên của chúng tôi bây giờ trông như thế này:
{ B(2), D(3), C(4) }
Một lần nữa, chúng tôi lấy nút có chi phí thấp nhất từ danh sách của chúng tôi ( B
) và đánh giá các nút lân cận. Đường dẫn đến D
đắt hơn chi phí hiện tại D
, do đó đường dẫn này có thể bị loại bỏ. E
sẽ được thêm vào danh sách ứng cử viên của chúng tôi, hiện tại trông như thế này:
{ D(3), C(4), E(4) }
Nút tiếp theo để kiểm tra là bây giờ D
. Kết nối C
có thể bị loại bỏ, vì đường dẫn không ngắn hơn chi phí hiện có. Chúng tôi đã tìm thấy một con đường ngắn hơn E
mặc dù, do đó chi phí cho E
và nút trước đó của nó sẽ được cập nhật. Danh sách của chúng tôi bây giờ trông như thế này:
{ E(3), C(4) }
Vì vậy, như chúng tôi đã làm trước đây, chúng tôi kiểm tra nút với chi phí thấp nhất từ danh sách của chúng tôi, bây giờ E
. E
chỉ có một hàng xóm chưa được giải quyết, đó cũng là nút đích. Chi phí để đạt được nút đích được đặt thành 10
và nút trước đó của nó thành E
. Danh sách các ứng cử viên của chúng tôi bây giờ trông như thế này:
{ C(4), F(10) }
Tiếp theo chúng tôi kiểm tra C
. Chúng tôi có thể cập nhật chi phí và nút trước đó cho F
. Vì danh sách của chúng tôi hiện có F
nút với chi phí thấp nhất, chúng tôi đã hoàn thành. Đường dẫn của chúng ta có thể được xây dựng bằng cách quay lại các nút ngắn nhất trước đó.
Vì vậy, bạn có thể tự hỏi tại sao tôi giải thích Dijkstra cho bạn thay vì thuật toán A * ? Chà, sự khác biệt duy nhất là cách bạn cân nhắc (hoặc sắp xếp) các ứng cử viên của bạn. Với Dijkstra, đó là:
Cost_of_Edge + Cost_of_previous_Node
Với A * đó là:
Cost_of_Edge + Cost_of_previous_Node + Estimated_Cost_to_reach_Target_from(Node)
Trường hợp Estimated_Cost_to_reach_Target_from
thường được gọi là một chức năng Heuristic . Đây là một hàm sẽ cố gắng ước tính chi phí để đạt được nút đích. Một chức năng heuristic tốt sẽ đạt được rằng ít nút hơn sẽ phải được truy cập để tìm mục tiêu. Trong khi thuật toán của Dijkstra sẽ mở rộng ra tất cả các phía, A * sẽ (nhờ tìm kiếm heuristic) theo hướng của mục tiêu.
Trang của Amit về heuristic có một cái nhìn tổng quan tốt về các heuristic thông thường.
Tìm kiếm đường dẫn * là tìm kiếm loại đầu tiên tốt nhất sử dụng phương pháp phỏng đoán bổ sung.
Điều đầu tiên bạn cần làm là phân chia khu vực tìm kiếm của bạn. Đối với lời giải thích này, bản đồ là một lưới ô vuông, bởi vì hầu hết các trò chơi 2D sử dụng lưới ô vuông và vì đơn giản để hình dung. Tuy nhiên, xin lưu ý rằng khu vực tìm kiếm có thể được chia nhỏ theo bất kỳ cách nào bạn muốn: có thể là lưới hex hoặc thậm chí là các hình dạng tùy ý như Rủi ro. Các vị trí bản đồ khác nhau được gọi là "các nút" và thuật toán này sẽ hoạt động bất cứ khi nào bạn có một loạt các nút để đi qua và có các kết nối được xác định giữa các nút.
Dù sao, bắt đầu từ một gạch bắt đầu nhất định:
8 ô xung quanh ô bắt đầu được "tính điểm" dựa trên a) chi phí di chuyển từ ô hiện tại sang ô tiếp theo (thường là 1 cho chuyển động ngang hoặc dọc, sqrt (2) cho chuyển động chéo).
Mỗi ô sau đó được gán một điểm "heuristic" bổ sung - một xấp xỉ giá trị tương đối của việc di chuyển đến mỗi ô. Các phương pháp phỏng đoán khác nhau được sử dụng, đơn giản nhất là khoảng cách đường thẳng giữa tâm của gạch đã cho và gạch kết thúc.
Ngói hiện tại sau đó được "đóng" và tác nhân di chuyển đến ô lân cận đang mở, có điểm di chuyển thấp nhất và điểm heuristic thấp nhất.
Quá trình này được lặp lại cho đến khi đạt được nút mục tiêu hoặc không còn nút mở nào nữa (nghĩa là tác nhân bị chặn).
Để biết sơ đồ minh họa các bước này, hãy tham khảo hướng dẫn tốt cho người mới bắt đầu này .
Có một số cải tiến có thể được thực hiện, chủ yếu là cải thiện heuristic:
Có tính đến sự khác biệt địa hình, độ gồ ghề, độ dốc, vv
Đôi khi cũng rất hữu ích khi thực hiện "quét" trên lưới để chặn các khu vực trên bản đồ không phải là đường dẫn hiệu quả: ví dụ hình chữ U đối diện với tác nhân. Nếu không có thử nghiệm quét, đầu tiên, nhân viên sẽ vào U, quay lại, sau đó rời đi và đi vòng quanh rìa của U. Một tác nhân thông minh "thực sự" sẽ lưu ý bẫy hình chữ U và chỉ cần tránh nó. Quét có thể giúp mô phỏng điều này.
Nó còn lâu mới tốt nhất, nhưng đây là một triển khai tôi đã làm về A * trong C ++ vài năm trước.
Có lẽ tốt hơn là tôi chỉ cho bạn các tài nguyên hơn là cố gắng giải thích toàn bộ thuật toán. Ngoài ra, khi bạn đọc qua bài viết wiki, hãy chơi với bản demo và xem liệu bạn có thể hình dung được nó đang hoạt động như thế nào không. Để lại một bình luận nếu bạn có một câu hỏi cụ thể.
Bạn có thể thấy bài viết của ActiveTut về Tìm kiếm đường dẫn hữu ích. Nó vượt qua cả Thuật toán của A * và Dijkstra và sự khác biệt giữa chúng. Nó hướng đến các nhà phát triển Flash, nhưng nó sẽ cung cấp một số hiểu biết tốt về lý thuyết ngay cả khi bạn không sử dụng Flash.
Một điều quan trọng cần hình dung khi giao dịch với Thuật toán của A * và Dijkstra là A * được định hướng; nó cố gắng tìm con đường ngắn nhất đến một điểm cụ thể bằng cách "đoán" hướng nhìn nào. Thuật toán của Dijkstra tìm thấy con đường ngắn nhất đến / every / point.
Vì vậy, giống như một tuyên bố đầu tiên, A * là một thuật toán khám phá đồ thị. Thông thường trong các trò chơi, chúng tôi sử dụng gạch hoặc hình học thế giới khác làm biểu đồ, nhưng bạn có thể sử dụng A * cho những thứ khác. Hai thuật toán ur cho truyền tải đồ thị là tìm kiếm theo chiều sâu và tìm kiếm theo chiều rộng. Trong DFS, bạn luôn luôn khám phá đầy đủ chi nhánh hiện tại của mình trước khi nhìn vào anh chị em của nút hiện tại và trong BFS, bạn luôn luôn nhìn vào anh chị em trước rồi đến trẻ em. A * cố gắng tìm một điểm giữa giữa những nơi bạn khám phá một nhánh (giống như DFS) khi bạn đang tiến gần đến mục tiêu mong muốn nhưng đôi khi dừng lại và thử anh chị em nếu nó có kết quả tốt hơn ở nhánh đó. Toán học thực tế là bạn giữ một danh sách các nút có thể để khám phá tiếp theo nơi mỗi nút có "lòng tốt" điểm số cho thấy mức độ gần gũi (theo một cách hiểu trừu tượng nào đó) với mục tiêu, điểm thấp hơn sẽ tốt hơn (0 có nghĩa là bạn đã tìm thấy mục tiêu). Bạn chọn sử dụng tiếp theo bằng cách tìm mức tối thiểu của điểm cộng với số nút cách xa gốc (thường là cấu hình hiện tại hoặc vị trí hiện tại trong tìm đường). Mỗi lần bạn khám phá một nút, bạn thêm tất cả các con của nó vào danh sách này và sau đó chọn nút mới tốt nhất.
Ở mức độ trừu tượng, A * hoạt động như thế này: