Differences

This shows you the differences between two versions of the page.

Link to this comparison view

pa:laboratoare:laborator-11 [2022/05/17 14:26]
darius.neatu [Laborator 11: Arbori minimi de acoperire]
pa:laboratoare:laborator-11 [2025/02/24 21:59] (current)
darius.neatu [Laborator 11: Flux maxim în rețele de transport]
Line 1: Line 1:
-====== ​Laborator 11Arbori minimi ​de acoperire ​======+====== ​TutorialFlux maxim în rețele ​de transport ​======
  
  
-{{:​pa:​new_pa:​partners:​bitdefender-logo.png?​190 |}} +{{:​pa:​new_pa:​partners:​bitdefender-logo.png?​190 |}} Bitdefender provides cybersecurity solutions with leading security efficacy, performance and ease of use to small and medium businesses, mid-market enterprises and consumers. Guided by a vision to be the world’s most trusted cybersecurity solutions provider, Bitdefender is committed to defending organizations and individuals around the globe against cyberattacks to transform and improve their digital experience.
- +
-\\ Bitdefender provides cybersecurity solutions with leading security efficacy, performance and ease of use to small and medium businesses, mid-market enterprises and consumers. Guided by a vision to be the world’s most trusted cybersecurity solutions provider, Bitdefender is committed to defending organizations and individuals around the globe against cyberattacks to transform and improve their digital experience.+
  
 ===== Obiective laborator ===== ===== Obiective laborator =====
  
-În acest laborator vom introduce contextul pentru **minimum spanning tree problem** și vom studia algoritmi care pot rezolva această problemă.+În acest laborator vom introduce contextul pentru **maximum flow problem** și vom studia algoritmi care pot rezolva această problemă.
  
 +  * Prezentarea noțiunii de rețea de transport și a termenilor asociați.
   * Prezentarea problemei (diverse variante).   * Prezentarea problemei (diverse variante).
-  * Prezentarea algoritmilor pentru calcul ​unui arbore minim de acoperire.+  * Prezentarea algoritmilor pentru calcul unui flux maxim într-o rețea ​de transport.
  
-===== Importanţă – aplicaţii practice ​=====+===== Maximum flow problem ​=====
  
-Algoritmii pentru determinarea unor arbori (minimi) de acoperire au multiple aplicații practiceCâteva exemple de aplicații sunt:+Vă rugăm să parcugeți [[https://​ocw.cs.pub.ro/​courses/​pa/​laboratoare/​maximum-flow-problem|Maximum flow problem]] pentru a vă familiariza cu contextul, problema și notațiile folosite.
  
-  * Designul circuitelor electronice: +Concepte necesare:
-    * Maparea pinilor pe un circuit astfel încât să se foloseacă o lungime minimă de traseu. +
-  * Rețele de calculatoare:​ +
-    * Interconectarea mai multor stații în rețea, cu un cost / latență redus(ă). +
-    * STP Protocol: protocol care previne apariția buclelor într-un LAN. +
-  * Segmentarea imaginilor:​ +
-    * Împărțirea unei imagini în regiuni cu proprietăți asemănătoare. +
-    * [[https://​en.wikipedia.org/​wiki/​Minimum_spanning_tree-based_segmentation | Minimum spanning tree-based segmentation]]. +
-  * Clustering:​ +
-    * [[https://​en.wikipedia.org/​wiki/​Single-linkage_clustering|Single-linkage clustering]].+
  
-===== Minimum spanning tree problem =====+  * **rețea de transport** / **flow network** 
 +  * **flux** / **flow** 
 +  * **flux maxim** / **maximum flow** 
 +  * **capacitate reziduală** / **residual capacity** 
 +  * **rețea reziduală** / **residual network** 
 +  * **drum de ameliorare** / **augmenting path** 
 +  * **capacitate reziduală a unui drum de ameliorare** / **residual capacity of augmenting path**
  
-Puteți consulta capitolul **Minimum Spanning Trees** din **Introduction to Algorithms** [0] pentru mai multe definiții formale. Această secțiune sumarizează principalele notații folosite în laboratoarele de PA.+===== Algoritmi =====
  
-Cele mai uzuale notații din laboratoarele de grafuri sunt descrise în [[https://​ocw.cs.pub.ro/​courses/​pa/​skel_graph|Precizări laboratoare 07-12]] (ex. $n$, $m$, $adj$, $adj\_trans$,​ $(x, y)$, etc).+Pentru **maximum flow problem** există ​mai mulți algoritmi, dintre care amintim:
  
-Vom adăuga alte notații în continuarea celor introduse anterior.+  * **Ford–Fulkerson**:​ Este o metodă (nu un algorithm concret), a cărui complexitate este direct proporțională cu valoarea fluxului maxim - $O(m * f_{max})$. [1] 
 +  * **Edmonds-Karp**:​ Este o implementare concretă / optimizare a metodei anterioare - $O(n * m^2)$. [2] 
 +  * **Dinic**: O optimizare bazată pe construirea progresivă a unui graf folosind BFS. În fiecare etapă se poate afla fluxul ​în $O(n * m)$, iar în total sunt $n -1$ etape. Complexitate finală $O(n^2 * m)$. [3] 
 +  * **Preflow-push / push-relabel**:​ Un algoritm care poate fi optimizat pentru a obține complexitate $O(n^3)$ sau $O(n^2 * m)$.
  
 +Puteți consulta capitolul **Maximum Flow** din **Introduction to Algorithms** [0] pentru mai multe detalii despre acești algoritmi.
  
 +În acest laborator vom studia și analiza **metoda Ford-Fulkerson**,​ apoi vom implementa **algoritmul Edmonds-Karp** (care este o optimizare peste Ford-Fulkerson).
  
-> **Arbore de acoperire** / **spanning tree**: Într-un graf neorientat **conex** $G (V, E)$, cu funcția de cost $w: E -> W$, numim **un arbore de acoperire** un subgraf al lui **G** cu număr minim de muchii și care interconectează toate nodurile din graf.+===== Ford-Fulkerson =====
  
-<spoiler Exemplu>+Algoritmul lui [[https://​en.wikipedia.org/​wiki/​L._R._Ford_Jr.|Lester Randolph **Ford** Jr. ]] și [[https://​en.wikipedia.org/​wiki/​D._R._Fulkerson|Delbert Ray **Fulkerson**]],​ cunoscut ca și algoritmul / metoda [[https://​en.wikipedia.org/​wiki/​Ford%E2%80%93Fulkerson_algorithm|Ford-Fulkerson]] este un algoritm **greedy** pentru calcularea fluxului maxim într-o rețea de transport.
  
-{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab11-spanning-tree-example.png?​800| Arbore ​de acoperire }}+Atât timp cât în graf încă există drumuri de ameliorare (augmenting paths), înseamnă că mai putem adauga flux in rețea prin acele drumuriLa fiecare pas alegem un drum de ameliorare, prin care adăugăm cantitatea maximă posibilă de flux.
  
-În exemplul atașat, avem un graf **neorientat** și **conex** cu următoare configurație:​+<spoiler Ford-Fulkerson - Pseudocod>​
  
-  * ''%%n 5%%''​''​%%m ​5%%''​ +<code cpp> 
-  * Pentru acest exemplu, avem **3** arbori de acoperire evidențiați în aceeași figură ​(rândul 2).+// apply Ford-Fulkerson's algorithm for computing maximum flow in network G 
 +// 
 +// nodes     list of all nodes from G 
 +// edges     = the list of all edges in G 
 +//             ​example:​ edge (nodeneigh, capacity) 
 +// S         source in network G 
 +// T         = sink in network G 
 +// 
 +// returns: maxFlow (maximum flow in G from S to T) 
 +// 
 +Ford-Fulkerson(G=(nodes,​ edges), S, T) { 
 +  ​// STEP 0: initialize 
 +  // flow in network 
 +  foreach ​((u, vin edges) { 
 +      f[u][v] = 0; 
 +  } 
 +  // * maximum total flow 
 +  totalFlow = 0;
  
-Observație:​ Un graf **conex** are cel puțin un arbore de acoperire!+  while (exists augmenting path p in G) { 
 +      augment flow f along p; 
 +      update totalFlow;​ 
 +  }
  
-</​spoiler>​ \\+  return totalFlow;​ 
 +}
  
 +// Usage example:
 +maxFlow = Ford-Fulkerson(G=(nodes,​ edges), S, T);
 +// Use maxFlow.
 +</​code>​
 +După cum se observă, metoda Ford-Fulkerson descrie care este abordarea generală, însă nu ne spune concret cum să găsim drumurile de ameliorare și cum să creștem fluxul pe ele.
  
 +Putem să alegem drumurile la întâmplare. Vom obține un rezultat corect (nu vom demonstra).
  
- +Din punct de vedere al performanțeiîn cel mai rău caz, pornind o parcurgere oarecare vom găsi în $O(n + m)$ un drum de ameliorare pe care vom putea crește cu exact o unitate cantitatea de fluxAceastă abordare duce la complexitate de $O((n + m) |f_{max}|)adică un timp de execuție proporțional cu cantitea de flux pompată. Abordarea este ineficientă.
-> **Pădure ​de arbori de acoperire** / **spanning tree forest**: Se definește similardar pentru graf neorientat **neconex**. Fiecare componentă conexă are un **arbore ​de acoperire** / **spanning tree**Spunem că acești arbori formează împreună o **pădure** ​(**forest**)+
- +
-<spoiler Exemplu>​ +
- +
-{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab11-spanning-forest-example.png?​800| Pădure de arbori de acoperire }} +
- +
-În exemplul atașat, avem un graf **neorientat** și **neconex** cu următoare configurație+
- +
-  * ''​%%n = 6%%'',​ ''​%%m = 6%%''​ +
-  * În figura următoare evidențăm toate pădurile de arbori de acoperire (6 pe acest exemplu). +
- +
-Observație:​ Un graf **neconex** are cel puțin o pădure de arbori de acoperire!+
  
 </​spoiler>​ \\ </​spoiler>​ \\
  
  
 +În practică **NU** vom folosi direct metoda Ford-Fulkerson,​ ci **vom folosi algoritmul Edmonds-Karp**,​ pe care îl vom analiza în secțiunea următoare.
  
 +===== Edmonds-Karp =====
  
-**Arbore minim de acoperire** (**AMA**)/ **minimum spanning tree** (**MST**)Un arbore ​de acoperire este **ȘI** arbore minim de acoperire dacă costul total al muchiilor din arbore este minim posibil. Analog se poate defini ​și noțiunea de **pădure de arbori minimi ​de acoperire** (pornind ​de la cea de pădure de arbori de acoperire).+Algoritmul lui [[https://​en.wikipedia.org/​wiki/​Jack_Edmonds|Jack ​**Edmonds**]] și [[https://​en.wikipedia.org/​wiki/​Richard_M._Karp|Richard M. **Karp**]], cunoscut ca și algoritmul [[https://​en.wikipedia.org/​wiki/​Edmonds%E2%80%93Karp_algorithm|Edmonds-Karp]] reprezintă o implementare concretă a metodei Ford-Fulkerson. Acest algoritm rezolvă problema drumurilor ​de ameliorare folosind ​**BFS** - adică drumul cel mai scurt (ca și număde arce), pe care se poate merge ținând cont de restricția ​de capacitate ($f(u, v) \lt c(u, v)$).
  
-<spoiler Exemplu>+==== Edmonds-Karp - pseudocod ====
  
-{{https://ocw.cs.pub.ro/courses/_media/pa/new_pa/lab11-minimum-spanning-tree-example.png?​900| Exemplu funcție de cost pentru graf orientat}}+<code cpp> 
 +// apply Edmonds-Karp'​s algorithm for computing maximum flow in network G 
 +// 
 +// nodes     = list of all nodes from G 
 +// adj[node] = the adjacency list of node 
 +//             ​example:​ adj[node] = {..., neigh, ...} => edge (node, neigh) 
 +// S         = source in network G 
 +// T         = sink in network G 
 +// f         = flow in network G 
 +// c         = capacity in network G 
 +// 
 +// returns: maxFlow (maximum flow in G from S to T) 
 +// 
 +Edmonds-Karp(G=(nodes,​ adj), S, T) { 
 +  // STEP 0: initialize 
 +  // * flow in network 
 +  foreach (u in nodes) { 
 +      foreach(v in nodes) { 
 +          f[u][v] = 0; 
 +      ​} 
 +  ​} 
 +  // * maximum total flow 
 +  totalFlow = 0;
  
-În exemplul atașat, avem un graf **neorientat** și **conex** cu următoare configurație:+  // STEP 1: Start BFS for finding augmenting path(s). 
 +  while (BFS finds at least one augmenting path in G) { 
 +    // STEP 2.1Compute residual capacity of augmenting path. 
 +    rc = +oo; // residual capacity 
 +    foreach ((u, v) in path) { 
 +        rc = min(rc, c[ u ][ v ] - f[ u ][ v ]); 
 +    }
  
-  * ''​%%n = 5%%''​''​%%m ​5%%''​ +    // STEP 2.2: Update flow on path with rc. 
-  * În figura următoare evidențăm toți arborii de acoperire. Doar 2 dintre aceștia au cost minim **5**.+    foreach ((uv) in path) { 
 +        f[ u ][ v ] +rc;      // Increase on edge (u, v). 
 +        f[ v ][ u ] -= rc;      // Decrease on edge (v, u). 
 +    }
  
-ObservațieUn graf **conex** are cel puțin un arbore minim de acoperire!+    // STEP 2.3Update total flow. 
 +    totalFlow += rc; 
 +  }
  
-</​spoiler>​ \\+  return totalFlow;​ 
 +}
  
 +// Usage example:
 +maxFlow = Edmonds-Karp(G=(nodes,​ adj), S, T);
 +// Use maxFlow.
 +</​code>​
 +Algoritmul Edmons-Karp pornește o parcurgere BFS la fiecare pas. Pentru un drum de ameliorare găsit, calculează capacitatea reziduală a acestuia, apoi crește corespunzător fluxul pe acesta.
  
-===== Algoritmi =====+La **fiecare** pas, atunci când se crește fluxul pe un arc, se scade fluxul pe arcul invers (vom demonstra într-o subsecțiune viitoare necesitatea acestui pas pentru corectitudinea algoritmului).
  
-Pentru **minimum spanning tree problem** ​există ​mai mulți algoritmi, dintre care amintim:+Algoritmul se oprește când nu mai există ​drumuri de ameliorare. Fluxul pompat până atunci în rețea este maxim.
  
-  * **Kruskal**:​ abordare greedy care se bazează pe sortarea muchiile după cost. Mai multe detalii în **Introduction to Algorithms** [0]. +==== Exemple ====
-  * **Prim**: abordare greedy care extinde arborele căutat pornind de la o rădăcină (nod ales oarecare). Mai multe detalii în **Introduction to Algorithms** [0]. +
-  * **Karger, Klein & Tarjan**: algoritm randomizat cu complexitate liniară (mai bună decât Kruskal și Prim). Mai multe detalii în **A Randomized Linear-Time Algorithm to Find Minimum Spanning Trees** [1]. +
-  * **Bernard Chazelle**: algoritm determinist cu o complexitate liniară (cu complexitate mai bună decât soluția anterioară). Mai multe detalii în **A Minimum Spanning Tree Algorithm with Inverse Ackermann Type Complexity** [2].+
  
-În acest laborator vom studia și vom implementa **algoritmul lui Kruskal**. De asemenea, oferim ca studiu de caz pentru acasă, un material pentru algoritmul lui Prim.+=== Edmonds-Karp:​ exemplu rezultat ===
  
-===== Kruskal =====+{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab12-edmonds-karp-example.png?​512| Exemplu Edmonds-Karp}}
  
-Algoritmul lui [[https://​en.wikipedia.org/​wiki/​Joseph_Kruskal|Joseph Bernard ​**Kruskal**]] (**Kruskal’s algorithm**) rezolvă **minimum spanning tree problem** în grafuri neorientate **G = (V, E)** cu costurile muchiilor **oarecare** aplicând o strategie greedy foarte simplă: muchiile de cost minim probabil fac parte din MST!+Fluxul maxim calculat cu Edmonds-Karp pentru rețeaua atașată este: **2**.
  
-==== Kruskal - Pseudocod ====+Drumurile de ameliorare folosite sunt:
  
 +  * $1 - 2 - 4 - 6$ (capacitate reziduală **+1**)
 +  * $1 - 3 - 5 - 6$ (capacitate reziduală **+1**)
  
-<​note>​+=== Exemplu Edmonds-Karp:​ exemplu simplu pas cu pas ===
  
-Algoritmul lui Kruskal folosește o structură de date care suportă 2 operații:+În această secțiune vom exemplifica ​de ce doar simpla incrementare a fluxului pe arcele directe nu este suficientă pentru a găsi fluxul maxim în rețea. Următoarea secțiunea conține soluția.
  
-  * În ce mulțime este elementul **x**? +<spoiler Exemplu>
-  * Să se reunească mulțimile din care fac parte elementele **x** și **y**.+
  
-Se pot folosi mai multe structuri ​de date. Pentru o implementare eficientă, alegem **DisjointSet**. Vă recomandăm să parcurgeți [[https://​infoarena.ro/​problema/​disjoint | DisjointSet]]. **ATENȚIE!** Scheletul de laborator oferă o astfel de implementare.+Pornim ​de la rețeaua din figura următoare:
  
 +{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab12-edmonds-karp-example.png?​512| Exemplu Edmonds-Karp}}
  
-</​note>​+  * ''​%%n = 6%%'',​ ''​%%m = 7%%''​ 
 +  * Sursa este ''​%%S = 1%%'',​ iar terminalul este ''​%%T = 6%%''​. 
 +  * Funcția de capacitate ''​%%c%%''​ are valorile din figură.
  
-<code cpp> +Dacă rulăm Edmonds-Karpo posibilă succesiune de stări pentru rețea este cea din figura următoare:
-// apply Kruskal'​s algorithm for computing a Minimum Spanning Tree (MST). +
-// +
-// nodes     = list of all nodes from G +
-// adj[node] = the adjacency list of node +
-//             ​example:​ adj[node] = {...(neigh, weight) ...} => edge (node, neigh, weight) +
-// +
-// returnscost, mst +
-//          cost = the cost of the MST +
-//          mst = the actual set of edges in MST +
-// +
-Kruskal(G=(nodes,​ edges)) { +
-  // STEP 0: initialize results +
-  cost = 0; // cost of the built MST +
-  mst = {}; // the actual set of edges (u, v) in the built MST+
  
-  ​// STEP 1: initialize disjoint set data structure +{{https://ocw.cs.pub.ro/​courses/​_media/​pa/new_pa/lab12-edmonds-karp-basic-example-01.png?​512| Exemplu Edmonds-Karp}}
-  ​// (a tree is created for each node) +
-  disjointset = new DisjointSet(nodes);​+
  
-  ​// STEP 2: +  ​* Inițial fluxul în rețea este zero. 
-  ​sort(edges, compare=nondecreasing order by weight);+  * Se vor găsi drumuri de ameliorare $1 - 2 - 4 - 6$ și $ 1 - 3 - 5 - 6$. Pe fiecare drum se pompează câte o unitate de flux. 
 +  ​* În rețeaua reziduală obținută se observă că nu se mai poate ajunge de la S la T. 
 +  * Prin urmare fluxul obținut este maxim și are valoare $1 + 1 2$.
  
-  // STEP 3: Add edge by edge to MST if no cycles are created. 
-  foreach ( (u, v, w) in edges ) { 
-    // STEP 3.1: Check if u anv v are in different trees. 
-    if (disjointset.setOf(u) != disjointset.setOf(v)) { 
-      // STEP 3.2: Merge these 2 trees (no cycles created). 
-      disjointset.union(u,​ v); 
  
-      // STEP 3.3: Extend MST with the current edge. +<note warning>
-      cost += w; +
-      mst += { (u, v) }; +
-    } +
-  }+
  
-  return costmst; +NU avem control asupra drumurilor găsite de BFSadică ordinea în care acestea vor fi alese, ci știm doar că se va alege cele mai scurte drumuri.
-}+
  
-// Usage example: +</note>
-cost, mst = Kruskal(G=(nodes,​ edges)); +
-// 1. Use cost of MST. +
-// 2. Use edges of MST (e.g. actually build the tree). +
-</code> +
-Algoritmul lui Kruskal pornește cu structura de date Păduri de mulțimi disjuncte inițializată cu n noduri izolate. Muchiile sunt sortate crescător după cost și se încearcă adăugarea lor, rând pe rând la pădurea de arbori.+
  
-  * Dacă o muchie $(u, v)$ are capetele în 2 arbori diferiți, atunci prin adăugarea acesteia, nu vom crea ciclu, ci vom crea un arbore mai mare (din cei 2 mici reuniți). +O altă succesiune posibilă școrectă de stări pentru rețea ​se găsește în figura următoare:
-  * Altfel, nu putem adăuga muchia, deoarece am crea un ciclu. Această muchie ​se ignoră.+
  
-==== Exemple ====+{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab12-edmonds-karp-basic-example-02.png?​512| Exemplu Edmonds-Karp}}
  
-=== Exemplu Kruskal ===+  * Inițial fluxul în rețea este zero. 
 +  * Se găsește drumul de ameliorare $1 - 2 - 5 - 6$ (tot de lungime 3 arce) cu capacitatea reziduală 1. Se pompează această unitate de flux. 
 +  * În rețeaua reziduală obținută se observă că nu se mai poate ajunge de la S la T. 
 +  * Fluxul total obținut este **1**, care **NU** este maxim.
  
-{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab11-kruskal.png?​900| Exemplu Kruskal}} +Prin urmare ajungem într-un punct în care algoritmul a greșit ​și nu mai poate pompa flux în plusParcugeți următorul exemplu pentru a vedea concret cum se rezolvă această problemă.
- +
-Un **MST** găsit cu algoritmul lui Kruskal este: ${ (1, 2); (2, 4); (2, 3); (3, 5)}$ de cost **5**. +
- +
-<spoiler Explicație pas cu pas> +
- +
-În exemplul atașat, avem un graf **neorientat** cu următoare configurație:​ +
- +
-  * ''​%%n = 5%%'',​ ''​%%m = 5%%''​ +
-  * Funcția de cost ''​%%w%%''​ are valorile menționate pe muchii. +
-  * Muchiile sortate după cost sunt: +
-    * $(3, 5)$ de cost $-1$ +
-    * $(2, 4)$ de cost $1$ +
-    * $(2, 3)$ de cost $2$ +
-    * $(2, 5)$ de cost $2$ +
-    * $(1, 2)$ de cost $3$ +
-  * Explicație pas cu pas +
-    * STEP 0: Inițializăm pădurile de mulțimi disjuncte - pornind cu n arbori (noduri izolate). +
-    * STEP 1: Muchia cu costul cel mai mic care e nefolosită este $(3, 5)$ de cost -1. Se adaugă la **MST**. Se reunesc 2 arbori. +
-    * STEP 2: Muchia cu costul cel mai mic care e nefolosită este $(2, 4)$ de cost 1. Se adaugă la **MST**. Se reunesc 2 arbori. +
-    * STEP 3: Muchia cu costul cel mai mic care e nefolosită este $(2, 3)$ de cost 2. Se adaugă la **MST**. Se reunesc 2 arbori. +
-      * Observație:​ În acest pas am fi putut alege muchia $(2, 5)$ cu același cost. +
-    * STEP 4: +
-      * Muchia $(2, 5)$ nu va fi folosită deoarece are capetele ​în același arbore. +
-      * Muchia cu costul cel mai mic care e nefolosită este $(1, 2)$ de cost 3. Se adaugă la **MST**. Se reunesc 2 arbori. +
-    * Avem un arbore. STOP. +
- +
-Observație: În STEP 3 pas am fi putut alege muchia $(2, 5)$ cu acelașcost. Obțineam un MST care diferea printr-o singură muchie ({${ (1, 2); (2, 5); (2, 3); (3, 5)}}$), dar cu același cost **5**.+
  
 </​spoiler>​ \\ </​spoiler>​ \\
  
  
-==== Complexitate ====+=== Exemplu Edmonds-Karp:​ exemplu detaliat cu flux pe arce inverse ​===
  
-  * **complexitate temporală**: $T = O(m * log m)\ sau\ O(|E| * log |E|)$ +În această secțiune vom arăta ce face algoritmul pas cu pas și vom observa necesitatea și corectitudinea scăderii fluxului pe arcele inverse.
-  * **complexitate spațială** : $S = O(n)$+
  
-<spoiler Detalii (analiză + optimizări)>​+Pornim de la rețeaua din figura următoare:
  
-  * **complexitate temporală**:​ +{{https://ocw.cs.pub.ro/courses/_media/​pa/​new_pa/​lab12-edmonds-karp-negative-flow-01.png?512| Exemplu Edmonds-Karp}}
-    * Se sortează cele **m** muchii în ordine crescătoare după cost - $O(m log m)$. +
-    * Se folosesc **n** operații pe **DisjointSet** - $O(n log_{*}n)$, care poate fi aproximat cu $O(n)$ (demonstrație pe pagina **DisjointSet**). +
-      * ATENȚIE! Complexitatea conține funcția logaritm iterat, nu logaritm ([[https://en.wikipedia.org/wiki/Iterated_logarithm | Iterated logarithm]]),​ care pentru valorile uzuale din problemele de algoritmică poate fi aproximată cu o constantă foarte mică. +
-  * **complexitate spațială** : Se ține o structură de date **DisjointSet** cu maximum **n** noduri.+
  
-</​spoiler>​ \\+  * ''​%%n = 6%%'',​ ''​%%m = 7%%''​ 
 +  * Sursa este ''​%%S = 1%%'',​ iar terminalul este ''​%%T = 6%%''​. 
 +  * Funcția de capacitate ''​%%c%%''​ are valorile din figură. 
 +  * Observăm că pentru fiecare arc $(u, v)$ am dus un arc imaginar $(v, u)$ de capacitate **0**. Aceste arce fictive reprezintă cheia funcționării algoritmului Edmonds-Karp:​ de fiecare dată când vom pompa (adăuga) flux pe un arc $(u, v)$, vom scădea cu aceeași valoare fluxul pe arcul $(v, u)$.
  
 +Să vedem în continuare la ce ne ajută dacă alegem din nou, ghinionist, drumul de amelioarare $1 - 2 - 5 - 6$ și dacă de data aceasta putem corecta. Vom face acțiunile marcate în figura următoare:
  
-===== [Studiu de caz] Prim =====+{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab12-edmonds-karp-negative-flow-02.png?​512| Exemplu Edmonds-Karp}}
  
-Algoritmul lui [[https://​en.wikipedia.org/​wiki/​Robert_C._Prim|Robert C **Prim**]] (**Prim’s algorithm**) rezolvă **minimum spanning tree problem** în grafuri neorientate **G = (V, E)** cu costurile muchiilor **oarecare** aplicând o strategie constructivă greedyasemănătoare cu algoritmul lui Dijkstra: ​merge pe muchiile de cost minimdin aproape în aproapeconsiderând o sursă inițial aleasă aleator ​și extinzând MST-ul curent ​cu muchia de cost minim neadăugată încă la arbore.+  * Se pompează o unitate de flux pe drumul $1 - 2 - 5 - 6$Prin urmare pe arcele marcate cu verde creștem fluxul cu **+1** unități de flux, iar pe arcele marcate cu roșu scădem ​(**-1** unități de flux)
 +  ​La pasul următorse pornește BFS din 1 și se înaintează folosind aceeași condiție - se poate merge pe o muchie $(uv)$ dacă $f(uv) \lt c(u, v)$. De data aceasta, această condiție se îndeplinește ​și pentru unele dintre muchiile inverse ​cele cu **-1/0**.
  
-==== Prim Pseudocod ====+Vom face acțiunile marcate în figura următoare:
  
-<spoiler Prim Pseudocod>​+{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab12-edmonds-karp-negative-flow-03.png?​512| Exemplu Edmonds-Karp}}
  
-<code cpp> +  * Drumul de ameliorare găsit este $1 - 3 - 5 - 2 - 4 - 6$, care are capacitatea reziduală **1**Se pompează **+1** pe acest drum (arcele cu verde), se scade în sens invers ​(arcele cu roșu). 
-// apply Prim's algorithm for computing a Minimum Spanning Tree (MST). +  * La următoarea incercare de apelare BFSnu se mai poate ajunge de la S la Tprin urmare algoritmul se oprește! 
-// +  ​* Fluxul găsit este **1 2**, care este maxim!
-// source ​   = source / starting node for computing / expading MST +
-//             (if not specified, node 1 is defaulted) +
-// nodes     = list of all nodes from G +
-// adj[node] = the adjacency list of node +
-//             ​example:​ adj[node] = {..., (neigh, weight) ...} => edge (node, neigh, weight) +
-// +
-// returns: costmst +
-//          cost = the cost of the MST +
-//          mst = the actual set of edges in MST +
-// +
-Prim(source=1G=(nodes, adges)) { +
-  ​// STEP 0: initialize results +
-  // d[node] = distance from MST (any node in current/​partial MST) to node +
-  // p[node] = parent of node: node is connected to MST with edge p[node] - node +
-  foreach (node in nodes) { +
-      d[node] = +oo;                          // distance not yet computed +
-      p[node] ​null;                         // parent not yet found +
-  }+
  
-  // STEP 0: initialize results 
-  cost = 0; // cost of the built MST 
-  mst = {}; // the actual set of edges (u, v) in the built MST 
  
-  // STEP 1: initialize a priority queue +<note warning>
-  pq = {};+
  
-  // STEP 2: add the source(s) into pq +Să observăm că starea finală a rețelei conține ​unități de fluxDacă ne uităm cu atențieobservăm că avem o unitate de flux pe drumul $1 - 2 - 4 - 6$ și alta pe $1 - 3 - 5 - 6$.
-  d[source] = 0;                              // distance from source to MST +
-  p[source] = null;                           // source never has parent +
-  pq.push( (sourced[source]) );+
  
-  ​// STEP 3: Build MST. +Deși Edmonds-Karp nu a ales inițial aceste 2 drumuri, s-a corectat pe parcurs, iar în final, dacă fluxul maxim trebuia să treacă pe aici, rețeaua reziduală a fost actualizată ​corectată până s-a întâmplat acest lucru!
-  while ( !pq.empty() ) { +
-    // STEP 3.1: Pop next possible node to connect with MST. +
-    node, _ = pq.pop() +
-    if (used[noode]) { +
-      continue; +
-    }+
  
-    // STEP 3.2: Extend MST with edge p[node] ​node +Semnificația înaintării pe un arc fictiv este că defapt cautăm să deviem fluxul de pe un drum ales anterior (de exemplu, $1 - 2 - 5 - 6$), pe o altă cale (de exempluporțiunea $1 - 2$ să se continue cu $2 - 4 - 6$), astfel încât să putem folosi partea eliberată ​(de exemplu$5 - 6$), în construcția unui nou drum la pasul curent.
-    used[node] = true; +
-    if (p[node] != null+
-      cost += d[node]; +
-      mst += { (nodep[node]) }; +
-    } +
- +
-    // STEP 3.3: relax all edges (nodeneigh) +
-    foreach ( (neighw) in adj[node]) { +
-      // Can we find a better / shorter link / edge from node to current MST? +
-      if (!used[neigh] && w < d[neigh]) { +
-        d[neigh] = d[node] + w[node][neigh]; ​           // update the new distance from neigh to MST +
-        p[neigh] = node;                                // save parent +
- +
-        pq.push( ​(neighd[neigh]);                   // replace distance for neigh in pq +
-      } +
-    } +
-  } +
- +
-  return costmst; +
-+
- +
-// Usage example: +
-cost, mst = Prim(1, G=(nodes, edges)); // e.g. source = 1, can be any node in G +
-// 1. Use cost of MST. +
-// 2. Use edges of MST (e.g. actually build the tree). +
-</​code>​ +
-</​spoiler>​ \\+
  
 +</​note>​
  
 ==== Complexitate ==== ==== Complexitate ====
  
-  * **complexitate temporală**:​ $T = O(m * log n)\ sau\ O(|E| * log |V|)$ +  * **complexitate temporală**:​ $T = O(n * m^2)\ sau\ O(|V| * |E|^2)$ 
-  * **complexitate spațială** : $S = O(n)$+  * **complexitate spațială** : $S = O(n)\ sau \ O(|V|)$
  
 <spoiler Detalii (analiză + optimizări)>​ <spoiler Detalii (analiză + optimizări)>​
  
-  * **complexitate temporală**: ​Se încearcă relaxarea tuturor celor **m** muchii din grafanalog algoritmului Dijkstra+  * **complexitate temporală**: ​Demonstrația se găsește în **Introduction to Algorithms**. Ideea de bază este că de fiecare dată când creștem fluxul pe un drum de ameliorareo muchie devine saturată. Dacă vreodată această muchie va mai fi folosită în alt drum de ameliorare (în cazul unei corecții), aceasta va fi folosită într-un drum mai lung. Respectivele distanțe sunt monotone și limitate de $O(|V|)$
-  * **complexitate spațială** : Se ține o coadă de priorități un set cu maximum **n** noduri. +  * **complexitate spațială** : Stocăm ​o coadă ​pentru algoritmul BFS. De asemenea, în implementare vom stoca și un vector ​de părinți pentru a putea parcurge ​un drum de ameliorare de la T la S (sens invers).
-  * **optimizare**:​ Analog Dijkstra, putem să obținem: +
-    * **Prim cu heap binar** - $O(m log n)$. +
-    * **Prim cu heap Fibonacci** - $O(n logn + m)$.+
  
 </​spoiler>​ \\ </​spoiler>​ \\
Line 319: Line 262:
 ===== TLDR ===== ===== TLDR =====
  
-  * Pentru ​**minimum spanning tree problem**, am studiat 2 algoritmi (Kruskal ​și Prim) cu aceeași complexitate ($O(m log m)$)+  * Ford-Fulkerson este un tipar general, ​**în practică** vom folosi doar algoritmul Edmonds-Karp. 
-  * Algoritmul lui Kruskal este de preferat pentru simplitatea / intuitivitatea luidificultatea reducându-se la a implementa structura ​de date **DisjointSet**.+    * Algoritmul de bazează pe pomparea fluxului pe arcele directe ​și scăderea pe arcele inverse. 
 +    * Ordinea în care drumurile de amelioare sunt găsite nu contează. Este un algoritm greedy de tip constructiv care își corectează deciziile pe parcursÎn final mereu găsește fluxul maxim! 
 +  * Dacă în plus avem și costuri pe arceputem determina flux maxim de cost minim cu Edmonds-Karp înlocuind BFS cu Dijkstra pentru găsirea drumurilor ​de ameliorare.
  
 ===== Exerciții ===== ===== Exerciții =====
Line 327: Line 272:
 <​note>​ <​note>​
  
-Scheletul de laborator se găsește pe pagina [[https://​github.com/​acs-pa/​pa-lab/​tree/​main/​skel/​lab11|pa-lab::​skel/​lab11]].+Scheletul de laborator se găsește pe pagina [[https://​github.com/​acs-pa/​pa-lab/​tree/​main/​skel/​lab12|pa-lab::​skel/​lab12]].
  
 </​note>​ </​note>​
Line 343: Line 288:
 </​note>​ </​note>​
  
-==== Kruskal ​====+==== Edmonds-Karp ​====
  
-Se dă un graf **neorientat** și **conex** ​cu **n** noduri și **m** muchii (cu costuri oarecare pe muchii).+Se dă un graf orientat ​cu n noduri și m arce. Graful are pe arce **capacitati pozitive**.
  
-Folosițalgoritmul lui **Kruskal** ​pentru a găsi un **APM**. +FolosișEdmonds-Karp ​pentru a găsi fluxul maxim între nodul sursă 1 șnodul destinație n în graful dat.
- +
-Task-uri: +
- +
-  - Găsiț**costul** MST. +
-  - Găsiți care sunt **muchiile** din MST. **ATENȚIE!** Se poate găsi orice **MST** (în caz că există mai mulți). Ordinea muchiilor din vectorul rezultat **NU** este relevantă.+
  
  
Line 359: Line 299:
 Restricții și precizări: Restricții și precizări:
  
-  * $ n <= 2 * 10^+  * $ n <= 10^
-  * $ m <= * 10^+  * $ m <= * 10^
-  * $ -10^3 <= c <= 10^3$, unde c este costul unei muchii+  * $ <= c <= 110.000$, unde c este capacitatea unui arc 
 +  * Nu există arce de la n la 1.
   * timp de execuție   * timp de execuție
     * C++: ''​%%1s%%''​     * C++: ''​%%1s%%''​
-    * Java: ''​%%7s%%''​ +    * Java: ''​%%2s%%''​
-  * Vă recomandăm să parcurgeți [[https://​infoarena.ro/​problema/​disjoint | DisjointSet]]. **ATENȚIE!** Scheletul de laborator oferă o astfel de implementare.+
  
 +
 +</​note>​
 +<​note>​
 +
 +Rezultatul se va returna sub forma unui singur număr, reprezentând fluxul maxim ce poate fi trimis prin rețea.
  
 </​note>​ </​note>​
Line 376: Line 321:
 ==== Extra ==== ==== Extra ====
  
-  * [[https://​infoarena.ro/​problema/​desen|infoarena/desen]] +  * [[https://​infoarena.ro/​problema/​cuplaj|infoarena/cuplaj]] 
-  * [[https://​infoarena.ro/​problema/​radiatie|infoarena/radiatie]] +  * [[https://​infoarena.ro/​problema/​fmcm|infoarena/fcmm]] 
-  * [[https://​infoarena.ro/​problema/​bile|infoarena/bile]] +  * [[https://​infoarena.ro/​problema/​drumuri2|infoarena/drumuri2]] 
-  * [[https://codeforces.com/problemsets/acmsguru/problem/99999/323|codeforces/aviamachinations]]+  * [[https://infoarena.ro/problema/joc4|inforena/joc4]] 
 +  * [[https://acm.timus.ru/​problem.aspx?​space=1&​num=1533|acm/fat-hobbits]]
  
 ===== Referințe ===== ===== Referințe =====
  
-[0] Chapters ​**Minimum Spanning Trees**, “Introduction to Algorithms”,​ Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest and Clifford Stein.+[0] Chapter ​**Maximum Flow**, “Introduction to Algorithms”,​ Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest and Clifford Stein. 
 + 
 +[1] [[http://​www.cs.yale.edu/​homes/​lans/​readings/​routing/​ford-max_flow-1956.pdf|Maximal flow through a network]], L.R. Ford, D.R. Fulkerson
  
-[1] [[http://cs.brown.edu/research/pubs/pdfs/​1995/​Karger-1995-RLT.pdf | A Randomized Linear-Time Algorithm to Find Minimum Spanning Trees]], David R. KargerPhilip N. KleinRobert E. Tarjan.+[2] [[http://www.eecs.umich.edu/~pettie/matching/Edmonds-Karp-network-flow.pdf|Theoretical improvements in algorithmic efficiency for network flow problems]], EdmondsJack; KarpRichard M.
  
-[2] [[https://dl.acm.org/doi/pdf/10.1145/​355541.355562 ​A Minimum Spanning Tree Algorithm ​with InverseAckermann Type Complexity|]], Bernard, Chazelle.+[3] [[https://www.cs.bgu.ac.il/~dinitz/Papers/Dinitz_alg.pdf|Dinitz ​Algorithm]], ​Yefim Dinitz
  
pa/laboratoare/laborator-11.1652786768.txt.gz · Last modified: 2022/05/17 14:26 (external edit)
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0