Ai đó có thể cho tôi biết tại sao thuật toán của Dijkstra cho đường dẫn ngắn nhất nguồn đơn giả định rằng các cạnh phải không âm.
Tôi đang nói về chỉ cạnh chứ không phải chu kỳ trọng lượng âm.
Ai đó có thể cho tôi biết tại sao thuật toán của Dijkstra cho đường dẫn ngắn nhất nguồn đơn giả định rằng các cạnh phải không âm.
Tôi đang nói về chỉ cạnh chứ không phải chu kỳ trọng lượng âm.
Câu trả lời:
Hãy nhớ lại rằng trong thuật toán của Dijkstra, một khi một đỉnh được đánh dấu là "đóng" (và ngoài tập mở) - thuật toán tìm thấy đường đi ngắn nhất tới nó và sẽ không bao giờ phải phát triển nút này nữa - nó giả định đường dẫn được phát triển tới đây con đường là ngắn nhất.
Nhưng với trọng lượng tiêu cực - nó có thể không đúng. Ví dụ:
A
/ \
/ \
/ \
5 2
/ \
B--(-10)-->C
V={A,B,C} ; E = {(A,C,2), (A,B,5), (B,C,-10)}
Dijkstra từ A trước tiên sẽ phát triển C và sau đó sẽ không tìm thấy A->B->C
EDIT giải thích sâu hơn một chút:
Lưu ý rằng điều này rất quan trọng, bởi vì trong mỗi bước thư giãn, thuật toán giả định "chi phí" cho các nút "đóng" thực sự là tối thiểu, và do đó nút sẽ được chọn tiếp theo cũng là tối thiểu.
Ý tưởng của nó là: Nếu chúng ta có một đỉnh mở sao cho chi phí của nó là tối thiểu - bằng cách thêm bất kỳ số dương nào vào bất kỳ đỉnh nào - mức tối thiểu sẽ không bao giờ thay đổi.
Không có ràng buộc về số dương - giả định trên là không đúng.
Vì chúng tôi "biết" mỗi đỉnh "đóng" là tối thiểu - chúng tôi có thể thực hiện bước thư giãn một cách an toàn - mà không "nhìn lại". Nếu chúng ta cần "nhìn lại" - Bellman-Ford cung cấp giải pháp giống như đệ quy (DP) để làm như vậy.
A->B
sẽ 5 và A->C
sẽ 2. Sau đó B->C
sẽ -5
. Vì vậy, giá trị của C
sẽ -5
giống như bellman-ford. Làm thế nào là điều này không đưa ra câu trả lời đúng?
A
có giá trị bằng 0. Sau đó, nó sẽ nhìn vào nút có giá trị tối thiểu, B
là 5 và C
là 2. Tối thiểu là C
, vì vậy nó sẽ đóng C
với giá trị 2 và sẽ không bao giờ nhìn lại, khi nào sau đó B
được đóng lại, nó không thể sửa đổi giá trị của C
, vì nó đã được "đóng".
A -> B -> C
? Đầu tiên, nó sẽ cập nhật C
khoảng cách thành 2, và sau đó B
là khoảng cách 5. Giả sử trong biểu đồ của bạn không có cạnh đi ra C
, thì chúng ta không làm gì khi truy cập C
(và khoảng cách của nó vẫn là 2). Sau đó, chúng tôi truy cập D
các nút liền kề và nút liền kề duy nhất C
có khoảng cách mới là -5. Lưu ý rằng trong thuật toán của Dijkstra, chúng tôi cũng theo dõi cha mẹ mà chúng tôi tiếp cận (và cập nhật) nút và thực hiện từ đó C
, bạn sẽ nhận được cha mẹ B
, và sau đó A
, dẫn đến kết quả chính xác. Tôi đang thiếu gì?
Khi tôi đề cập đến thuật toán của Dijkstra, tôi sẽ nói về Thuật toán của Dijkstra như được triển khai dưới đây,
Vì vậy, bắt đầu các giá trị ( khoảng cách từ nguồn đến đỉnh ) ban đầu được gán cho mỗi đỉnh là,
Đầu tiên chúng ta trích xuất đỉnh trong Q = [A, B, C] có giá trị nhỏ nhất, tức là A, sau đó Q = [B, C] . Lưu ý A có cạnh được định hướng đến B và C, cả hai đều nằm trong Q, do đó chúng tôi cập nhật cả hai giá trị đó,
Bây giờ chúng tôi trích xuất C là (2 <5), bây giờ Q = [B] . Lưu ý rằng C được kết nối với không có gì, vì vậy line16
vòng lặp không chạy.
Cuối cùng chúng tôi trích xuất B, sau đó . Lưu ý B có cạnh được định hướng đến C nhưng C không có trong Q do đó chúng tôi lại không nhập vòng lặp for vào line16
,
Vì vậy, chúng tôi kết thúc với khoảng cách như
Lưu ý làm thế nào là sai vì khoảng cách ngắn nhất từ A đến C là 5 + -10 = -5, khi bạn đi .
Vì vậy, đối với biểu đồ này, Thuật toán của Dijkstra đã tính toán sai khoảng cách từ A đến C.
Điều này xảy ra bởi vì Dijkstra Thuật toán không cố gắng để tìm một con đường ngắn hơn để đỉnh được đã chiết xuất từ Q .
Những gì các line16
vòng lặp đang làm là lấy đỉnh u và nói "hey trông giống như chúng ta có thể đi đến v từ nguồn qua u , đó là (alt hoặc thay thế) khoảng cách bất kỳ tốt hơn so với hiện tại quận [v] chúng tôi đã nhận? Nếu vậy cho phép cập nhật xa [v] "
Lưu ý rằng trong line16
họ rà soát tất cả các nước láng giềng v (tức là một lợi thế cạnh đạo tồn tại từ u đến v ), của u mà là vẫn còn trong Q . Trong line14
họ loại bỏ nốt truy cập từ Q. Vì vậy, nếu x là một người hàng xóm đến thăm của u , con đường được thậm chí không coi như là một cách thể ngắn hơn từ nguồn tới v .
Trong ví dụ của chúng tôi ở trên, C là hàng xóm đã đến thăm của B, do đó đường dẫn không được xem xét, khiến đường dẫn ngắn nhất hiện tại không thay đổi.
Điều này thực sự hữu ích nếu các trọng số cạnh đều là các số dương , bởi vì sau đó chúng ta sẽ không lãng phí thời gian để xem xét các đường dẫn không thể ngắn hơn.
Vì vậy, tôi nói rằng khi chạy thuật toán này nếu x được trích xuất từ Q trước y , thì không thể tìm thấy đường dẫn - ngắn hơn. Hãy để tôi giải thích điều này với một ví dụ,
Vì y vừa được trích xuất và x đã được trích xuất trước chính nó, sau đó dist [y]> dist [x] bởi vì nếu không thì y sẽ được trích xuất trước x . ( line 13
khoảng cách tối thiểu trước)
Và như chúng ta đã giả sử rằng các trọng số cạnh là dương, tức là chiều dài (x, y)> 0 . Vì vậy, khoảng cách thay thế (alt) qua y luôn chắc chắn là lớn hơn, tức là dist [y] + length (x, y)> dist [x] . Vì vậy, giá trị của dist [x] sẽ không được cập nhật ngay cả khi y được coi là đường dẫn đến x , do đó chúng tôi kết luận rằng chỉ nên xem xét hàng xóm của y vẫn còn trong Q (ghi chú trong line16
)
Nhưng điều này phụ thuộc vào giả định của chúng tôi về độ dài cạnh dương, nếu độ dài (u, v) <0 thì tùy thuộc vào mức độ âm của cạnh đó, chúng tôi có thể thay thế dist [x] sau khi so sánh line18
.
Vì vậy, bất kỳ phép tính dist [x] nào chúng ta thực hiện sẽ không chính xác nếu x bị xóa trước khi tất cả các đỉnh v - sao cho x là hàng xóm của v có cạnh âm kết nối chúng - bị xóa.
Bởi vì mỗi đỉnh v đó là đỉnh cuối cùng thứ hai trên đường dẫn "tốt hơn" tiềm năng từ nguồn đến x , được loại bỏ bởi thuật toán của Dijkstra.
Vì vậy, trong ví dụ tôi đã đưa ra ở trên, lỗi là do C đã bị xóa trước khi B bị xóa. Trong khi đó C là hàng xóm của B với lợi thế tiêu cực!
Chỉ cần làm rõ, B và C là hàng xóm của A. B có một người hàng xóm C và C không có hàng xóm. chiều dài (a, b) là chiều dài cạnh giữa các đỉnh a và b.
Thuật toán của Dijkstra giả định các đường dẫn chỉ có thể trở nên 'nặng hơn', do đó, nếu bạn có đường dẫn từ A đến B có trọng số 3 và đường dẫn từ A đến C có trọng số 3, không có cách nào bạn có thể thêm cạnh và đi từ A đến B đến C với trọng lượng dưới 3.
Giả định này làm cho thuật toán nhanh hơn các thuật toán phải tính đến trọng số âm.
Tính chính xác của thuật toán Dijkstra:
Chúng ta có 2 bộ đỉnh ở bất kỳ bước nào của thuật toán. Tập A bao gồm các đỉnh mà chúng ta đã tính các đường đi ngắn nhất. Tập B bao gồm các đỉnh còn lại.
Giả thuyết quy nạp : Ở mỗi bước chúng ta sẽ cho rằng tất cả các lần lặp trước là đúng.
Bước quy nạp : Khi chúng ta thêm một đỉnh V vào tập A và đặt khoảng cách là dist [V], chúng ta phải chứng minh rằng khoảng cách này là tối ưu. Nếu điều này không tối ưu thì phải có một số đường dẫn khác đến đỉnh V có độ dài ngắn hơn.
Giả sử điều này một số đường dẫn khác đi qua một số đỉnh X.
Bây giờ, vì dist [V] <= dist [X], do đó, bất kỳ đường dẫn nào khác đến V sẽ có độ dài ít nhất dist [V], trừ khi biểu đồ có độ dài cạnh âm.
Do đó, để thuật toán của dijkstra hoạt động, các trọng số cạnh phải không âm.
Hãy thử thuật toán của Dijkstra trên biểu đồ sau, giả sử A
là nút nguồn, để xem điều gì đang xảy ra:
A->B
sẽ 1
và A->C
sẽ 100
. Rồi B->D
sẽ 2
. Rồi C->D
sẽ -4900
. Vì vậy, giá trị của D
sẽ -4900
giống như bellman-ford. Làm thế nào là điều này không đưa ra câu trả lời đúng?
A->B
sẽ 1
và A->C
sẽ 100
. Sau đó B
sẽ được tìm hiểu và bộ B->D
tới 2
. Sau đó D được khám phá vì hiện tại nó có đường dẫn ngắn nhất trở về nguồn? Tôi có đúng không khi nói rằng nếu B->D
được 100
, C
sẽ được khám phá trước? Tôi hiểu tất cả các ví dụ khác mọi người đưa ra ngoại trừ của bạn.
Hãy nhớ lại rằng trong thuật toán của Dijkstra, một khi một đỉnh được đánh dấu là "đóng" (và ngoài tập mở) - nó giả định rằng bất kỳ nút nào có nguồn gốc từ nó sẽ dẫn đến khoảng cách lớn hơn , vì vậy thuật toán tìm thấy đường đi ngắn nhất tới nó, và sẽ không bao giờ phải phát triển nút này một lần nữa, nhưng điều này không đúng trong trường hợp trọng số âm.
Các câu trả lời khác cho đến nay cho thấy khá rõ lý do tại sao thuật toán của Dijkstra không thể xử lý các trọng số âm trên các đường dẫn.
Nhưng bản thân câu hỏi có thể dựa trên sự hiểu biết sai về trọng lượng của các con đường. Nếu các trọng số âm trên các đường dẫn sẽ được cho phép trong các thuật toán tìm đường nói chung, thì bạn sẽ có các vòng lặp vĩnh viễn không dừng lại.
Xem xét điều này:
A <- 5 -> B <- (-1) -> C <- 5 -> D
Con đường tối ưu giữa A và D là gì?
Bất kỳ thuật toán tìm đường nào cũng sẽ phải liên tục lặp giữa B và C vì làm như vậy sẽ làm giảm trọng số của tổng đường dẫn. Vì vậy, việc cho phép các trọng số âm cho một kết nối sẽ hiển thị bất kỳ thuật toán pathfindig nào, có thể ngoại trừ nếu bạn giới hạn mỗi kết nối chỉ được sử dụng một lần.
Bạn có thể sử dụng thuật toán của dijkstra với các cạnh âm không bao gồm chu kỳ âm, nhưng bạn phải cho phép một đỉnh có thể được truy cập nhiều lần và phiên bản đó sẽ mất độ phức tạp thời gian nhanh.
Trong trường hợp đó, thực tế tôi đã thấy tốt hơn là sử dụng thuật toán SPFA có hàng đợi bình thường và có thể xử lý các cạnh âm.