Differences

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

Link to this comparison view

pa:laboratoare:laborator-10 [2022/04/30 00:59]
darius.neatu
pa:laboratoare:laborator-10 [2026/05/14 01:18] (current)
radu.nichita [Extra]
Line 1: Line 1:
-====== Laborator 10: Drumuri minime în grafuri: surse / destinații multiple. (2/2) ======+====== Laborator 10: Arbori minimi de acoperire ​======
  
-TODO: Add Adobe partnership. ## Obiective laborator 
  
-În laboratorul anterior am introdus contextul pentru **Shortest-paths problem** ​și **Single-source shortest-paths problem**În laboratorul 10 vom continua cu **All-pairs shortest-paths problem**.+{{:​pa:​new_pa:​partners:​bitdefender_masterbrand_logo_positive.png?​190 |}}  
 +Bitdefender este un lider recunoscut în domeniul securității IT, care oferă soluții superioare de prevenție, detecție ​și răspuns la incidente de securitate ciberneticăMilioane de sisteme folosite de oameni, companii și instituții guvernamentale sunt protejate de soluțiile companiei, ceea ce face Bitdefender unul dintre cei mai de încredere experți în combaterea amenințărilor informatice,​ în protejarea intimității și datelor, a identității digitale și în consolidarea rezilienței la atacuri. Compania a inovat constant în domenii precum antimalware,​ Internetul Lucrurilor, analiză comportamentală și inteligență artificială,​ iar tehnologiile Bitdefender sunt licențiate către peste 180 dintre cele mai cunoscute branduri de securitate din lume. 
 +===== Obiective laborator =====
  
-  * Înțelegerea conceptelor de cost asociat unei muchii, relaxare a unei muchii. +În acest laborator vom introduce contextul pentru ​**minimum spanning tree problem** și vom studia algoritmi care pot rezolva această problemă.
-  ​Prezentarea problemei drumului de cost minim (diverse variante). +
-  ​Prezentarea algoritmilor pentru calculul drumurilor minime.+
  
-===== Shortest-paths problem: all-pairs =====+  * Prezentarea problemei (diverse variante). 
 +  * Prezentarea algoritmilor pentru calcul a unui arbore minim de acoperire.
  
-Vă rugăm să parcugeți [[https://​ocw.cs.pub.ro/​courses/​pa/​laboratoare/​shortest-paths-problem|Shortest-paths problem]] pentru a vă familiariza cu contextul, problema și notațiile folosite.+===== Importanţă – aplicaţii practice =====
  
-Concepte necesare:+Algoritmii pentru determinarea unor arbori (minimi) de acoperire au multiple aplicații practice. Câteva exemple de aplicații sunt:
  
-  * **cost muchie** / **edge cost** +  * Designul circuitelor electronice:​ 
-  * **cost drum** ​/ **path cost** +    ​Maparea pinilor pe un circuit astfel încât să se foloseacă o lungime minimă de traseu. 
-  * **problema drumurilor minimesurse destinații multiple** ​**all-pairs shortest-paths problem** +  * Rețele de calculatoare:​ 
-  * **relaxarea unei muchii** / **edge relaxation** +    ​Interconectarea mai multor stații în rețea, cu un cost / latență redus(ă). 
-  * **reconstruirea unui drum** / **RebuildPath**+    ​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]].
  
-===== Algoritmi ​=====+===== Minimum spanning tree problem ​=====
  
-În acest laborator vom studia ​**all-pairs shortest-paths problem**. Pentru această problemă, vom prezenta 2 algoritmi:+Puteți consulta capitolul ​**Minimum Spanning Trees** din **Introduction to Algorithms** [0] pentru mai multe definiții formaleAceastă secțiune sumarizează principalele notații folosite în laboratoarele de PA.
  
-  * **Roy-Floyd**eficient pentru grafuri **dense** ($m >> n$ sau $|E| >> |V|$ - a.k.anumăr de muchii mult mai decât număr de noduri). +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).
-  * **Johnson**:​ eficient pentru grafuri **rare** ($n >> ​m$ sau $|V| >> |E|- a.k.a. număr de noduri mult mai mare decât număr de muchii).+
  
-Vom prezenta fiecare algoritm, îl vom analiza, iar la final vom vedea când îl vom folosi pe fiecare.+Vom adăuga alte notații în continuarea celor introduse anterior.
  
-Puteți consulta capitolul **All-Pairs Shortest Paths** din **Introduction to Algorithms** [0] pentru mai multe detalii despre acești algoritmi. 
  
-===== Roy-Floyd ===== 
  
-Algoritmul [[https://​en.wikipedia.org/​wiki/​Floyd%E2%80%93Warshall_algorithm|Roy-Floyd]] (**Roy-Floyd** / **Floyd–Warshall** algorithm) rezolvă **shortest-paths problem** în grafuri ​**G = (V, E)** care sunt **dense**.+**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.
  
-==== Roy-Floyd - Pseudocod ====+<spoiler Exemplu>
  
-<code cpp> +{{https://ocw.cs.pub.ro/courses/_media/pa/new_pa/lab11-spanning-tree-example.png?800| Arbore de acoperire ​}}
-// apply Roy-Floyd'​s algorithm for all-pairs shortest-paths problem +
-// +
-// nodes     = list of all nodes from G +
-// adj[node] = the adjacency list of node +
-//             ​example:​ adj[node] = {..., neigh, ...} => edge (node, neigh) of cost w[node][neigh] +
-// +
-// +
-// returns: d, p +
-//          d = distance matrix +
-//          p = parent matrix +
-// +
-Roy-Floyd(G=(nodes,​ adj)) { +
-  // STEP 1: initialize results +
-  // d[i][j] = minimum distance from i to j +
-  // p[i][j] = parent of node j, on shortest path from i to j +
-  for (i in nodes) { +
-    for (j in nodes) { +
-      d[i][j] = w[i][j]; ​                         // edge cost (or infinity if missing) +
-      p[i][j] = (w[i][j] != infinity ​i : null); // parent (or null if missing) +
-    ​} +
-  ​}+
  
-  // STEP 2: For each intermediar node k, +În exemplul atașatavem un graf **neorientat** ș**conex** cu următoare configurație:​
-  // try to update shortest path from to j. +
-  for (k in nodes) { +
-    for (i in nodes) { +
-      for (j in nodes) { +
-        // Is (i -> ... -> k) reunion with (k -> ... -> j) shorter than (i -> ... -> j)? +
-        if (d[i][k] + d[k][j] < d[i][j]) { +
-          d[i][j] = d[i][k] + d[k][j]; +
-          p[i][j] = p[k][j]; +
-        } +
-      } +
-    } +
-  }+
  
-  ​return dp; +  ​* ''​%%n = 5%%''​''​%%m = 5%%''​ 
-}+  * Pentru acest exemplu, avem **3** arbori de acoperire evidențiați în aceeași figură (rândul 2).
  
-// Usage example: +ObservațieUn graf **conex** are cel puțin un arbore de acoperire!
-d, p = Roy-Floyd(G=(nodes,​ adj)); +
-// 1. Use distances from d +
-// 2. Rebuild path from node to source using parents (p) +
-RebuildPath(source,​ destination,​ p); +
-</​code>​+
  
-==== Exemple ====+</​spoiler>​ \\
  
-=== Exemplu Roy-Floyd === 
  
-{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab10-graph-royfloyd-example.png?​512| Exemplu Roy-Floyd}} 
  
-Drumurile minime calculate de algoritmul Roy-Floyd sunt: 
  
-|-|1|2|3|4| \\ +> **Pădure de arbori de acoperire** / **spanning tree forest**: Se definește similar, dar 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**).
-|1|0|-1|-2|0| \\ +
-|2|4|0|2|4| \\ +
-|3|5|1|0|2| \\ +
-|4|3|-1|1|0| \\+
  
 +<spoiler Exemplu>
  
-<spoiler Explicație pas cu pas> În exemplul atașat, avem un graf **orientat** cu următoare configurație:+{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab11-spanning-forest-example.png?​800| Pădure de arbori de acoperire }}
  
-  * ''​%%n = 4%%''​''​%%m = 5%%''​ +În exemplul atașatavem un graf **neorientat** și **neconex** cu următoare configurație:
-  * Funcția de cost ''​%%w%%''​ are valorile menționate pe muchii. +
-  * Avem mai multe drumuri de cost diferite între diverse perechi de noduri din graf+
-  ​Construim tabloul ​**d** al distanțelor minime: +
-    ​* **d[u][v] = w[u][v]**, dacă există muchia $(u, v)$. +
-    * **d[u][v] = +∞, dacă **NU%%**%% există muchia $(u, v)$. +
-    * **d[u][u] = 0**, convenție+
-    * Obținem matricea **d_0**:+
  
-|-|1|2|3|4| \\ +  * ''​%%n = 6%%'',​ ''​%%m = 6%%''​ 
-|1|0|+∞|-2|+∞| \\ +  * În figura următoare evidențăm toate pădurile de arbori de acoperire ​9 pe acest exemplu. 
-|2|4|0|3|+∞| \\ +    * Putem elimina în 3 moduri câte o muchie din prima componentă conexă ​deci 3 arbori posibili. 
-|3|+∞|+∞|0|2| \\ +    * Putem elimina în moduri câte o muchie din a doua componentă conexă - deci 3 arbori posibili. 
-|4|+∞|-1|+∞|0| \\+    * Numărul de păduri este $* 3 =9$ (combinăm oricare ​arbori corespunzători componentelor conexe diferite).
  
 +Observație:​ Un graf **neconex** are cel puțin o pădure de arbori de acoperire!
  
-  * Rulăm algoritmul Roy-Floyd, vom scrie matricea pentru fiecare $ k = 1, 2, 3, 4$. +</​spoiler>​ \\
-    * $d_1$ (matricea dupa primul pas din algoritm; se modifică doar **d[2][3]**)+
  
-|-|1|2|3|4| \\ 
-|1|0|+∞|-2|+∞| \\ 
-|2|4|0|**2**|+∞| \\ 
-|3|+∞|+∞|0|2| \\ 
-|4|+∞|-1|+∞|0| \\ 
  
  
-  * $d_2$ (matricea dupa primul pas din algoritm; se modifică doar **d[4][3]**) 
  
-|-|1|2|3|4| \\ +**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).
-|1|0|+∞|-2|+∞| \\ +
-|2|4|0|2|+∞| \\ +
-|3|+∞|+∞|0|2| \\ +
-|4|+∞|-1|**1**|0| v+
  
-  * $d_3$ (matricea dupa primul pas din algoritm; se modifică doar **d[1][4]** și **d[2][4]**)+<spoiler Exemplu>
  
-|-|1|2|3|4| \\ +{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab11-minimum-spanning-tree-example.png?​900Exemplu funcție de cost pentru graf orientat}}
-|1|0|+∞|-2|**0**| \\ +
-|2|4|0|2|**4**| \\ +
-|3|+∞|+∞|0|2| \\ +
-|4|+∞|-1|1|0\\+
  
 +În exemplul atașat, avem un graf **neorientat** și **conex** cu următoare configurație:​
  
-  * $d_4$ (matricea dupa primul pas din algoritm; se modifică doar **d[1][2]**, **d[3][1]** ​ș**d[3][2]**)+  * ''​%%n = 5%%'',​ ''​%%m = 5%%''​ 
 +  * În figura următoare evidențăm toți arborii de acoperire. Doar dintre aceștia au cost minim **5**.
  
-|-|1|2|3|4| \\ +Observație:​ Un graf **conex** are cel puțin un arbore minim de acoperire!
-|1|0|**-1**|-2|0| \\ +
-|2|4|0|2|4| \\ +
-|3|**5**|**1**|0|2| \\ +
-|4|+∞|-1|1|0| \\+
  
 +</​spoiler>​ \\
  
-  * Drumurile minime sunt finale (cele menționate anterior - $d_4$). 
  
-</​spoiler>​ \\+===== Algoritmi =====
  
 +Pentru **minimum spanning tree problem** există mai mulți algoritmi, dintre care amintim:
  
-==== Complexitate ====+  * **Kruskal**:​ abordare greedy care se bazează pe sortarea muchiile după cost. Mai multe detalii în **Introduction to Algorithms** [0]. 
 +  * **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].
  
-  ​* **complexitate temporală**: $T = O(n^3)\ sau\ O(|E|^3)$ +Î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.
-  * **complexitate spațială** : $S = O(1)$+
  
-<spoiler Detalii (analiză + optimizări)>​+===== Kruskal =====
  
-  ​* **complexitate temporală**: Se aplică recurența discutată anterior care pentru fiecare nod intermediar ​**k**, încearcă să actualizeze drumul minim de la **i** la **j**. Cele 3 foruri dau complexitatea temporară. +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!
-  ​* **complexitate spațială** Nu stocăm tablouri auxilare.+
  
-</​spoiler>​ \\+==== Kruskal - Pseudocod ====
  
  
-===== Johnson =====+<​note>​
  
-Algoritmul lui [[https://​en.wikipedia.org/​wiki/​Johnson%27s_algorithm|Johnson]] (**Johnson’s algorithm**) rezolvă **shortest-paths problem** în grafuri **G = (V, E)** care sunt **rare**.+Algoritmul lui Kruskal folosește o structură de date care suportă 2 operații:
  
-Ideea de la care pornește acest algoritm ​este de a rula cel mai rapid algorithm pentru ​**shortest-paths single source**, adică algoritmul lui Dijkstra, pentru fiecare sursă (nod) din graf. Dacă toate costurile sunt pozitive, putem face direct acest lucru. Dacă însă există costuri negative, nu putem aplica Dijkstra pe acest graf. Algoritmul lui Johnson face o preprocesare (în **ComputeH**și calculează un graf echivalent, în care toate costurile sunt pozitive. Pe acest graf se poate aplica Dijkstra și să se afle toate distanțele. Ulterior se face translatarea inversă și se obțin ​**distanțele în graful inițial**.+  * În ce mulțime ​este elementul ​**x**
 +  * Să se reunească mulțimile ​din care fac parte elementele ​**x** și **y**.
  
-Observație:​ Dacă se știe că toate costurile din graf sunt pozitive (nenegative)algoritmul lui Johnson se poate înlocui cu rularea directă a algoritmului Dijkstra pentru toate nodurile din graf.+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.
  
-==== Johnson - Pseudocod ====+ 
 +</​note>​
  
 <code cpp> <code cpp>
-// apply Johnson's algorithm for all-pairs shortest-paths problem+// apply Kruskal's algorithm for computing a Minimum Spanning Tree (MST).
 // //
 // nodes     = list of all nodes from G // nodes     = list of all nodes from G
-// adj[node] ​= the adjacency ​list of node +// edges     = the list of all edges in G 
-//             ​example: ​adj[node] = {..., neigh, ...} => edge (node, neigh) ​of cost w[node][neigh]+//             ​example:​ edge (node, neigh, weight)
 // //
-// returns: ​has_cycled, p +// returns: ​costmst 
-//          ​has_cycle ​negative cycle detection flag (true if found) +//          ​cost the cost of the MST 
-//          ​d = distance matrix (defined only if has_cycle == false) +//          ​mst the actual set of edges in MST
-//          p = parent matrix (defined only if has_cycle =false)+
 // //
-Johnson(G=(nodes, ​adj)) { +Kruskal(G=(nodes, ​edges)) { 
-  // STEP 1Compute adjustment distances h (using Bellmand-Ford). +  // STEP 0initialize results 
-  ​has_cycle, h ComputerH(G)+  ​cost 0// cost of the built MST 
-  ​if (has_cycle+  ​mst = {}; // the actual set of edges (u, vin the built MST
-    return true, null, null; +
-  }+
  
-  // STEP 2Update all costs in G to obtain all costs nonnegative. +  // STEP 1initialize disjoint set data structure 
-  ​foreach ​((u, vin edges) { +  ​// (a tree is created for each node
-    if (w[u][v] !infinity) { +  ​disjointset ​DisjointSet(nodes); 
-      w[u][v] = w[u][v] + (h[u] - h[v]); + 
-    } +  ​// STEP 2: 
-  ​}+  sort(edges, compare=nondecreasing order by weight);
  
-  // STEP 3: Now all costs are nonnegative,​ so we can apply Dijsktra+  // STEP 3: Add edge by edge to MST if no cycles ​are created
-  // Start Dijkstra for each source ​u, saving just distances+  ​foreach ( (u, v, w) in edges ) { 
-  ​foreach ​(u in nodes) { +    ​// STEP 3.1: Check if anv v are in different trees
-    ​d_dijkstra,​ p_dijkstra = Dijkstra(u, G);+    if (disjointset.setOf(u) != disjointset.setOf(v)) { 
 +      // STEP 3.2: Merge these 2 trees (no cycles created). 
 +      disjointset.union(u, v);
  
-    ​// STEP 4Compute distance (u, v) on initial graph. +      ​// STEP 3.3Extend MST with the current edge
-    foreach (v in nodes) { +      ​cost +w
-      ​d[u][v] ​d_dijkstra[v] + (h[v] - h[u])+      ​mst += { (uv) };
-      ​p[u][v] = p_dijkstra[v];+
     }     }
   }   }
  
-  return ​falsed, p // no negative cycles detected+  return ​costmst;
 } }
- 
-ComputeH(G=(nodes,​ adj)){ 
-  // STEP 0: Create a new **temporary** graph 
-  // * add a new node 
-  source = new node; 
-  new_nodes = nodes + { source }; 
-  new_adj = adj; 
-  // * add a new edge (source, node) with cost 0 for all existing nodes 
-  for (node in nodes) { 
-    new_adj[source].push_back(node);​ 
-    w[source][node] = 0; 
-  } 
- 
-  // STEP 1: Run Bellman-Ford on the new graph. Save just flag and distances. 
-  has_cycle, h, _ = Bellmann-Ford(source,​ new_G=(new_nodes,​ new_adj))) 
-  if (has_cycle) { 
-    return true, null; // negative cycle detected 
-  } 
- 
-  return false, d; 
-} 
- 
  
 // Usage example: // Usage example:
-has_cycled, p Johnson(source, ​G=(nodes, ​adj)); +costmst Kruskal(G=(nodes, ​edges)); 
-if (has_cycle) { +// 1. Use cost of MST. 
-  print "Has Cycle!"​ +// 2. Use edges of MST (e.g. actually build the tree).
-  STOP. +
-} else { +
-  ​// 1. Use distances from d +
-  // (e.g. d[node] = distance from source to node) +
-  // +
-  // 3Rebuild path from node to source using parents (p) +
-  RebuildPath(source,​ destination,​ p); +
-}+
 </​code>​ </​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).
 +  * Altfel, nu putem adăuga muchia, deoarece am crea un ciclu. Această muchie se ignoră.
  
 ==== Exemple ==== ==== Exemple ====
  
-=== Exemplu ​Johnson ​===+=== Exemplu ​Kruskal ​===
  
-În această secțiune exemplificăm grafic cum rulează algoritmul lui Johnson pe un graf dat.+{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab11-kruskal.png?​900| Exemplu Kruskal}}
  
-<spoiler Explicație pas cu pas> ​{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab10-graph-johnson-example01.png?​512| Exemplu Johnson ​1/3}}+Un **MST** găsit ​cu algoritmul lui Kruskal este: $(1, 2); (2, 4); (2, 3); (3, 5)}$ de cost **5**.
  
-În exemplul atașat, avem un graf **orientat** cu următoare configurație:+<spoiler Explicație pas cu pas>
  
-  ​* ''​%%n = 5%%'',​ ''​%%m = 8%%''​+Î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.   * Funcția de cost ''​%%w%%''​ are valorile menționate pe muchii.
-  * Avem mai multe drumuri ​de cost diferite între diverse perechi ​de noduri din graf. (Observăm că nu avem ciclu de cost negativdeci are sens să rulăm un algoritm ​de drumuri minime). +  * Muchiile sortate după cost sunt: 
-  ''​%%STEP 1%%''​Adăugăm un nod fictiv ​(exemplu nodul ''​%%6%%''​). Îl vom uni de fiecare nod din graful inițial (1, 2, 3, 4, 5) cu muchie ​de cost 0Obținem graful din figura următoare:+    * $(3, 5)$ de cost $-1$ 
 +    * $(2, 4)$ de cost $1$ 
 +    * $(2, 3)$ de cost $2$ 
 +    * $(25)$ 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 1Se 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 3Se adaugă la **MST**. Se reunesc 2 arbori. 
 +    * Avem un arbore. STOP.
  
-{{https://ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab10-graph-johnson-example02.png?​512| Exemplu Johnson ​- 2/3}}+ObservațieÎn STEP 3 pas am fi putut alege muchia $(2, 5)$ cu același costObțineam un MST care diferea printr-o singură muchie ({${ (1, 2); (2, 5); (2, 3); (3, 5)}}$), dar cu același cost **5**.
  
-  * Pe acest graf putem rula o dată algoritmul Bellman-Ford,​ considerând sursă noul nod adăugat. +</​spoiler> ​\\
-  * Obținem vectorul de distanțe $h[node] = distanțadela\ nodul\ fictiv\ (6)\ la\ nodul\ node$+
  
-|node|1|2|3|4|5|6| \\ 
-|h[node]|-1|-7|-4|0|-2|0| \\ 
  
 +==== Complexitate ====
  
-  * ''​%%STEP 2%%'':​ Revenim la graful inițial (cel cu 5 noduri, 8 muchii) și îi alterăm costurile:​ +  * **complexitate temporală**$O(log m)\ sau\ O(|E| log |E|)$ 
-    ​$w[1][4] = w[1][4] + (h[1] - h[4]) = 2 + [ (-1) - (0) ] = 1$ +  * **complexitate spațială** : $O(n)$
-    ​* $w[2][1] ​w[2][1] + (h[2] - h[1]) = 6 + [ (-7) - (-1) ] = 0$ +
-    ​$w[2][3] = w[2][3] + (h[2] - h[3]= 3 + [ (-7) - (-4) ] = 0$ +
-    ​$w[3][1] = w[3][1] + (h[3] - h[1]= 4 + [ (-4) - (-1) ] = 1+
-    $w[3][4] = w[3][4] + (h[3] - h[4]) = 5 + [ (-4) - (0) ] = 1$ +
-    ​$w[4][2] = w[4][2] + (h[4] - h[2]) = -7 + [ (0) - (-7) ] = 0$ +
-    ​$w[4][5] = w[4][2] + (h[4] - h[2]) = -2 + [ (0) - (-2) ] = 0$ +
-    ​* $w[5][4] ​w[5][4] + (h[5] - h[4]= -1 + [ (-2) - (-4) ] = 1$ +
-  * Obținem graful din următoarea figură:+
  
-{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab10-graph-johnson-example03.png?​512| Exemplu Johnson - 2/3}}+<spoiler Detalii (analiză + optimizări)>​
  
-  * ''​%%STEP 3%%'':​ Deoarece toate costurile sunt pozitive, putem rula Dijkstra pe rând, pentru fiecare sursă ''​%%source = 1, 2, 3, 4, 5%%''​ din graf. +  * **complexitate temporală**
-  ​''​%%STEP 3%%''​$ source = 1 $ +    * Se sortează cele **m** muchii în ordine crescătoare după cost - $O(m log m)$
-    * Vectorul de distanțe este ''​%%d_dijkstra%%''​ atașat mai jos+    * Se folosesc **n** operații pe **DisjointSet** - $O(n log_{*}n)$, care poate fi aproximat cu $O(n)(demonstrație pe pagina **DisjointSet**). 
-    * Distanțele față de nodul ''​%%1%%'' ​pe graful inițial sunt: +      * 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ă. 
-      ​$d[1][1] = 0$ +  * **complexitate spațială*: Se ține o structură de date **DisjointSet** cu maximum **n** noduri.
-      ​* $d[1][2] = d_{dijkstra}[2] + (h[2] - h[1]= 1 + [ (-7) - (-1) ] = -5$ +
-      * $d[1][3] = d_{dijkstra}[3] + (h[3] - h[1]= 1 + [ (-4- (-1) ] = -2$ +
-      * $d[1][4= d_{dijkstra}[4] + (h[4] - h[1]) = 1 + [ (0) - (-1) ] = 2$ +
-      $d[1][5] = d_{dijkstra}[5] + (h[5] - h[1]) = 1 + [ (-2) - (-1) ] = 0$+
  
-|node|1|2|3|4|5| \\ +</​spoiler> ​\\
-|d_dijkstra[node]|0|1|1|1|1| ​\\+
  
  
-|node|1|2|3|4|5| \\ +===== [Studiu de cazPrim =====
-|d[1][node]|0|-5|-2|2|0| \\+
  
 +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ă greedy, asemănătoare cu algoritmul lui Dijkstra: merge pe muchiile de cost minim, din aproape în aproape, considerând o sursă inițial aleasă aleator și extinzând MST-ul curent cu muchia de cost minim neadăugată încă la arbore.
  
-  * ''​%%STEP 3%%'':​ $ source ​2 $ +==== Prim Pseudocod ​====
-    * Vectorul de distanțe este ''​%%d_dijkstra%%''​ atașat mai jos. +
-    * Distanțele față de nodul ''​%%2%%''​ pe graful inițial sunt: +
-      * $d[2][1] ​d_{dijkstra}[1] + (h[1] - h[2]) 0 + [ (-1) - (-7) ] 6$ +
-      * $d[2][2] ​0$ +
-      * $d[2][3] ​d_{dijkstra}[3] + (h[3] - h[2]) 0 + [ (-4) - (-7) ] 3$ +
-      * $d[2][4] = d_{dijkstra}[4] + (h[4] - h[2]) = 1 + [ (0) - (-7) ] = 8$ +
-      * $d[2][5] = d_{dijkstra}[5] + (h[5] - h[2]) = 1 + [ (-2) - (-7) ] = 6$+
  
-|node|1|2|3|4|5| \\ +<spoiler Prim - Pseudocod>​
-|d_dijkstra[node]|0|0|0|1|1| \\+
  
 +<code cpp>
 +// apply Prim's algorithm for computing a Minimum Spanning Tree (MST).
 +//
 +// 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: cost, mst
 +//          cost = the cost of the MST
 +//          mst = the actual set of edges in MST
 +//
 +Prim(source=1,​ G=(nodes, adj)) {
 +  // 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
 +  }
  
-|node|1|2|3|4|5| \\ +  // STEP 0: initialize results 
-|d[2][node]|6|0|3|8|6| \\+  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
 +  pq = {};
  
-  ​* ''​%%STEP 3%%''​source ​= 3 $ +  ​// STEP 2add the source(sinto pq 
-    * Vectorul de distanțe este ''​%%d_dijkstra%%''​ atașat mai jos. +  d[source] = 0;                              // distance from source to MST 
-    * Distanțele față de nodul ''​%%3%%''​ pe graful inițial sunt: +  p[source] = null;                           // source never has parent 
-      * $d[3][1] = d_{dijkstra}[1] + (h[1] - h[3]= 1 + [ (-1) - (-4) ] = 4$ +  ​pq.push( (source, ​d[source]) );
-      * $d[3][2] = d_{dijkstra}[2] + (h[2] - h[3]) = 1 + [ (-7) - (-4) ] = -2$ +
-      * $d[3][3] = 0$ +
-      * $d[3][4] = d_{dijkstra}[4] + (h[4] - h[3]) = 1 + [ (0) - (-4) ] = 5$ +
-      * $d[3][5] = d_{dijkstra}[5] + (h[5] - h[3]) = 1 + [ (-2- (-4) ] = 3$+
  
-|node|1|2|3|4|5| \\ +  // STEP 3: Build MST. 
-|d_dijkstra[node]|1|1|0|1|1| \\+  while ( !pq.empty() ) { 
 +    // STEP 3.1: Pop next possible node to connect with MST. 
 +    node, _ = pq.pop() 
 +    if (used[node]) { 
 +      continue; 
 +    }
  
 +    // STEP 3.2: Extend MST with edge p[node] - node
 +    used[node] = true;
 +    if (p[node] != null) {
 +      cost += d[node];
 +      mst += { (node, p[node]) };
 +    }
  
-|node|1|2|3|4|5| \\ +    // STEP 3.3: relax all edges (node, neigh) 
-|d[3][node]|4|-2|0|5|3| \\+    ​foreach ( (neigh, weight) in adj[node]) { 
 +      // Can we find a better / shorter link / edge from node to current MST? 
 +      if (!used[neigh] && weight < d[neigh]) { 
 +        d[neigh= weight; ​                             // update the new distance from neigh to MST 
 +        p[neigh] = node;                                // save parent
  
 +        pq.push( (neigh, d[neigh]) );                   // replace distance for neigh in pq
 +      }
 +    }
 +  }
  
-  ​* ''​%%STEP 3%%'':​ $ source = 4 $ +  ​return cost, mst; 
-    * Vectorul de distanțe este ''​%%d_dijkstra%%''​ atașat mai jos. +}
-    * Distanțele față de nodul ''​%%4%%''​ pe graful inițial sunt: +
-      * $d[4][1] = d_{dijkstra}[1] + (h[1] - h[4]) = 0 + [ (-1) - (0) ] = -1$ +
-      * $d[4][2] = d_{dijkstra}[2] + (h[2] - h[4]) = 0 + [ (-7) - (0) ] = -7$ +
-      * $d[4][3] = d_{dijkstra}[4] + (h[3] - h[4]) = 0 + [ (-4) - (0) ] = -4$ +
-      * $d[4][4] = 0$ +
-      * $d[4][5] = d_{dijkstra}[5] + (h[5] - h[4]) = 0 + [ (-2) - (0) ] = 2$+
  
-|node|1|2|3|4|5| \\ +// Usage example: 
-|d_dijkstra[node]|0|0|0|0|0| ​\\+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> ​\\
  
  
-|node|1|2|3|4|5| \\ +==== Complexitate ====
-|d[4][node]|-1|-7|-4|0|2| \\+
  
 +  * **complexitate temporală**:​ $T = O(m * log n)\ sau\ O(|E| * log |V|)$
 +  * **complexitate spațială** : $S = O(n)$
  
-  * ''​%%STEP 3%%'':​ $ source = 5 $ +<spoiler Detalii (analiză + optimizări)>
-    * Vectorul de distanțe este ''​%%d_dijkstra%%''​ atașat mai jos. +
-    * Distanțele față de nodul ''​%%4%%''​ pe graful inițial sunt: +
-      * $d[5][1] = d_{dijkstra}[1] ​(h[1] - h[5]) = 2 + [ (-1) - (-2) ] = 3$ +
-      * $d[5][2] = d_{dijkstra}[2] + (h[2] - h[5]) = 2 + [ (-7) - (-2) ] = -3$ +
-      * $d[5][3] = d_{dijkstra}[4] + (h[3] - h[5]) = 1 + [ (-4) - (-2) ] = -1$ +
-      * $d[5][4] = d_{dijkstra}[5] + (h[4] - h[5]) = 2 + [ (0) - (-2] = 4$ +
-      * $d[5][5] = 0$+
  
-|node|1|2|3|4|5| \\ +  * **complexitate temporală**:​ Se încearcă relaxarea tuturor celor **m** muchii din graf, analog algoritmului Dijkstra. 
-|d_dijkstra[node]|2|2|1|2|0| \\+  * **complexitate spațială** : Se ține o coadă de priorități / un set cu maximum **n** noduri. 
 +  * **optimizare**:​ Analog Dijkstra, putem să obținem: 
 +    * **Prim cu heap binar** - $O(m log n)$. 
 +    * **Prim cu heap Fibonacci** - $O(n logn + m)$.
  
 +</​spoiler>​ \\
  
-|node|1|2|3|4|5| \\ 
-|d[5][node]|3|-3|-1|4|0| \\ 
  
 +===== TLDR =====
  
-  * STOP! Am obținut toate distanțele ​$d[u][v]cerute!+  * Pentru **minimum spanning tree problem**, am studiat 2 algoritmi (Kruskal și Prim) cu aceeași complexitate ($O(m log m)$). 
 +  * Algoritmul lui Kruskal este de preferat pentru simplitatea / intuitivitatea lui, dificultatea reducându-se la a implementa structura de date **DisjointSet**.
  
-</​spoiler>​ \\+===== Exerciții =====
  
  
-==== Complexitate ====+<​note>​
  
-  * **complexitate temporală**$T = O(n * m * log (n))\ sau\ O(|V| * |E| * log (|V|))$ +Scheletul de laborator se găsește pe pagina [[https://​github.com/​acs-pa/​pa-lab/​tree/​main/​skel/​lab11|pa-lab::​skel/​lab11]].
-  * **complexitate spațială** ​$S = O(n + m)\ sau \ O(|V| + |E|)$+
  
-<spoiler Detalii (analiză + optimizări)>+</note> 
 +<note warning>
  
-  * **complexitate temporală**:​ +Înainte ​de a rezolva exercițiileasigurați-vă că ați citit și înțeles toate precizările din secțiunea [[https://​ocw.cs.pub.ro/​courses/​pa/​skel_graph | Precizari laboratoare 07-12]].
-    * **ComputeH**:​ +
-      * Construire graf nou - $O(n + m))$. +
-      * Aplicare Bellman-Ford pe noul graf - $O(n * m)$. +
-    * **Update edges** - pasul se face în $O(m)$. +
-    * Rularea Dijkstra pentru fiecare nod din graf - este complexitatea ​de la rulare Dijkstra pentru un singur nod sursă (a.k.a. $O(m log n)$)multiplicată cu numărul de noduri. +
-    * În final ajungem la un total de $O(n * m * log(n) + n * m + n + m) = O(n * m + log(n))$. +
-  * **complexitate spațială** : Se construiește un alt graf (**new_node** - n, **new_adj** - m), se produc câțiva vectori temporari de lungime n (**h**, **d_dijkstra**,​ **p_dijkstra**). +
-  * **optimizare**:​ Deoarece Dijsktra se poate optimiza (vezi laborator anterior), putem obține $O(n^2 * log n + n * m)$ complexitatea pentru ultima etapăComplexitatea finală este $O(n ^ 2 log (m) + n * m)$.+
  
-</​spoiler>​ \\+Prin citirea acestor precizări vă asigurați că:
  
 +  * știți **convențiile** folosite
 +  * evitați **buguri**
 +  * evitați **depunctări** la lab/​teme/​test
  
-===== TLDR ===== 
  
-  * Pentru cazul **shortest-path single source** am studiat în laboratorul anterior algoritmul lui Dijkstra ​algoritmul Bellman-Ford. +</note>
-  * Pentru cazul **shortest-path all-pairs**,​ discuția se facă după numărul de muchii din graf: +
-    * **graf dens**: aplicăm Roy-Floyd și obținem $O(n ^ 3)$. +
-    * **graf rar**: aplicăm Johnson și obținem $O(n * m * log (n))$.+
  
-===== Exerciții ​=====+====== Pool probleme (pentru prezentări) ======
  
-==== Roy-Floyd ​====+===== 1) Min Cost to Connect All Points =====
  
-Se dă un graf **orientat** cu **n** noduriGraful are **costuri strict pozitive**.+Enunț: ​Se dau ''​N''​ puncte în plan. Costul conectării a două puncte este distanța Manhattan dintre acesteaSe cere costul minim pentru a conecta toate punctele.
  
-Se dă matricea ponderilor - **w**se cere matricea drumurilor minime - **d**, aplicând algoritmul **Roy-Floyd**.+Date de intrare: Un vector de puncte ''​(xy)''​.
  
 +Date de ieșire: Costul minim necesar pentru conectarea tuturor punctelor.
  
-<note warning>+Problema se poate testa la: 
 +[[https://​leetcode.com/​problems/​min-cost-to-connect-all-points/​description/​ 
 + | LeetCode - Min Cost to Connect All Points]]
  
-Restricții și precizări:+===== 2) Road Reparation =====
  
-  * $ n <= 100 $ +Enunț: Se dau ''​N''​ orașe și ''​M''​ drumuri bidirecționale cu costuri de reparațieSe cere costul ​minim necesar pentru a face posibilă deplasarea între oricare două orașe. Dacă nu este posibil, ​se afișează ''​IMPOSSIBLE''​.
-  * $ 0 <= c <= 1.000$, unde c este costul ​unui arc. +
-    * Dacă **nu există arc** între o pereche de noduri x și y, distanța de la nodul x la nodul y din **matricea ponderilor** va fi 0. +
-    * Dacă după aplicarea algoritmului **nu se găsește drum** pentru o pereche de noduri x și y, se va considera **distanța** dintre ele egală cu 0 (se stochează în **matricea distantelor** valoarea 0). +
-    * Drumul de la nodul i la nodul i are lungime 0 (prin convenție). +
-  * timp de execuție +
-    * C++: 1s +
-    * Java: 8s+
  
 +Date de intrare: ''​N'',​ ''​M'',​ urmate de ''​M''​ muchii ponderate.
  
-</​note>​+Date de ieșire: Costul minim al unui arbore de acoperire sau ''​IMPOSSIBLE''​.
  
-==== Johnson ====+Problema se poate testa la: 
 +[[https://​cses.fi/​problemset/​task/​1675 
 + | CSES - Road Reparation]]
  
-Se dă un graf **orientat** cu **n** noduri. Graful are **costuri oarecare** (pot fi și negative).+===== 3Critical and Pseudo-Critical Edges in Minimum Spanning Tree =====
  
-Se dă lista de adiacență cu costurile aferente, se cere matricea drumurilor minime - **d**, aplicând algoritmul **Johnson**.+Enunț: ​Se dă un graf ponderat conexPentru fiecare muchie se determină dacă este:
  
 +critică (apare în orice MST)
 +pseudo-critică (poate apărea într-un MST)
  
-<note warning>+Date de intrare: număr de noduri și lista muchiilor ponderate.
  
-Restricții ​și precizări:+Date de ieșire: două liste de muchiicritice și pseudo-critice.
  
-  * $ n <= 1000 $ +Problema ​se poate testa la: 
-  * $ m <= 25000 $ +[[https://​leetcode.com/​problems/​find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree/​ 
-  * $ -1000 <= c <= 1.000$, unde c este costul unui arc. + | LeetCode - Critical and Pseudo-Critical Edges]]
-    * Dacă **nu există arc** între o pereche de noduri x și y, distanța de la nodul x la nodul y din **matricea ponderilor** va fi 0. +
-    * Dacă după aplicarea algoritmului **nu se găsește drum** pentru o pereche de noduri x și y, se va considera **distanța** dintre ele egală cu 0 (se stochează în **matricea distantelor** valoarea 0). +
-    * Drumul de la nodul i la nodul i are lungime 0 (prin convenție). +
-    * Dacă graful conține un ciclu de cost negativ, se va afișa mesajulCiclu negativ! +
-  * timp de execuție +
-    * C++1s +
-    * Java: 8s+
  
 +===== Extra =====
  
-</note>+  * [[https://​infoarena.ro/​problema/​desen|infoarena/​desen]] 
 +  * [[https://​infoarena.ro/​problema/​radiatie|infoarena/​radiatie]] 
 +  * [[https://​infoarena.ro/​problema/​bile|infoarena/​bile]] 
 +  * [[https://​codeforces.com/​problemsets/​acmsguru/​problem/​99999/​323|codeforces/​aviamachinations]]
  
-==== BONUS ====+===== Referințe =====
  
-La acest laboratorasistentul va alege 1-2 probleme din secțiunea extra.+[0] Chapters **Minimum Spanning Trees**“Introduction to Algorithms”,​ Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest and Clifford Stein.
  
-==== Extra ==== +[1] [[http://cs.brown.edu/research/pubs/pdfs/1995/Karger-1995-RLT.pdf A Randomized Linear-Time Algorithm to Find Minimum Spanning Trees]], David RKarger, Philip N. Klein, Robert E. Tarjan.
- +
-  * [[https://​infoarena.ro/​problema/​rfinv|infoarena/​rfinv]] +
-  * [[https://infoarena.ro/​problema/​rf|infoarena/​rf]] +
-  * [[https://​infoarena.ro/problema/coach|infoarena/coach]] +
-  * [[https://codeforces.com/​contest/​295/​problem/​B|codeforces/​greg-and-graph]] +
-  * [[https://​codeforces.com/​contest/​25/​problem/​C|codeforces/​roads-in-berland]] +
-  * [[https://​codeforces.com/​problemset/​problem/​21/​D|codeforces/​traveling-graph]] +
-  * [[https://​codeforces.com/​gym/​101498/​problem/​L|codeforces/​the-shortest-path]] +
- +
-===== Referințe =====+
  
-[0Chapters **Single-Source Shortest Paths** ​**All-Pairs Shortest Paths**, “Introduction to Algorithms”,​ Thomas HCormen, Charles ELeiserson, Ronald LRivest and Clifford Stein.+[2[[https://dl.acm.org/​doi/​pdf/​10.1145/​355541.355562 | A Minimum Spanning Tree Algorithm with InverseAckermann Type Complexity|]],​ Bernard, Chazelle.
  
pa/laboratoare/laborator-10.1651269594.txt.gz · Last modified: 2022/04/30 00:59 by darius.neatu
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