Differences

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

Link to this comparison view

pa:laboratoare:laborator-10 [2022/03/01 23:57]
darius.neatu created
pa:laboratoare:laborator-10 [2025/11/04 15:39] (current)
cpatrascu1910 [Laborator 10: Arbori minimi de acoperire]
Line 1: Line 1:
-====== Laborator 10: Drumuri minime (2/2) ======+====== Laborator 10: Arbori minimi de acoperire ​======
  
-TODO Gigel+ 
 +{{:​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 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 - 9 pe acest exemplu. 
 +    * Putem elimina în 3 moduri câte o muchie din prima componentă conexă - deci 3 arbori posibili. 
 +    * Putem elimina în 3 moduri câte o muchie din a doua componentă conexă - deci 3 arbori posibili. 
 +    * Numărul de păduri este $3 * 3 =9$ (combinăm oricare 2 arbori corespunzători componentelor conexe diferite). 
 + 
 +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 
 +// edges     = the list of all edges in G 
 +//             ​example:​ 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 = 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, 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 
 +  } 
 + 
 +  // 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[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]) }; 
 +    } 
 + 
 +    // STEP 3.3: relax all edges (node, neigh) 
 +    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 
 +      } 
 +    } 
 +  } 
 + 
 +  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>​ 
 + 
 +==== Task-1: 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 **MST**. 
 + 
 +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>​ 
 + 
 + 
 +=== Task-2: Costul minim să conectăm n puncte 2D în plan === 
 +Rezolvați problema [[ https://​leetcode.com/​problems/​min-cost-to-connect-all-points/​description/​ | Min Cost to Connect All Points ]] pe LeetCode. 
 + 
 + 
 + 
 +==== 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-10.1646171828.txt.gz · Last modified: 2022/03/01 23:57 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