Bellman-Ford ve Negatif Çevrimler
Tüm kenarlar üzerinde n-1 gevşetme geçişi yapın, son bir geçişle negatif çevrimleri belirleyin ve negatif ağırlıklı kenarlarda Dijkstra’nın neden başarısız olduğunu açıklayın.
Bellman-Ford ve Negatif Çevrimler, CoddyKit'te ücretsiz bir Coding Interview Prep dersidir. Bu, 4 dersinin 2. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, Coding Interview Prep öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. Coding Interview Prep kursu toplamda 4 dersten oluşur.
Bellman-Ford Neden Gerekli
Bellman-Ford, Dijkstra gibi tek kaynaklı en kısa yol problemini çözer; ancak negatif kenar ağırlıklarını da ele alır. Ayrıca negatif çevrimleri algılar: toplam ağırlığı negatif olan bu çevrimler, içlerinden geçen sonlu bir en kısa yol tanımlamayı imkânsız kılar. Dijkstra'dan daha yavaş olsa da graf negatif ağırlıklı kenarlar içerebiliyorsa doğru seçim Bellman-Ford'dur.
Gevşetme: Temel İşlem
Bellman-Ford tek bir işleme dayanır: gevşetme. (u, v, w) kenarını gevşetmek şu anlama gelir: dist[u] + w < dist[v] ise dist[v] = dist[u] + w değerini atayın. Tüm kenarları tekrar tekrar gevşetiriz. Temel fikir şudur: negatif çevrim içermeyen bir grafta her en kısa yol en fazla V-1 kenara sahiptir. Bu nedenle, tüm kenarlar üzerinde V-1 gevşetme turu yapmak bütün en kısa yolları bulmak için yeterlidir.
Bellman-Ford Uygulaması
Grafı bir kenar listesi olarak [(u, v, weight)] gösterin. dist[source] = 0 değerini başlatın ve diğer tüm değerleri inf yapın. V-1 tur çalıştırıp her turda tüm kenarları gevşetin. V. turda hâlâ gerçekleşen herhangi bir güncelleme, negatif bir çevrime işaret eder.
def bellman_ford(V, edges, source):
dist = [float('inf')] * V
dist[source] = 0
# V-1 relaxation passes
for _ in range(V - 1):
for u, v, w in edges:
if dist[u] != float('inf') and dist[u] + w < dist[v]:
dist[v] = dist[u] + w
# V-th pass: detect negative cycle
for u, v, w in edges:
if dist[u] != float('inf') and dist[u] + w < dist[v]:
return None # negative cycle exists
return dist
edges = [(0,1,4),(0,2,5),(1,2,-3),(2,3,1)]
print(bellman_ford(4, edges, 0)) # [0, 4, 1, 2]V-1 Tur Neden Yeterlidir
Negatif çevrim içermeyen bir graftaki en kısa yol her düğümü en fazla bir kez ziyaret eder; dolayısıyla en fazla V-1 kenara sahiptir. 1. turdan sonra en kısa 1 atlamalı yollar en iyi duruma gelir. 2. turdan sonra en kısa 2 atlamalı yollar en iyi duruma gelir. V-1 turdan sonra, en fazla V-1 atlama kullanan tüm en kısa yollar bulunmuş olur. V. tur hâlâ bir uzaklığı güncelliyorsa graf, kaynaktan erişilebilen bir negatif çevrim içerir.
Negatif Çevrim Algılama
V-1 turdan sonra tüm kenarlar üzerinde bir ek tur çalıştırın. (u, v, w) kenarlarından herhangi biri dist[u] + w < dist[v] koşulunu sağlıyorsa negatif bir çevrim vardır ve bazı düğümlere giden en kısa yol -infinity değerindedir. Gerçek dünya uygulamaları arasında döviz değişiminde arbitraj fırsatlarını algılama (log ağırlıklı graflardaki negatif çevrimler) ve kısıt sistemlerindeki tutarsızlıkları algılama bulunur.
def has_negative_cycle(V, edges, source):
dist = [float('inf')] * V
dist[source] = 0
for _ in range(V - 1):
for u, v, w in edges:
if dist[u] != float('inf') and dist[u] + w < dist[v]:
dist[v] = dist[u] + w
# Nth pass
for u, v, w in edges:
if dist[u] != float('inf') and dist[u] + w < dist[v]:
return True # negative cycle detected
return False
# Negative cycle: 1->2->3->1 with weights -1,-1,1 (sum=-1)
edges_neg = [(0,1,1),(1,2,-1),(2,3,-1),(3,1,1)]
print(has_negative_cycle(4, edges_neg, 0)) # TrueDijkstra ve Bellman-Ford Karşılaştırması
Dijkstra: O((V+E) log V), negatif olmayan ağırlıklar gerektirir, açgözlü yaklaşım kullanır. Bellman-Ford: O(V × E), negatif ağırlıkları ele alır ve negatif çevrimleri algılar. Negatif olmayan ağırlıklara sahip çoğu mülakat probleminde Dijkstra tercih edilir. Negatif ağırlıklar ortaya çıktığında (örneğin "negatif maliyetli kenarlarla en kısa yolu bulma" veya "arbitrajı algılama"), yanıt Bellman-Ford'dur. Yoğun graflarda Bellman-Ford'un en kötü durumdaki O(V³) karmaşıklığı, Floyd-Warshall ile karşılaştırılabilir.
Uygulama: Bellman-Ford ile En Ucuz Uçuşlar
K Durak İçindeki En Ucuz Uçuşlar (LeetCode 787), değiştirilmiş bir Bellman-Ford ile çözülebilir: tam olarak k+1 gevşetme turu çalıştırın (k durak, k+1 kenar anlamına gelir). Tek bir turda izin verilenden fazla atlama kullanmadığımızdan emin olmak için önceki turdaki uzaklıkların bir kopyasını kullanın; aksi takdirde tek bir turda birden fazla atlama zincirlenebilir.
def findCheapestPrice_bf(n, flights, src, dst, k):
dist = [float('inf')] * n
dist[src] = 0
for _ in range(k + 1): # k stops = k+1 edges
temp = dist[:] # copy to avoid using updated dist in same pass
for u, v, w in flights:
if dist[u] != float('inf') and dist[u] + w < temp[v]:
temp[v] = dist[u] + w
dist = temp
return dist[dst] if dist[dst] != float('inf') else -1
print(findCheapestPrice_bf(4,[[0,1,100],[1,2,100],[0,2,500]],0,2,1)) # 200SPFA: Kuyruk Tabanlı Optimizasyon
Shortest Path Faster Algorithm (SPFA), yalnızca uzaklığı az önce güncellenen düğümlerden çıkan kenarları bir kuyruk kullanarak yeniden gevşeten, Bellman-Ford'un optimize edilmiş hâlidir. Ortalama durum karmaşıklığı O(E) olsa da en kötü durum karmaşıklığı yine O(V × E) olur. Mülakatlarda SPFA'ya nadiren ihtiyaç duyulur; ancak Bellman-Ford seyrek graflarda çok yavaş kaldığında bunu bir optimizasyon olarak belirtebilirsiniz. Python'da yerleşik bir SPFA yoktur; ancak collections.deque ile uygulamak kolaydır.
Döviz Arbitrajı Algılama
Bellman-Ford'un klasik bir uygulaması şudur: döviz kurları verildiğinde arbitrajın mümkün olup olmadığını algılayın (dövizleri dönüştürdükten sonra başladığınızdan daha fazlasını elde ettiğiniz bir çevrim). Döviz kurlarının negatif logaritmasını alarak dönüşüm yapın. Arbitraj = negatif toplam log ağırlığına sahip bir çevrim = Bellman-Ford ile algılanabilen negatif çevrim. Bu, gerçek dünyadaki finans problemlerini standart algoritmaya dönüştürür.
import math
def has_arbitrage(rates):
n = len(rates)
# Transform: -log(rate) converts product to sum
log_rates = [[-math.log(rates[i][j]) for j in range(n)] for i in range(n)]
edges = [(i,j,log_rates[i][j]) for i in range(n) for j in range(n) if i != j]
dist = [float('inf')] * n
dist[0] = 0
for _ in range(n - 1):
for u, v, w in edges:
if dist[u] + w < dist[v]:
dist[v] = dist[u] + w
for u, v, w in edges:
if dist[u] + w < dist[v]:
return True # arbitrage!
return FalseErken Sonlandırma Optimizasyonu
Tüm kenarlar üzerinde yapılan tam bir turda hiçbir uzaklık güncellenmezse sonraki turlar da hiçbir şeyi güncellemeyecektir; bu nedenle erken sonlandırın. Bu optimizasyon, graf birkaç turdan sonra zaten en iyi durumdaysa en iyi durum karmaşıklığını O(E) değerine düşürür. Her turun başında updated = False bayrağını ekleyin; turdan sonra False olarak kalırsa hemen çıkın.
def bellman_ford_optimised(V, edges, source):
dist = [float('inf')] * V
dist[source] = 0
for _ in range(V - 1):
updated = False
for u, v, w in edges:
if dist[u] != float('inf') and dist[u] + w < dist[v]:
dist[v] = dist[u] + w
updated = True
if not updated:
break # no more improvements possible
return distKomşuluk Listeli Graflarda Bellman-Ford
Graf, kenar listesi yerine komşuluk listesi olarak verildiğinde önce bir kenar listesine dönüştürün veya tüm komşuluk listesi girdilerini kenarlar olarak dolaşın. V=1000 ve E=5000 için, her biri 5000 kenarı tarayan V-1=999 tur toplam 4.995.000 işlem yapar; bu, süre sınırlarının rahatlıkla içindedir. Çok yoğun graflarda (E ≈ V²) O(V³) en kötü durum karmaşıklığı Floyd-Warshall ile aynı olur; bu nedenle seçim bağlama bağlıdır.
from collections import defaultdict
def bellman_ford_adj(V, adj, source):
# Convert adjacency list to edge list
edges = [(u, v, w) for u in range(V) for v, w in adj[u]]
dist = [float('inf')] * V
dist[source] = 0
for _ in range(V - 1):
for u, v, w in edges:
if dist[u] != float('inf') and dist[u] + w < dist[v]:
dist[v] = dist[u] + w
return distHızlı Kontrol
Bu dersteki Veri Yapıları ve Algoritmalar — Kodlama Mülakatı Hazırlığı kavramlarını anlayıp anlamadığınızı test edin.
Ders Özeti
Bu derste şunları öğrendiniz: Bellman-Ford, negatif ağırlıklı kenarları ele almak için tüm kenarları V-1 kez gevşetir, iyileştirmeler bulmaya devam eden V. gevşetme turu negatif bir çevrime işaret eder ve algoritmanın karmaşıklığı, Dijkstra'nın O((V+E) log V) değerine karşılık O(V × E)'dir. Sırada, tek bir O(V³) hesaplamayla tüm düğüm çiftleri arasındaki en kısa yolları bulan Floyd-Warshall var.
Sıkça Sorulan Sorular
“Bellman-Ford ve Negatif Çevrimler” dersi ücretsiz mi?
Evet — “Bellman-Ford ve Negatif Çevrimler” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve Coding Interview Prep kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. Coding Interview Prep kursu toplamda 4 dersten oluşur.
“Bellman-Ford ve Negatif Çevrimler” dersinde ne öğreneceğim?
Tüm kenarlar üzerinde n-1 gevşetme geçişi yapın, son bir geçişle negatif çevrimleri belirleyin ve negatif ağırlıklı kenarlarda Dijkstra’nın neden başarısız olduğunu açıklayın. Coding Interview Prep ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.
Coding Interview Prep öğrenmeye başlamak için deneyim gerekli mi?
Önceden deneyim gerekmez. CoddyKit'te Coding Interview Prep, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 2. dersidir.
“Bellman-Ford ve Negatif Çevrimler” dersi ne kadar sürer?
Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.
Bu Coding Interview Prep dersinde kod yazıp çalıştırabilir miyim?
Evet. Her Coding Interview Prep dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.
Bu kursun tüm dersleri
- Öncelik Kuyruğuyla Dijkstra Algoritması
- Bellman-Ford ve Negatif Çevrimler
- Floyd-Warshall: Tüm Çiftler İçin En Kısa Yollar
- Ağ Gecikme Süresi ve Yolun Yeniden Oluşturulması