Bellman-Ford Algoritması
Son güncelleme
Bellman-Ford, bir kaynak düğümden diğer tüm düğümlere en kısa yolu bulur ve Dijkstra'nın aksine bazı kenar ağırlıkları negatif olsa bile çalışır. Kaba kuvvet gevşetmesiyle çalışır: her kenarı tekrar tekrar tarar ve o kenardan geçmek daha kısa bir mesafe veriyorsa hedef düğümün mesafesini günceller. Geçici mesafelerin geçiş geçiş düştüğünü görmek için yukarıdan oynat'a basın.
Tüm kenarlar üzerinde V - 1 geçişten sonra, her en kısa yol (en fazla V - 1 kenar içerebilir) bulunmuş olur. Bu onu O(V · E) yapar - Dijkstra'dan daha yavaş, ancak negatif ağırlıkları işler ve negatif ağırlıklı bir döngüyü tespit edebilir (bir V. geçiş hâlâ bir kenarı gevşetiyorsa, en kısa yol yoktur).
Zaman ve alan karmaşıklığı
| Ölçüt | Karmaşıklık | Notlar |
|---|---|---|
| Zaman | O(V · E) | Tüm E kenarı üzerinde V − 1 geçiş |
| Alan | O(V) | Her düğüm için bir mesafe |
| Negatif ağırlıklar | Destekleniyor | Dijkstra'ya karşı temel avantajı |
| Negatif döngüler | Tespit edilebilir | Gevşeten bir V. geçiş birini işaret eder |
Adım adım
| Adım | Ne olur |
|---|---|
| 1 | Kaynak mesafesini 0, diğerlerinin tümünü sonsuz olarak ayarlayın. |
| 2 | Sonraki adımı V − 1 kez tekrarlayın. |
| 3 | Her kenar (u → v, w) için dist[u] + w < dist[v] olup olmadığını kontrol edin. |
| 4 | Öyleyse gevşetin: dist[v] = dist[u] + w olarak ayarlayın. |
| 5 | Tam bir geçiş hiçbir değişiklik yapmazsa erken durun. |
Çözümlü örnek
4 düğümlü S, A, B, C üzerinde kaynak S, kenarlar S→A (4), S→B (5), A→C (3), B→A (-3), bu sırayla gevşetilir. Mesafeler S=0 ve diğer her şey ∞ olarak başlar:
| Geçiş | Mesafeler [S, A, B, C] | Eylem |
|---|---|---|
| Init | [0, ∞, ∞, ∞] | Kaynak mesafesi 0, diğerlerinin tümü sonsuz. |
| 1 | [0, 2, 5, 7] | S→A 4'e, S→B 5'e, A→C 7'ye gevşer, ardından B→A A'yı 5 + (-3) = 2 değerine düşürür. |
| 2 | [0, 2, 5, 5] | A→C artık C'yi 2 + 3 = 5 değerine iyileştirir; başka hiçbir kenar gevşemez. |
| 3 | [0, 2, 5, 5] | Hiçbir kenar gevşemez, bu yüzden mesafeler kesindir: A, S→B→A üzerinden 2'ye mal olur. |
Bellman-Ford ne zaman kullanılır
| Şu durumda kullanın | Şu durumda kaçının |
|---|---|
| Kenar ağırlıkları negatif olabilir. | Tüm ağırlıklar negatif değil (Dijkstra daha hızlı). |
| Negatif ağırlıklı döngüleri tespit etmeniz gerekir. | Graf çok büyük ve yoğun - O(V · E) çok yavaş. |
| Graf küçük veya seyrek. | Büyük bir grafta yalnızca tek bir kaynak-hedef sorgusuna ihtiyacınız var. |
| Basit, uygulanması kolay bir temel gerekiyor. | Ağırlıklar negatif değil ve gecikme önemli. |
Bellman-Ford Algorithm kodu
Python, JavaScript, Java, C++, C dillerinde temiz ve çalıştırılabilir bir Bellman-Ford Algorithm uygulaması. Bir dil seçin, kodu kopyalayın veya Coddy Playground'da hazır yüklenmiş olarak açın.
Python ile Bellman-Ford Algorithm kodu
1def bellman_ford(vertices, edges, start):2 dist = {v: float("inf") for v in vertices}3 dist[start] = 04 # Relax every edge V-1 times5 for _ in range(len(vertices) - 1):6 for u, v, w in edges:7 if dist[u] + w < dist[v]:8 dist[v] = dist[u] + w9 # One more pass: any improvement means a negative cycle10 for u, v, w in edges:11 if dist[u] + w < dist[v]:12 raise ValueError("Graph contains a negative-weight cycle")13 return dist14
15
16vertices = ["A", "B", "C", "D", "E"]17edges = [18 ("A", "B", 6), ("A", "C", 7), ("B", "D", 5), ("B", "E", -4),19 ("C", "D", -3), ("D", "B", -2), ("E", "D", 7),20]21
22for node, d in bellman_ford(vertices, edges, "A").items():23 print(f"A -> {node}: {d}")JavaScript ile Bellman-Ford Algorithm kodu
1const nodes = ["A", "B", "C", "D", "E"];2const edges = [3 ["A", "B", 6], ["A", "C", 7], ["B", "D", 5], ["B", "E", -4],4 ["C", "D", -3], ["D", "B", -2], ["E", "A", 2],5];6
7function bellmanFord(source) {8 const dist = Object.fromEntries(nodes.map((n) => [n, Infinity]));9 dist[source] = 0;10 // Relax every edge V-1 times11 for (let i = 0; i < nodes.length - 1; i++) {12 for (const [u, v, w] of edges) {13 if (dist[u] + w < dist[v]) dist[v] = dist[u] + w;14 }15 }16 // One more pass: any improvement means a negative cycle17 for (const [u, v, w] of edges) {18 if (dist[u] + w < dist[v]) throw new Error("Negative cycle detected");19 }20 return dist;21}22
23console.log("Shortest distances from A:", bellmanFord("A"));Java ile Bellman-Ford Algorithm kodu
1import java.util.Arrays;2
3public class Main {4 public static void main(String[] args) {5 int n = 5;6 // Directed edges {from, to, weight}; negative weights allowed7 int[][] edges = {8 {0, 1, 6}, {0, 2, 7}, {1, 3, 5}, {1, 2, 8},9 {2, 4, 9}, {3, 1, -2}, {4, 3, 7}, {2, 3, -3}10 };11 int[] dist = new int[n];12 Arrays.fill(dist, Integer.MAX_VALUE);13 dist[0] = 0;14
15 // Relax every edge V-1 times16 for (int i = 0; i < n - 1; i++) {17 for (int[] e : edges) {18 if (dist[e[0]] != Integer.MAX_VALUE && dist[e[0]] + e[2] < dist[e[1]]) {19 dist[e[1]] = dist[e[0]] + e[2];20 }21 }22 }23
24 // One more pass: any improvement means a negative cycle25 boolean hasNegativeCycle = false;26 for (int[] e : edges) {27 if (dist[e[0]] != Integer.MAX_VALUE && dist[e[0]] + e[2] < dist[e[1]]) {28 hasNegativeCycle = true;29 }30 }31
32 System.out.println("Distances from 0: " + Arrays.toString(dist));33 System.out.println("Negative cycle: " + hasNegativeCycle);34 }35}C++ ile Bellman-Ford Algorithm kodu
1#include <iostream>2#include <vector>3
4struct Edge {5 int from, to, weight;6};7
8int main() {9 const int INF = 1000000000;10 const int n = 5;11 std::vector<Edge> edges = {12 {0, 1, 6}, {0, 2, 7}, {1, 3, 5}, {2, 3, -3},13 {1, 4, -4}, {3, 4, 9}, {2, 1, -2},14 };15 std::vector<int> dist(n, INF);16 dist[0] = 0;17 // Relax every edge V-1 times18 for (int pass = 0; pass < n - 1; ++pass) {19 for (const Edge& e : edges) {20 if (dist[e.from] != INF && dist[e.from] + e.weight < dist[e.to]) {21 dist[e.to] = dist[e.from] + e.weight;22 }23 }24 }25 // One extra pass: any improvement means a negative cycle26 bool negativeCycle = false;27 for (const Edge& e : edges) {28 if (dist[e.from] != INF && dist[e.from] + e.weight < dist[e.to]) {29 negativeCycle = true;30 }31 }32 if (negativeCycle) {33 std::cout << "Negative cycle detected\n";34 } else {35 for (int v = 0; v < n; ++v) {36 std::cout << "Distance 0 -> " << v << ": " << dist[v] << "\n";37 }38 }39 return 0;40}C ile Bellman-Ford Algorithm kodu
1#include <stdbool.h>2#include <stdio.h>3
4#define N 55#define INF 10000000006
7typedef struct {8 int from, to, weight;9} Edge;10
11int main(void) {12 Edge edges[] = {13 {0, 1, 6}, {0, 2, 7}, {1, 3, 5}, {2, 3, -3},14 {1, 4, -4}, {3, 4, 9}, {2, 1, -2},15 };16 int m = sizeof(edges) / sizeof(edges[0]);17 int dist[N];18 for (int v = 0; v < N; v++) dist[v] = INF;19 dist[0] = 0;20 // Relax every edge V-1 times21 for (int pass = 0; pass < N - 1; pass++) {22 for (int i = 0; i < m; i++) {23 Edge e = edges[i];24 if (dist[e.from] != INF && dist[e.from] + e.weight < dist[e.to]) {25 dist[e.to] = dist[e.from] + e.weight;26 }27 }28 }29 // One extra pass: any improvement means a negative cycle30 bool negativeCycle = false;31 for (int i = 0; i < m; i++) {32 Edge e = edges[i];33 if (dist[e.from] != INF && dist[e.from] + e.weight < dist[e.to]) {34 negativeCycle = true;35 }36 }37 if (negativeCycle) {38 printf("Negative cycle detected\n");39 } else {40 for (int v = 0; v < N; v++) {41 printf("Distance 0 -> %d: %d\n", v, dist[v]);42 }43 }44 return 0;45}Bellman-Ford SSS
Bellman-Ford'un zaman karmaşıklığı nedir?
O(V · E) sürede çalışır: V - 1 geçiş yapar ve her geçiş tüm E kenarını gevşetir. Bu, Dijkstra'nın O((V + E) log V) değerinden daha yavaştır; bu, negatif kenar ağırlıklarını desteklemenin bedelidir.Dijkstra yerine Bellman-Ford'u ne zaman kullanmalıyım?
Bellman-Ford negatif bir döngüyü nasıl tespit eder?
V - 1 gevşetme geçişinden sonra, negatif döngü yoksa tüm en kısa yollar kesinleşir. Bir geçiş daha hâlâ bir kenarı gevşetebiliyorsa, ulaşılabilir negatif ağırlıklı bir döngü vardır ve en kısa yollar tanımsızdır (maliyeti düşürmek için sonsuza dek döngü kurabilirsiniz).Bellman-Ford ile Floyd-Warshall arasındaki fark nedir?
O(V · E) sürede hesaplarken, Floyd-Warshall tüm çiftler arası en kısa yolları O(V³) sürede hesaplar. Yoğun bir grafta Bellman-Ford'u her kaynaktan çalıştırmak O(V² · E) maliyetlidir, bu yüzden tüm çiftlere ihtiyacınız olduğunda genellikle Floyd-Warshall daha iyi bir seçimdir. İkisi de negatif kenarları işler ve negatif döngüleri işaretleyebilir.Bellman-Ford neden tam olarak V - 1 geçişe ihtiyaç duyar?
V - 1 kenar kullanır, çünkü V'den fazla düğümü ziyaret eden bir yol birini tekrar etmek zorundadır. Her geçiş, her en kısa yolun en az bir kenarının daha doğru şekilde gevşetildiğini garanti eder, bu yüzden V - 1 geçiş hepsini oturtmaya yeter. Yaygın bir yanılgı, daha fazla geçişin sonucu iyileştirdiğidir - negatif bir döngü olmadıkça bunu asla yapmazlar.Bellman-Ford erken durabilir mi?
V - 1 geçişe ulaşmadan durabilirsiniz. Bu erken çıkış optimizasyonu, hızlı yakınsayan graflarda onu genellikle çok daha hızlı yapar, ancak en kötü durum sınırı O(V · E) olarak kalır.