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:25]
darius.neatu [Laborator 11: Arbori minimi de acoperire]
— (current)
Line 1: Line 1:
-====== Laborator 11: Arbori minimi de acoperire ====== 
- 
- 
-{{:​pa:​new_pa:​partners:​bitdefender-logo.png?​180 |}} 
- 
-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 ===== 
- 
-În acest laborator vom introduce contextul pentru **minimum spanning tree problem** și vom studia algoritmi care pot rezolva această problemă. 
- 
-  * Prezentarea problemei (diverse variante). 
-  * Prezentarea algoritmilor pentru calcul a unui arbore minim de acoperire. 
- 
-===== Importanţă – aplicaţii practice ===== 
- 
-Algoritmii pentru determinarea unor arbori (minimi) de acoperire au multiple aplicații practice. Câteva exemple de aplicații sunt: 
- 
-  * Designul circuitelor electronice:​ 
-    * 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 ===== 
- 
-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. 
- 
-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). 
- 
-Vom adăuga alte notații în continuarea celor introduse anterior. 
- 
- 
- 
-> **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. 
- 
-<spoiler Exemplu> 
- 
-{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab11-spanning-tree-example.png?​800| Arbore de acoperire }} 
- 
-În exemplul atașat, avem un graf **neorientat** și **conex** cu următoare configurație:​ 
- 
-  * ''​%%n = 5%%'',​ ''​%%m = 5%%''​ 
-  * Pentru acest exemplu, avem **3** arbori de acoperire evidențiați în aceeași figură (rândul 2). 
- 
-Observație:​ Un graf **conex** are cel puțin un arbore de acoperire! 
- 
-</​spoiler>​ \\ 
- 
- 
- 
- 
-> **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**). 
- 
-<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>​ \\ 
- 
- 
- 
- 
-> **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). 
- 
-<spoiler Exemplu> 
- 
-{{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}} 
- 
-În exemplul atașat, avem un graf **neorientat** și **conex** cu următoare configurație:​ 
- 
-  * ''​%%n = 5%%'',​ ''​%%m = 5%%''​ 
-  * În figura următoare evidențăm toți arborii de acoperire. Doar 2 dintre aceștia au cost minim **5**. 
- 
-Observație:​ Un graf **conex** are cel puțin un arbore minim de acoperire! 
- 
-</​spoiler>​ \\ 
- 
- 
-===== Algoritmi ===== 
- 
-Pentru **minimum spanning tree problem** există mai mulți algoritmi, dintre care amintim: 
- 
-  * **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]. 
- 
-Î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. 
- 
-===== Kruskal ===== 
- 
-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! 
- 
-==== Kruskal - Pseudocod ==== 
- 
- 
-<​note>​ 
- 
-Algoritmul lui Kruskal folosește o structură de date care suportă 2 operații: 
- 
-  * În ce mulțime este elementul **x**? 
-  * 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. 
- 
- 
-</​note>​ 
- 
-<code cpp> 
-// 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) 
-// 
-// returns: cost, 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 
-  // (a tree is created for each node) 
-  disjointset = new DisjointSet(nodes);​ 
- 
-  // STEP 2: 
-  sort(edges, compare=nondecreasing order by weight); 
- 
-  // 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. 
-      cost += w; 
-      mst += { (u, v) }; 
-    } 
-  } 
- 
-  return cost, mst; 
-} 
- 
-// Usage example: 
-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). 
-  * Altfel, nu putem adăuga muchia, deoarece am crea un ciclu. Această muchie se ignoră. 
- 
-==== Exemple ==== 
- 
-=== Exemplu Kruskal === 
- 
-{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab11-kruskal.png?​900| Exemplu Kruskal}} 
- 
-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și 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>​ \\ 
- 
- 
-==== Complexitate ==== 
- 
-  * **complexitate temporală**:​ $T = O(m * log m)\ sau\ O(|E| * log |E|)$ 
-  * **complexitate spațială** : $S = O(n)$ 
- 
-<spoiler Detalii (analiză + optimizări)>​ 
- 
-  * **complexitate temporală**:​ 
-    * 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>​ \\ 
- 
- 
-===== [Studiu de caz] Prim ===== 
- 
-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. 
- 
-==== Prim Pseudocod ==== 
- 
-<spoiler Prim - Pseudocod>​ 
- 
-<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, 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 
-  pq = {}; 
- 
-  // STEP 2: add the source(s) into pq 
-  d[source] = 0;                              // distance from source to MST 
-  p[source] = null;                           // source never has parent 
-  pq.push( (source, d[source]) ); 
- 
-  // STEP 3: Build MST. 
-  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 
-    used[node] = true; 
-    if (p[node] != null) { 
-      cost += d[node]; 
-      mst += { (node, p[node]) }; 
-    } 
- 
-    // STEP 3.3: relax all edges (node, neigh) 
-    foreach ( (neigh, w) 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( (neigh, d[neigh]) );                   // replace distance for neigh in pq 
-      } 
-    } 
-  } 
- 
-  return cost, mst; 
-} 
- 
-// 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>​ \\ 
- 
- 
-==== Complexitate ==== 
- 
-  * **complexitate temporală**:​ $T = O(m * log n)\ sau\ O(|E| * log |V|)$ 
-  * **complexitate spațială** : $S = O(n)$ 
- 
-<spoiler Detalii (analiză + optimizări)>​ 
- 
-  * **complexitate temporală**:​ Se încearcă relaxarea tuturor celor **m** muchii din graf, analog algoritmului Dijkstra. 
-  * **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>​ \\ 
- 
- 
-===== TLDR ===== 
- 
-  * 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**. 
- 
-===== Exerciții ===== 
- 
- 
-<​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]]. 
- 
-</​note>​ 
-<note warning> 
- 
-Înainte de a rezolva exercițiile,​ asiguraț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]]. 
- 
-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 
- 
- 
-</​note>​ 
- 
-==== Kruskal ==== 
- 
-Se dă un graf **neorientat** și **conex** cu **n** noduri și **m** muchii (cu costuri oarecare pe muchii). 
- 
-Folosiți algoritmul lui **Kruskal** pentru a găsi un **APM**. 
- 
-Task-uri: 
- 
-  - Găsiți **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ă. 
- 
- 
-<note warning> 
- 
-Restricții și precizări: 
- 
-  * $ n <= 2 * 10^5 $ 
-  * $ m <= 4 * 10^5 $ 
-  * $ -10^3 <= c <= 10^3$, unde c este costul unei muchii 
-  * timp de execuție 
-    * C++: ''​%%1s%%''​ 
-    * Java: ''​%%7s%%''​ 
-  * Vă recomandăm să parcurgeți [[https://​infoarena.ro/​problema/​disjoint | DisjointSet]]. **ATENȚIE!** Scheletul de laborator oferă o astfel de implementare. 
- 
- 
-</​note>​ 
- 
-==== BONUS ==== 
- 
-La acest laborator, asistentul va alege 1-2 probleme din secțiunea extra. 
- 
-==== Extra ==== 
- 
-  * [[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]] 
- 
-===== Referințe ===== 
- 
-[0] Chapters **Minimum Spanning Trees**, “Introduction to Algorithms”,​ Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest and Clifford Stein. 
- 
-[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. Karger, Philip N. Klein, Robert E. Tarjan. 
- 
-[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-11.1652786738.txt.gz · Last modified: 2022/05/17 14:25 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