Bölüm 4.4: Ağ Akışı
Giriş
Öğrenme Çıktıları
Bir problemi kaynak, hedef ve kapasiteli kenarlardan oluşan bir akış ağı olarak kurmak; akışın kapasite ve korunum kısıtlarını yazmak.
Artık ağı, artış yolunu ve darboğazı hesaplamak; s–t kesiminin kapasitesini bulmak ve maks-akış min-kesim teoremini uygulamak.
Ford-Fulkerson, Edmonds-Karp, Dinic ve itme–yeniden etiketleme algoritmalarını bir örnek ağ üzerinde adım adım çalıştırmak.
O(E·|f*|), O(V·E²) ve O(V²·E) sınırlarının nereden geldiğini açıklamak; algoritmaları karşılaştırıp uygun olanı seçmek.
Akış Ağları
Maksimum Akış (Maximum Flow) Problemi
- Yönlü çizgede bir kaynak (source, s) ve bir hedef (sink, t) düğüm bulunur.
- Her kenara bir kapasite (capacity) c(u, v) ≥ 0 atanır: o kenardan birim zamanda geçebilecek en fazla miktar.
- Amaç: kapasiteleri aşmadan kaynaktan hedefe gönderilebilecek en büyük akış miktarını bulmak.
Kenar etiketleri kapasitedir. Bu ağda en büyük akış 7'dir.
Akış Ağları
Akış (Flow) ve Kısıtları
Akış, her kenara bir sayı f(u, v) atayan bir fonksiyondur:
- Kapasite kısıtı: 0 ≤ f(u, v) ≤ c(u, v).
- Akış korunumu: s ve t dışındaki her v için
giren akış = çıkan akış.
Akışın değeri: |f| = Σ f(s, v) − Σ f(v, s)
(kaynaktan çıkan net akış = hedefe giren net akış).
Etiket: akış/kapasite. C'ye 3 + 2 = 5 girer, 5 çıkar. |f| = 4 + 3 = 7.
Akış Ağları
Birden Çok Kaynak ve Hedef
- Birden çok kaynak (s₁ … sₙ) ve hedef (t₁ … tₘ) varsa yeni bir süper kaynak S ve süper hedef T eklenir.
- S → sᵢ ve tⱼ → T kenarlarına sınırsız (∞) kapasite verilir; problem tek kaynaklı, tek hedefli maksimum akışa dönüşür.
Artık Ağ ve Artış Yolu
Artık Ağ (Residual Network)
- Artık ağ Gf, mevcut akış f altında hâlâ gönderilebilecek akışı gösterir.
- İleri kenar: cf(u, v) = c(u, v) − f(u, v) — kapasiteden akış çıkarılır; akış kapasiteden azsa kalan kapasite pozitiftir.
- Ters kenar: cf(v, u) = f(u, v) — gönderilmiş akış geri alınabilir (iptal edilebilir).
- Yalnızca cf > 0 olan kenarlar Gf'ye girer; artış yolları bu ağda aranır.
Düz ok: ileri (kalan) kapasite · kesikli ok: ters kenar. Ters kenarlar olmadan algoritma ilk yanlış seçimini düzeltemez.
Artık Ağ ve Artış Yolu
Artış Yolu (Augmenting Path)
- Artık ağda kaynaktan hedefe giden yönlü bir yoldur; kaynaktan hedefe ek akış sağlar.
- Yoldaki her kenarın artık kapasitesi pozitiftir: kenarlar kapasitelerinden daha az akış taşır (ya da ters kenardır).
- Darboğaz (bottleneck): cf(p) = min cf(u, v), (u, v) ∈ p. Yol boyunca bu kadar akış eklenir.
- Genellikle DFS veya BFS ile bulunur; artış yolu kalmayınca akış maksimumdur.
p = S → A → B → T: min(4, 4, 2) = 2.
Kesimler
Kesimler (Cuts)
- s–t kesimi (S, T): düğümleri s ∈ S ve t ∈ T olacak biçimde ikiye bölen ayrımdır; kesilen kenarlar çizgenin bağlantısını keser.
- Kesim kapasitesi: c(S, T) = Σ c(u, v), u ∈ S, v ∈ T — yalnızca S'den T'ye giden kenarlar sayılır.
- Her akış için kesimden geçen net akış |f|'ye eşittir; bu yüzden |f| ≤ c(S, T).
- Minimum kesim (min-cut): kapasitesi en küçük kesim (kenar sayısı değil, kapasite toplamı).
S = {S, A}: c(S, T) = 4 + 3 = 7 (minimum).
Kesimler
Maksimum Akış – Minimum Kesim Teoremi
Bir f akışı için aşağıdakiler denktir (Ford ve Fulkerson, 1956):
- f bir maksimum akıştır.
- Artık ağ Gf'de hiçbir artış yolu yoktur.
- Bir (S, T) kesimi için |f| = c(S, T).
| S tarafı (örnek ağ) | c(S, T) |
|---|---|
| {S} | 7 |
| {S, A} | 7 |
| {S, A, B} | 8 |
| {S, A, B, C, D} | 8 |
| {S, C} | 10 |
| {S, B, D} | 18 |
16 kesimin en küçüğü 7'dir; bulunan akışın değeri de 7 olduğu için bu akış maksimumdur.
Kesimler
Ağ Akış Algoritmaları (Network Flow)
Amaç: kısıtlar altında kaynaktan hedefe mümkün olan en fazla akışı sağlamak.
| Algoritma | Yaklaşım | Süre | Not |
|---|---|---|---|
| Ford-Fulkerson (1956) | herhangi bir artış yolu (çoğunlukla DFS) | O(E · |f*|) | basit; süre akış değerine bağlı |
| Edmonds-Karp (1972) | en kısa artış yolu (BFS) | O(V · E²) | Ford-Fulkerson'ın polinom zamanlı hâli |
| Dinic (1970) | seviye çizgesi + engelleyici akış | O(V² · E) | Edmonds-Karp'tan hızlı; birim kapasitede çok verimli |
| İtme–yeniden etiketleme (1988) | ön akış + yükseklikler, yerel işlemler | O(V² · E) | yol aramaz; FIFO sürümü O(V³) |
|f*|: maksimum akışın değeri. Ford-Fulkerson'ın süresi tam sayı kapasiteler için geçerlidir.
Ford-Fulkerson
Ford-Fulkerson Yöntemi
- L. R. Ford Jr. ve D. R. Fulkerson tarafından geliştirilmiştir (1956).
- Ağırlıklı (kapasiteli) yönlü çizgede iki düğüm arasındaki maksimum akışı bulur: kaynaktan hedefe, belirli kapasiteli borularla gönderilebilecek en fazla suyu modelleyen bir çizge problemi.
- İlkeler: ağ yönlü bir çizgedir; her kenarın bir kapasitesi vardır; başlangıçta tüm akışlar 0'dır; artış yolu bulma adımı tekrarlanarak maksimum akışa ulaşılır.
- Artış yolunun nasıl bulunacağını belirtmediği için bir yöntemdir: Ford-Fulkerson genellikle DFS, Edmonds-Karp ise BFS kullanır.
Ford-Fulkerson
Ford-Fulkerson: Algoritma Adımları
0. Tüm kenarlarda f(u, v) = 0; artık ağ = özgün ağ.
Başlangıçta akış sıfırdır, her kenarın artık kapasitesi kendi kapasitesidir.
1. Artık ağda kaynaktan hedefe bir artış yolu p bul (cf > 0 olan kenarlar).
Yol DFS ile (Ford-Fulkerson) ya da BFS ile (Edmonds-Karp) aranır.
2. Darboğazı hesapla: δ = min cf(u, v), (u, v) ∈ p.
Yol boyunca gönderilebilecek en fazla akış, yolun en dar kenarıdır.
3. Yol boyunca δ kadar akış gönder: f(u, v) += δ, ters kenar için f(v, u) −= δ; |f| += δ.
Ters kenarlar güncellenir; yoldaki en az bir kenar doyar.
4. Artış yolu kalmayana kadar 1–3'ü tekrarla; |f| maksimumdur.
Döngü, hedefe ulaşılamadığında (artış yolu kalmadığında) biter; maks-akış min-kesim teoremi sonucun en iyi olduğunu garanti eder.
Ford-Fulkerson
Ford-Fulkerson Örneği 1
Başlangıç: tüm akışlar 0, artık ağ özgün ağın aynısı.
İlk artış yolu p = {S, A, B, T}: darboğaz 2 birim. |f| = 2.
İkinci artış yolu p = {S, C, D, T}: darboğaz 3 birim. |f| = 5.
Üçüncü artış yolu p = {S, A, B, C, D, T}: darboğaz 2 birim. |f| = 7.
S'den çıkan iki kenar da doydu: artık ağda S'den hiçbir yere gidilemez. Maksimum akış 7; min kesim ({S}, diğerleri) = 4 + 3.
Ford-Fulkerson
Ford-Fulkerson Örneği 2: Ters Kenar Kullanımı
Kaynak S, hedef T. Başlangıçta tüm akışlar 0.
p = S → 1 → 4 → T: darboğaz 1 → 4 (8). |f| = 8.
p = S → 3 → 4 → T: 4 → T'de yalnızca 2 kaldı. |f| = 10.
p = S → 3 → 4 → 1 → 2 → T: 4 → 1 ters kenarı kullanılır; 1 → 4 akışı 8'den 4'e düşer, 1'den çıkan 4 birim 1 → 2'ye yönlenir. |f| = 14.
p = S → 1 → 4 → 2 → T: S → 1'de 2 birim kalmıştı. |f| = 16.
p = S → 3 → 4 → 2 → T: darboğaz 3 → 4 (9 − 6 = 3 kaldı). |f| = 19.
Artık ağda S'den yalnızca 3'e ulaşılır → min kesim ({S, 3}, diğerleri) = 10 + 9 = 19 = |f*|.
Ford-Fulkerson
Ters Kenarlar Neden Gerekli?
- S → 1 → 4 → T: 8
- S → 3 → 4 → T: 2
- S → 1 → 2 → T: 2
- S → 3 → 4 → 2 → T: 6
Toplam 18. S'den yalnızca 3 ve 4'e gidilebiliyor; 4'ün çıkışları dolu. Takıldı!
İlk seçim 1 → 4'e 8 birim gönderdi. 4 → 1 ters kenarı bu kararın bir kısmını geri almayı sağlar: 1'den gelen akış 2'ye, 3'ten gelen akış 4'e yönlenir.
Sonuç 19 = min kesim. Yol hangi sırayla seçilirse seçilsin artık ağ maksimuma ulaşır.
Ford-Fulkerson
Ford-Fulkerson: Sözde Kod
FORD_FULKERSON(G, kaynak, hedef): akış[u, v] = 0, her bir (u, v) için E içinde // başlangıçta 0 maksimumAkış = 0 döngü artırıcı yol var iken: yol = DFS(G, kaynak, hedef, akış) // c − akış > 0 kenarlarla delta = ∞ // yoldaki en küçük kalan kapasite v = hedef döngü v ≠ kaynak iken: u = yol.ata[v] delta = min(delta, kapasite[u, v] − akış[u, v]) v = u v = hedef // yoldaki akışları güncelle döngü v ≠ kaynak iken: u = yol.ata[v] akış[u, v] += delta akış[v, u] −= delta // ters kenar v = u maksimumAkış += delta döndür maksimumAkış, akış
Ford-Fulkerson
Java: DFS ile Artış Yolu
boolean dfs(int u, int hedef, boolean[] ziyaret, int[] ata) { ziyaret[u] = true; if (u == hedef) return true; for (int v = 0; v < V; v++) { if (!ziyaret[v] && artik[u][v] > 0) { ata[v] = u; if (dfs(v, hedef, ziyaret, ata)) return true; } } return false; // bu daldan hedefe yol yok }
artik[u][v]: artık kapasite matrisi. Başlangıçta kapasiteler; ters kenarlar 0.- Hedefe ulaşılınca
truedöner;ata[]yolu tersten verir. - Akış eklenirken
artik[u][v] −= dveartik[v][u] += dyapılır: ters kenar kendiliğinden oluşur. - Komşu matrisinde bir DFS O(V²), komşu listesinde O(V + E) sürer.
Ford-Fulkerson
Ford-Fulkerson: Karmaşıklık Analizi
- Çalışma süresi ağın yapısına ve kapasitelere bağlıdır.
- Bir artış yolu bulmak (DFS/BFS) ve akışı güncellemek: O(E).
- Kapasiteler tam sayı ise her artış yolu akışı en az 1 birim artırır → en fazla |f*| tekrar.
- Toplam: O(E · |f*|); E kenar sayısı, |f*| maksimum akış değeri.
Ford-Fulkerson
Kötü Yol Seçimi: 2000 Artış
- Maksimum akış 2000 (s → a → t ve s → b → t).
- Kötü bir yol seçimi her seferinde ortadaki kenarı kullanır: s → a → b → t, sonra ters kenarla s → b → a → t … Her yol yalnızca 1 birim ekler → 2000 artış.
- Edmonds-Karp (BFS) en kısa yolları seçer: 2 artış yeter.
- Kapasite 10⁹ olsaydı DFS'li sürüm milyarlarca adım atabilirdi; BFS bu bağımlılığı kaldırır.
Edmonds-Karp
Edmonds-Karp Algoritması
- Ağırlıklı yönlü çizgede iki düğüm arasındaki maksimum akışı bulur; Ford-Fulkerson yönteminin bir türevidir.
- Jack Edmonds ve Richard Karp tarafından geliştirilmiştir (1972; Dinic 1970'te bağımsız olarak).
- Artış yollarını BFS (genişlik öncelikli arama) ile bulur: her seferinde kenar sayısı en az olan artış yolu seçilir.
- Edmonds-Karp BFS'yi artış yolu bulmak için kullanır; Dinic ise BFS'yi daha fazla akış mümkün mü diye bakmak ve seviye çizgesi kurmak için kullanır.
Edmonds-Karp
Edmonds-Karp: Karmaşıklık O(V · E²)
- Her tur bir BFS ve bir yol güncellemesidir: O(E).
- BFS en kısa yolu seçtiği için s'ten her düğüme olan artık ağ uzaklığı hiç azalmaz.
- Her artışta en az bir kenar kritik olur (doyar). Aynı kenarın yeniden kritik olabilmesi için uzaklığı en az 2 artmalıdır; uzaklık ≤ V olduğundan her kenar en fazla ~V/2 kez kritik olur.
- Artış sayısı O(V · E) → toplam O(V · E) · O(E) = O(V · E²); E kenar, V düğüm sayısı.
Edmonds-Karp
Java: BFS ile Artış Yolu
boolean bfs(int kaynak, int hedef, int[] ata) { boolean[] ziyaret = new boolean[V]; Queue<Integer> kuyruk = new LinkedList<>(); kuyruk.add(kaynak); ziyaret[kaynak] = true; ata[kaynak] = -1; while (!kuyruk.isEmpty()) { int u = kuyruk.poll(); for (int v = 0; v < V; v++) if (!ziyaret[v] && artik[u][v] > 0) { kuyruk.add(v); ata[v] = u; ziyaret[v] = true; } } return ziyaret[hedef]; }
- Kuyruk kaynaktan başlar; artık kapasitesi pozitif ve ziyaret edilmemiş her komşu kuyruğa eklenir.
ata[v] = uile BFS ağacı saklanır; hedef ziyaret edildiyse bulunan yol en az kenarlı yoldur.- Ford-Fulkerson'dan tek farkı bu fonksiyondur; ana döngü aynıdır.
Edmonds-Karp · Kodu adım adım çalıştır
edmondsKarp(s, t): Kodu Adım Adım İzle
Başlangıç: artık ağ = kapasiteler, maksAkis = 0.
bfs en kısa yolu buldu: s → 1 → 3 → t (3 kenar).
Hedeften geriye: akis = min(16, 12, 20) = 12.
Yoldaki ileri kenarlardan 12 çıkar, ters kenarlara 12 eklenir (kesikli oklar).
maksAkis = 0 + 12 = 12.
bfs en kısa yolu buldu: s → 2 → 4 → t (3 kenar).
Hedeften geriye: akis = min(13, 14, 4) = 4.
Yoldaki ileri kenarlardan 4 çıkar, ters kenarlara 4 eklenir (kesikli oklar).
maksAkis = 12 + 4 = 16.
bfs en kısa yolu buldu: s → 2 → 4 → 3 → t (4 kenar).
Hedeften geriye: akis = min(9, 10, 7, 8) = 7.
Yoldaki ileri kenarlardan 7 çıkar, ters kenarlara 7 eklenir (kesikli oklar).
maksAkis = 16 + 7 = 23.
BFS t'ye ulaşamadı (s'ten yalnızca s, 1, 2, 4 ulaşılabilir). return 23.
int edmondsKarp(int s, int t) { int maksAkis = 0; int[] ata = new int[V]; while (bfs(s, t, ata)) { int akis = Integer.MAX_VALUE; for (int v = t; v != s; v = ata[v]) akis = Math.min(akis, artik[ata[v]][v]); for (int v = t; v != s; v = ata[v]) { int u = ata[v]; artik[u][v] -= akis; // ileri artik[v][u] += akis; // ters } maksAkis += akis; } return maksAkis; }
Edmonds-Karp
Edmonds-Karp Örneği
Ağ verilsin. Üst sınır: |f*| ≤ 24 (s'den çıkan kapasite 16 + 13 = 29, t'ye giren 20 + 4 = 24; küçüğü).
Adım 1: c(1, 3) yüzünden cf(p1) = 12 olan p1 = ⟨s, 1, 3, t⟩ artış yolu seçilir. |f| = 12.
Adım 2: c(4, t) yüzünden cf(p2) = 4 olan p2 = ⟨s, 2, 4, t⟩ artış yolu seçilir. |f| = 16.
Adım 3: c(4, 3) yüzünden cf(p3) = 7 olan p3 = ⟨s, 2, 4, 3, t⟩ artış yolu seçilir. |f| = 23.
Son durum: artık ağda s'ten yalnızca 1, 2, 4'e ulaşılır. Kesim ({s, 1, 2, 4}, {3, t}) (kırmızı: T tarafı): 12 + 7 + 4 = 23 = |f*| (t'ye giren akış: 19 + 4).
Edmonds-Karp
Edmonds-Karp Örneği 2
Kaynak A, hedef G. Başlangıçta tüm akışlar 0.
p = {A, D, E, G}: E → G (1) darboğaz. Akış = 1.
p = {A, D, F, G}: A → D'de 2 kaldı. Akış = 1 + 2 = 3.
p = {A, B, C, D, F, G}: A → D doydu, BFS 5 kenarlı yolu bulur; C → D (1) darboğaz. Akış = 4.
p = {A, B, C, E, D, F, G}: E → D ters kenarı D → E akışını 1'den 0'a indirir. Akış = 5.
BFS G'ye ulaşamaz; A'dan A, B, C, E ulaşılabilir. Kesim kapasitesi A→D 3 + C→D 1 + E→G 1 = 5 = |f*|.
Edmonds-Karp
Edmonds-Karp Örneği 3: Artık Ağ Üzerinde
s'ten t'ye; başlangıçta artık ağ = kapasiteler. En kısa yolların hepsi 3 kenarlı: s→4→t, s→1→2→t, s→1→3→t.
s → 4 → t yolu: darboğaz s → 4 (2). 2 birim itilir; s → 4 kalanı 0, 4 → s ters kenarı 2.
s → 1 → 2 → t yolu: darboğaz 2 → t (3). 3 birim itilir.
s → 1 → 3 → t yolu: s → 1'de 1 kalmıştı. 1 birim itilir.
s'ten çıkan kenarlar doydu → yol yok. |f*| = 2 + 3 + 1 = 6: 3 birim s→1→2→t, 1 birim s→1→3→t, 2 birim s→4→t.
Dinic
Dinic Algoritması
- Bir çizgede maksimum akışı bulur (Y. Dinitz, 1970); kapasiteli (ağırlıklı) çizge üzerinde çalışır.
- Seviye çizgesi (level graph): artık ağda BFS ile her düğümün seviyesi (kaynağa uzaklığı) bulunur; yalnızca seviye(v) = seviye(u) + 1 olan kenarlar tutulur.
- Engelleyici akış (blocking flow): seviye çizgesinde her s–t yolunda en az bir doymuş kenar bırakan akış; DFS ile bulunur.
- Akış ters kenarlarla güncellenir; hedef seviye çizgesinde ulaşılamaz olana kadar fazlar tekrarlanır.
Dinic
Dinic: Algoritma Adımları
1. Başlangıçta tüm kenarların akışına 0 ata.
Artık ağ özgün ağla aynıdır.
2. Kaynaktan BFS ile her düğümün seviyesini (kaynağa uzaklık) belirle. Hedefin seviyesi yoksa dur.
BFS, artık kapasitesi pozitif kenarları kullanır; hedefe ulaşılamıyorsa akış maksimumdur.
3. DFS ile her adımda bir seviye ileri giden artış yolları bul.
Yalnızca seviye(v) = seviye(u) + 1 olan kenarlar izlenir; çıkmaz kenarlar bir daha denenmez.
4. Her yoldaki minimum kapasiteyi toplam akışa ekle; kenarların ve ters kenarların kapasitesini güncelle.
Yol kalmayınca bu fazın engelleyici akışı bulunmuştur.
5. 2. adıma dön ve işlemleri tekrarla.
Her fazda s–t uzaklığı en az 1 artar; bu yüzden en fazla V − 1 faz vardır.
Dinic
Dinic Örneği: Fazlar
Ağ (akış/kapasite). Başlangıçta tüm akışlar 0; artık ağ = kapasiteler.
Faz 1 – BFS: seviyeler s = 0; 1, 2 = 1; 3, 4 = 2; t = 3. Soluk kenarlar (1 → 2, 4 → 3) seviye atlamaz, kullanılmaz.
Faz 1 – engelleyici akış (DFS): {s, 1, 3, t} 4 birim, {s, 1, 4, t} 6 birim, {s, 2, 4, t} 4 birim → |f| = 14.
Faz 1 sonrası artık ağ (akış çizgesi Gf): doymuş kenarlar yalnızca ters yönde (kesikli) kalır.
Faz 2 – BFS: s = 0; 2 = 1; 4 = 2; 1, 3 = 3; t = 4. s–t uzaklığı 3'ten 4'e çıktı. 4 → 1 kenarı çıkmaz sokaktır.
Faz 2 – engelleyici akış: {s, 2, 4, 3, t} 5 birim (darboğaz 2 → 4) → |f| = 14 + 5 = 19.
Faz 3 – BFS: yalnızca s (0) ve 2 (1) ulaşılabilir; t'ye yol yok → algoritma biter. Min kesim ({s, 2}, diğerleri) = 10 + 9 = 19.
Sonuç: maksimum akış 19; yalnızca 2 faz (2 + 1 BFS) yetti. Aynı ağda Ford-Fulkerson 5 artış yolu kullanmıştı.
Dinic
Dinic: Sözde Kod
DINIC(G, kaynak, hedef): akış = [ ] // her kenar için 0 maksimumAkış = 0 döngü BFS(G, kaynak, hedef, akış) iken: // seviye çizgesi kuruldu döngü engelleyici akış var iken: delta = DFS(G, kaynak, hedef, akış, seviye, ∞) maksimumAkış += delta döndür maksimumAkış BFS(G, kaynak, hedef, akış): seviye[v] = −1, her v için seviye[kaynak] = 0 kuyruk.ekle(kaynak) döngü kuyruk boş değil iken: u = kuyruk.çıkar() her bir (u, v) için G[u] içinde: eğer seviye[v] < 0 ve kapasite[u,v] − akış[u,v] > 0: seviye[v] = seviye[u] + 1 kuyruk.ekle(v) döndür seviye[hedef] ≥ 0
DFS(G, u, hedef, akış, seviye, lim): eğer u == hedef: döndür lim her bir (u, v) için G[u] içinde: kalan = kapasite[u,v] − akış[u,v] eğer seviye[v] == seviye[u] + 1 ve kalan > 0: delta = DFS(G, v, hedef, akış, seviye, min(lim, kalan)) eğer delta > 0: akış[u, v] += delta akış[v, u] −= delta // ters döndür delta döndür 0
Dinic
Java: Seviyeli DFS ve Kaldığı Yer Dizisi
int dfs(int u, int t, int akis, int[] basla) { if (u == t) return akis; for (; basla[u] < komsu[u].size(); basla[u]++) { Kenar k = komsu[u].get(basla[u]); if (seviye[k.hedef] == seviye[u] + 1 && k.akis < k.kapasite) { int d = dfs(k.hedef, t, Math.min(akis, k.kapasite - k.akis), basla); if (d > 0) { k.akis += d; // ileri kenar komsu[k.hedef].get(k.tersIndis).akis -= d; // ters kenar return d; } } } return 0; // u'dan t'ye seviyeli yol kalmadı }
- Kenarlar komşu listesinde tutulur; her kenar ters kenarının indisini (
tersIndis) bilir. basla[u]: u'da denenecek sıradaki kenar (current arc). Çıkmaz kenar bir daha denenmez; bir fazdaki tüm DFS'ler toplam O(V · E).- Her fazın başında
baslasıfırlanır, seviyeler BFS ile yeniden hesaplanır.
Dinic
Dinic: Karmaşıklık O(V² · E)
- Her faz bir BFS gerektirir: O(V + E).
- Her fazdan sonra s–t uzaklığı kesin olarak artar ve uzaklık ≤ V − 1 olduğundan en kötü durumda V faz olabilir.
- Bir fazdaki engelleyici akış: her artış yolu O(V) uzunluktadır ve en fazla E yol bulunur; kaldığı yer dizisiyle faz başına O(V · E).
- Toplam: O(V) · O(V · E) = O(V² · E).
İtme–Yeniden Etiketleme
İtme ve Yeniden Etiketleme (Push–Relabel)
- A. Goldberg ve R. Tarjan (1988). Artış yolu aramaz; düğümler üzerinde yerel işlemlerle çalışır.
- Kaynaktan çıkan tüm kenarları doldurarak aşırı akış başlatır; akış, korunum kısıtını geçici olarak bozan bir ön akıştır (preflow).
- Her düğümün bir yükseklik (height) etiketi vardır; akış yalnızca yüksekten bir alçağa doğru itilir (push), su gibi.
- Bir düğüm fazlasını itemiyorsa yüksekliği artırılır (relabel).
- Aşırı akışı olan düğüm kalmayınca ön akış gerçek bir akıştır; hedefteki aşırı akış maksimum akışı verir.
İtme–Yeniden Etiketleme
Ön Akış ve Aşırı Akış (Excess Flow)
e(V) = (3 + 7) − (2 + 4) = 4
- Aşırı akış: e(v) = giren toplam akış − çıkan toplam akış.
- Ön akış: kapasite kısıtına uyar, korunum yerine e(v) ≥ 0 ister (s hariç).
- e(v) > 0 olan düğüm (s ve t dışında) aktif düğümdür; algoritma aktif düğüm kalmayınca biter.
İtme–Yeniden Etiketleme
Yükseklik Fonksiyonu ve Başlangıç
Başlangıç:
- Tüm kenarların akışı 0.
- h(s) = |V| (düğüm sayısı); diğer tüm düğümler h = 0.
- s'ten çıkan her kenar doldurulur: f(s, v) = c(s, v), e(v) = c(s, v).
Değişmezler:
- h(s) = |V| ve h(t) = 0 hiç değişmez.
- Artık ağdaki her (u, v) kenarı için h(u) ≤ h(v) + 1.
- Bu yüzden h(u), u'dan t'ye artık ağ uzaklığının bir alt sınırıdır.
İtme–Yeniden Etiketleme
İtme (Push) ve Yeniden Etiketleme (Relabel)
Koşul: e(u) > 0, cf(u, v) > 0 (kenarda kapasite ile akış arasında fark var) ve h(u) = h(v) + 1.
δ = min(e(u), cf(u, v)) kadar akış itilir: e(u) −= δ, e(v) += δ. Kenar doyarsa doyuran, aksi hâlde doyurmayan itme.
Koşul: e(u) > 0 ama artık ağdaki her (u, v) için h(u) ≤ h(v); yani hiçbir komşuya itilemiyor.
h(u) = 1 + min h(v), (u, v) artık ağda. Böylece en az bir itme mümkün olur.
İtme–Yeniden Etiketleme
İtme–Yeniden Etiketleme Örneği
| Düğüm | h | e |
|---|---|---|
| s | 0 | − |
| A | 0 | 0 |
| B | 0 | 0 |
| C | 0 | 0 |
| D | 0 | 0 |
| t | 0 | 0 |
Ağ verilsin (akış/kapasite). 6 düğüm var: |V| = 6.
| Düğüm | h | e |
|---|---|---|
| s | 6 | − |
| A | 0 | 13 |
| B | 0 | 10 |
| C | 0 | 0 |
| D | 0 | 0 |
| t | 0 | 0 |
Başlangıç: h(s) = 6; s → A ve s → B doldurulur. e(A) = 13, e(B) = 10.
| Düğüm | h | e |
|---|---|---|
| s | 6 | − |
| A | 0 | 13 |
| B | 1 | 10 |
| C | 0 | 0 |
| D | 0 | 0 |
| t | 0 | 0 |
B ele alınır. A ile aynı yükseklikte (0) olduğundan fazlasını A'ya gönderemez → relabel: h(B) = 1 + min(h(A) = 0, h(s) = 6) = 1.
| Düğüm | h | e |
|---|---|---|
| s | 6 | − |
| A | 0 | 16 |
| B | 1 | 7 |
| C | 0 | 0 |
| D | 0 | 0 |
| t | 0 | 0 |
B şimdi fazlasını A'ya itebilir: δ = min(10, 3) = 3. e(A) = 16, e(B) = 7.
| Düğüm | h | e |
|---|---|---|
| s | 6 | − |
| A | 1 | 16 |
| B | 1 | 7 |
| C | 0 | 0 |
| D | 0 | 0 |
| t | 0 | 0 |
A ele alınır. İtebileceği alçak komşu yok → relabel: h(A) = 1 + min(h(C) = 0, h(t) = 0, h(B) = 1, h(s) = 6) = 1.
| Düğüm | h | e |
|---|---|---|
| s | 6 | − |
| A | 1 | 3 |
| B | 1 | 7 |
| C | 0 | 6 |
| D | 0 | 0 |
| t | 0 | 7 |
A şimdi C'ye 6 ve hedefe 7 birim iter (iki kenar da doyar). e(A) = 3, e(C) = 6, hedefte 7.
| Düğüm | h | e |
|---|---|---|
| s | 6 | − |
| A | 1 | 3 |
| B | 1 | 7 |
| C | 1 | 6 |
| D | 0 | 0 |
| t | 0 | 7 |
C relabel edilir: h(C) = 1 + h(D) = 1. (C'nin 6 birim fazlası şimdilik bekler; aktif düğümler istenen sırada seçilebilir.)
| Düğüm | h | e |
|---|---|---|
| s | 6 | − |
| A | 2 | 3 |
| B | 1 | 7 |
| C | 1 | 6 |
| D | 0 | 0 |
| t | 0 | 7 |
A relabel edilir: artık ağda yalnızca B (h = 1) ve s (h = 6) kaldı → h(A) = 2.
| Düğüm | h | e |
|---|---|---|
| s | 6 | − |
| A | 2 | 0 |
| B | 1 | 10 |
| C | 1 | 6 |
| D | 0 | 0 |
| t | 0 | 7 |
A'nın yüksekliği B'den fazla: 3 birimi B'ye (B → A akışını iptal ederek) iter. e(A) = 0, e(B) = 10.
| Düğüm | h | e |
|---|---|---|
| s | 6 | − |
| A | 2 | 3 |
| B | 3 | 7 |
| C | 1 | 6 |
| D | 0 | 0 |
| t | 0 | 7 |
B'nin gidebileceği başka kenar yok → relabel h(B) = 3; 3 birimi tekrar A'ya iter. e(A) = 3, e(B) = 7.
| Düğüm | h | e |
|---|---|---|
| s | 6 | − |
| A | 6 | 3 |
| B | 7 | 0 |
| C | 1 | 6 |
| D | 0 | 0 |
| t | 0 | 7 |
A ile B arasında relabel–push sürer (h(A) = 4, h(B) = 5, h(A) = 6, h(B) = 7). B'nin yüksekliği kaynağı geçince: 3 birimi A'ya, 7 birimi kaynağa iter.
| Düğüm | h | e |
|---|---|---|
| s | 6 | − |
| A | 7 | 0 |
| B | 7 | 0 |
| C | 1 | 6 |
| D | 0 | 0 |
| t | 0 | 7 |
A relabel: h(A) = 1 + min(h(s) = 6, h(B) = 7) = 7; 3 birimi kaynağa iter. Artık A ve B'de fazla yok.
| Düğüm | h | e |
|---|---|---|
| s | 6 | − |
| A | 7 | 0 |
| B | 7 | 0 |
| C | 1 | 0 |
| D | 0 | 6 |
| t | 0 | 7 |
C ele alınır: fazlası 6 birim D'ye itilir (h(C) = 1 > h(D) = 0).
| Düğüm | h | e |
|---|---|---|
| s | 6 | − |
| A | 7 | 0 |
| B | 7 | 0 |
| C | 1 | 0 |
| D | 1 | 1 |
| t | 0 | 12 |
D relabel (h = 1) ve hedefe 5 birim iter. D'de hâlâ 1 birim fazla var.
| Düğüm | h | e |
|---|---|---|
| s | 6 | − |
| A | 7 | 0 |
| B | 7 | 0 |
| C | 1 | 1 |
| D | 2 | 0 |
| t | 0 | 12 |
D relabel: hedef kenarı dolu, tek komşu C (h = 1) → h(D) = 2. 1 birimi C'ye iter.
| Düğüm | h | e |
|---|---|---|
| s | 6 | − |
| A | 7 | 1 |
| B | 7 | 0 |
| C | 8 | 0 |
| D | 8 | 0 |
| t | 0 | 12 |
C'nin yüksekliği A'yı (7) geçene kadar C–D arasında relabel–push sürer (C: 3, 5, 7, 8; D: 4, 6, 8). C 1 birim fazlayı A'ya iter.
| Düğüm | h | e |
|---|---|---|
| s | 6 | − |
| A | 7 | 0 |
| B | 7 | 0 |
| C | 8 | 0 |
| D | 8 | 0 |
| t | 0 | 12 |
A 1 birim fazlayı kaynağa iter. Aktif düğüm kalmadı: maksimum akış = hedefe giren = 7 + 5 = 12 = kaynaktan çıkan 9 + 3.
İtme–Yeniden Etiketleme
İtme–Yeniden Etiketleme: Sözde Kod
PUSH_RELABEL(G, kaynak, hedef): yükseklik[kaynak] = |V| yükseklik[v] = 0, v ≠ kaynak aşırı[v] = 0, her v için akış[e] = 0, her kenar e için // ön akış: kaynak kenarlarını doldur her bir (kaynak, v) ∈ E için: c = kapasite[kaynak, v] akış[kaynak, v] = c akış[v, kaynak] = −c aşırı[v] = c aşırı[kaynak] −= c
döngü aşırı[u] > 0 olan u varken: // u ≠ kaynak, u ≠ hedef eğer kalan(u,v) > 0 ve yükseklik[u] == yükseklik[v]+1: // İtme delta = min(aşırı[u], kalan(u,v)) akış[u,v] += delta akış[v,u] −= delta aşırı[u] −= delta aşırı[v] += delta değilse: // Yeniden etiketleme m = ∞ her bir (u, v) için G[u] içinde: eğer akış[u,v] < kapasite[u,v]: m = min(m, yükseklik[v]) yükseklik[u] = m + 1 döndür aşırı[hedef] // kalan = kapasite − akış
İtme–Yeniden Etiketleme
Java: push ve relabel
boolean it(Dugum d) { for (Kenar k : d.kenarlar) { if (d.yukseklik > k.hedef.yukseklik && k.akis != k.kapasite) { int akis = Math.min( k.kapasite - k.akis, d.asiri); d.asiri -= akis; k.hedef.asiri += akis; k.akis += akis; tersKenariGuncelle(k, akis); return true; // itme yapıldı } } return false; }
void etiketle(Dugum d) { int minY = Integer.MAX_VALUE; for (Kenar k : d.kenarlar) { // yalnızca artık ağdaki kenarlar if (k.akis != k.kapasite && k.hedef.yukseklik < minY) { minY = k.hedef.yukseklik; d.yukseklik = minY + 1; } } }
Ana döngü: aktif düğüm varken if (!it(d)) etiketle(d); — sonuç hedefin aşırı akışıdır. Ters kenarlar negatif akışla tutulur (kapasite 0).
İtme–Yeniden Etiketleme
İtme–Yeniden Etiketleme: Karmaşıklık
| İşlem | Toplam sayı | Gerekçe |
|---|---|---|
| Yeniden etiketleme | O(V²) | her düğümün yüksekliği ≤ 2|V| − 1 ve yalnızca artar |
| Doyuran itme | O(V · E) | bir kenar yeniden doyabilmek için iki uçtaki yükseklik ≥ 2 artmalı |
| Doyurmayan itme | O(V² · E) | potansiyel fonksiyonu Σ h(v) (aktif v) ile |
- Genel algoritma en kötü durumda O(V² · E); bir relabel en fazla derece kadar kenarı tarar (toplam O(V · E)).
- FIFO seçimiyle O(V³), en yüksek etiket seçimiyle O(V² · √E).
- Pratikte gap ve global relabeling sezgileriyle en hızlı genel amaçlı maksimum akış yöntemlerindendir.
Karşılaştırma ve Uygulamalar
Algoritmaların Karşılaştırması
| Algoritma | Temel işlem | Süre | Örnek ağda (|f*| = 19) |
|---|---|---|---|
| Ford-Fulkerson | herhangi bir artış yolu (DFS) | O(E · |f*|) | 5 artış yolu (Örnek 2'deki sıra) |
| Edmonds-Karp | en kısa artış yolu (BFS) | O(V · E²) | 4 artış yolu, 5 BFS |
| Dinic | seviye çizgesi + engelleyici akış | O(V² · E) | 2 faz, 3 BFS |
| İtme–yeniden etiketleme | yerel push / relabel | O(V² · E), FIFO O(V³) | yol aramaz |
Karşılaştırma ve Uygulamalar
Uygulamalar
- İki parçalı eşleme: çalışan–iş, öğrenci–proje atama (birim kapasiteler).
- Kenar ayrık yollar (Menger): birim kapasiteli maksimum akış.
- Görüntü bölütleme: ön plan/arka plan ayrımı minimum kesimle.
- Ağ güvenilirliği: ağı bölmek için kesilmesi gereken en küçük toplam kapasite.
- Ulaşım ve lojistik: boru hattı, trafik, havayolu çizelgeleme, proje seçimi.
Maksimum akış 3 = en büyük eşleme: a₁–b₂, a₂–b₁, a₃–b₃.
Özet
Özet
| Konu | Hatırlanacaklar |
|---|---|
| Akış | 0 ≤ f ≤ c; ara düğümlerde giren = çıkan; |f| = s'ten çıkan net akış |
| Artık ağ | ileri c − f, ters f; artış yolu Gf'de s → t yolu, darboğaz = en küçük cf |
| Kesim | c(S, T) = S'den T'ye kapasiteler; maks akış = min kesim; S = son Gf'de s'ten ulaşılanlar |
| Ford-Fulkerson | herhangi bir artış yolu; O(E · |f*|), tam sayı kapasitelerde sonlanır |
| Edmonds-Karp | BFS ile en kısa yol; O(V · E²) |
| Dinic | BFS seviye çizgesi + DFS engelleyici akış; ≤ V faz; O(V² · E) |
| Push–relabel | h(s) = |V|, ön akış; yüksekten alçağa it, itemezsen etiketle; O(V² · E) |
Özet
Etkileşimli Simülatörler
Özet
Sık Yapılan Hatalar
Özet
Örnek Problemler
- |f*| için kaynak ve hedef tarafından üst sınır nedir?
- Edmonds-Karp hangi artış yollarını sırayla bulur? (BFS komşuları alfabetik dener.)
- Maksimum akış ve bir minimum kesim nedir?
- Son durumda cf(s, a) ve cf(c, d) kaçtır?
- Push–relabel başlangıcında h ve e değerleri nedir?
Cevapları göster
- s'ten çıkan 5 + 4 = 9, t'ye giren 4 + 3 = 7 → |f*| ≤ 7.
- s→a→c→t (3), s→b→d→t (3), s→b→d→c→t (1). İlk ikisi 3, sonuncusu 4 kenarlı.
- |f*| = 7; S = {s, a, b, c, d}: c→t 4 + d→t 3 = 7 (son artık ağda t dışındaki her düğüme ulaşılır).
- cf(s, a) = 5 − 3 = 2; cf(c, d) = f(d, c) = 1 (ters kenar).
- h(s) = 6, diğerleri 0; e(a) = 5, e(b) = 4, diğerleri 0 (s → a ve s → b doldurulur).
Özet
Alıştırmalar
- Bir kenarın kapasitesi 1 artırılırsa maksimum akış en fazla ne kadar artar?
- Tüm kapasiteler k ile çarpılırsa maksimum akış ve minimum kesim nasıl değişir?
- Düğüm kapasiteleri c(v) varsa problem nasıl modellenir?
- Son artık ağdan minimum kesimi bulan bir Java metodu yazın.
- "Kötü durum" ağında DFS'li Ford-Fulkerson ve Edmonds-Karp en fazla kaç artış yapar?
- s'ten t'ye kenar ayrık yol sayısı maksimum akışla nasıl bulunur?
Cevaplar / ipuçları
- En fazla 1 (her kesimin kapasitesi en çok 1 artar); kenar bir min kesimde değilse hiç artmayabilir.
- Akış ve tüm kesim kapasiteleri k katına çıkar; aynı kesim minimum kalır.
- Her v'yi vg → vç kenarına (kapasite c(v)) bölün; gelenler vg'ye, gidenler vç'den bağlanır.
- Son artık ağda s'ten BFS; ziyaret edilen u, edilmeyen v olan özgün (u, v) kenarları kesimdir.
- DFS'li Ford-Fulkerson en kötü 2000, Edmonds-Karp 2 artış.
- Her kenara kapasite 1 verin; maksimum akış = kenar ayrık yol sayısı (Menger).
Özet
Kaynaklar
- Cormen, Leiserson, Rivest ve Stein, Introduction to Algorithms, 4. baskı, MIT Press, 2022 (Bölüm 24: Maximum Flow).
- Kleinberg ve Tardos, Algorithm Design, Pearson, 2006 (Bölüm 7: Network Flow).
- Sedgewick ve Wayne, Algorithms, 4. baskı, 2011 (Bölüm 6.4: Maximum Flow).
- L. R. Ford ve D. R. Fulkerson, "Maximal Flow Through a Network", Canadian Journal of Mathematics 8, 1956.
- J. Edmonds ve R. M. Karp, "Theoretical Improvements in Algorithmic Efficiency for Network Flow Problems", Journal of the ACM 19(2), 1972.
- E. A. Dinic, "Algorithm for Solution of a Problem of Maximum Flow in Networks with Power Estimation", Soviet Math. Doklady 11, 1970.
- A. V. Goldberg ve R. E. Tarjan, "A New Approach to the Maximum-Flow Problem", Journal of the ACM 35(4), 1988.