Differences

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

Link to this comparison view

pa:laboratoare:laborator-10 [2019/04/19 21:11]
radu.iacob [Complexitate]
pa:laboratoare:laborator-10 [2023/03/15 16:55] (current)
radu.nichita
Line 1: Line 1:
-====== Laborator 10 Arbori ​Minimi ​de Acoperire ​======+====== Laborator 10Arbori ​minimi ​de acoperire ​====== 
 + 
 + 
 +{{:​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.
  
 ===== Obiective laborator ===== ===== Obiective laborator =====
-  ​*Însuşirea conceptului de arbore minim de acoperire; + 
-  *Înţelegerea modului de funcţionare a algoritmilor ​de determinare ​a unui arbore minim de acoperire ​prezentaţi;​ +În acest laborator vom introduce contextul pentru ​**minimum spanning tree problem** și vom studia algoritmi care pot rezolva această problemă. 
-  *Aplicarea algoritmilor în rezolvarea problemelor;​+ 
 +  * Prezentarea problemei (diverse variante). 
 +  * Prezentarea ​algoritmilor ​pentru calcul ​a unui arbore minim de acoperire.
  
 ===== Importanţă – aplicaţii practice ===== ===== Importanţă – aplicaţii practice =====
  
-Găsirea unui arbore minim de acoperire ​pentru un graf are aplicaţii în domenii cât se poate de variate: +Algoritmii pentru determinarea unor arbori (minimi) ​de acoperire ​au multiple ​aplicații practiceCâteva exemple ​de aplicații ​sunt:
-  *Reţele (de calculatoare,​ telefonie, cablu TV, electricitate,​ drumuri): se doreşte interconectarea mai multor puncte, cu un cost redus şi atunci este utilă cunoaşterea arborelui care conectează toate punctele, cu cel mai mic cost posibilSTP(Spanning Tree Protocol) este un protocol ​de rutare care previne apariţia buclelor într-un LAN, şi se bazează pe crearea unui arbore de acoperire. Singurele legături active ​sunt cele care apar în acest arbore, iar astfel se evită buclele. +
-  *Segmentarea imaginilorîmpărţirea unei imagini în regiuni de pixeli cu proprietăţi asemănătoare. E utilă mai apoi în analiza medicală a unei zone afectate de o tumoare de exemplu. +
-  *Algoritmi de aproximare pt probleme NP-dure: problema comis-voiajorului,​ arbori Steiner. +
-  *Clustering:​ pentru detectarea de clustere cu forme neregulate [8], [9].+
  
-===== Descrierea problemei şa rezolvărilor =====+  * 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ăț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]].
  
-Dându-se un graf conex neorientat G =(V, E), se numeşte arbore de acoperire al lui G un subgraf G’=(V, E’)  care conţine toate vârfurile grafului G şi o submulţime minimă de muchii E’⊆ E cu proprietatea că uneşte toate vârfurile şi nu conţine cicluri. Cum G’ este conex şi aciclic, el este arbore. Pentru un graf oarecare, există mai mulţi arbori de acoperire.+===== Minimum spanning tree problem =====
  
-Dacă asociem o matrice de costuri, w, pentru ​muchiile din G, fiecare arbore de acoperire va avea asociat un cost egal cu suma costurilor muchiilor conţinute. Un arbore care are costul asociat ​mai mic sau egal cu costul oricărui alt arbore de acoperire se numeşte arbore minim de acoperire (minimum spanning tree) al grafului GUn graf poate avea mai mulţi arbori minimi de acoperire. Dacă toate costurile muchiilor sunt diferite, există un singur AMA.  +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.
-Primul algoritm pentru determinarea unui arbore minim de acoperire a fost scris în 1926 de Otakar BoruvkaÎn prezent, cei mai folosiţi algoritmi sunt Prim şi Kruskal. Toţi trei sunt algoritmi greedy, şi rulează în timp polinomial. La fiecare pas, pentru a construi arborele se alege cea mai bună variantă posibilă la momentul respectiv. Generic, algoritmul de determinare a unui AMA se poate scrie astfel:+
  
-<code cpp> +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$,​ $(xy)$etc).
-ArboreMinimDeAcoperire(G(VE)c) +
- MuchiiAMA = ∅; +
- while ​(MuchiiAMA nu reprezintă muchiile unui arbore minim de acoperire) +
- găseşte o muchie (uvcare este sigură pentru MuchiiAMA;​ +
- MuchiiAMA = MuchiiAMA ∪ {(uv)}; +
- return MuchiiAMA;​ +
-</​code>​+
  
-O muchie sigură este o muchie care se poate adăuga ​unei submulţimi de muchii ale unui arbore minim de acoperire, astfel încât noua mulţime obţinută să aparţină tot unui arbore minim de acoperire. Iniţial, MuchiiAMA este o mulţime vidă. La fiecare pas, se adaugă câte o muchie sigură, deci MuchiiAMA rămâne o submulţime a unui AMA. În consecinţă,​ la sfarşitul rulării algoritmului (când muchiile din mulţime unesc toate nodurile din graf), MuchiiAMA va conţine de fapt arborele minim de acoperire dorit. +Vom adăuga ​alte notații în continuarea celor introduse anterior.
-  +
-==== Algoritmul Kruskal ====+
  
-Algoritmul a fost dezvoltat în 1956 de Joseph Kruskal. Determinarea arborelui minim de acoperire se face prin reuniuni de subarbori minimi de acoperire. Iniţial, se consideră că fiecare nod din graf este un arbore. Apoi, la fiecare pas se selectează muchia de cost minim care uneşte doi subarbori disjuncţi, şi se realizează unirea celor doi subarbori. Muchia respectivă se adaugă la mulţimea MuchiiAMA, care la sfârşit va conţine chiar muchiile din arborele minim de acoperire. ​ 
  
-===== Pseudocod ===== 
  
-<code cpp> +**Arbore de acoperire** / **spanning tree**: Într-un graf neorientat **conex** $(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.
-Kruskal(G(V, E), w+
-MuchiiAMA <∅; +
-for each v in V do +
- MakeSet(v);​ //​fiecare nod e un arbore ​diferit +
-sort(E);​ //​sortează muchiile în ordine crescătoare a costului +
-for each (u,v) in E do +
- if (FindSet(u) != FindSet(v)) then //​capetele muchiei fac parte //din subarbori disjuncţi +
- MuchiiAMA = MuchiiAMA ∪ {(u, v)};​ //​adaugă muchia la arbore +
- Union(u, v);    //uneşte subarborii corespunzători lui u şi v +
-return MuchiiAMA;​ +
-</​code>​+
  
-Bucla principală for poate fi înlocuită cu o buclă while, în care se verifică dacă în MuchiiAMA există mai puţin de |V| - 1 muchii, pentru că orice arbore de acoperire are |V| - 1 muchii, iar la fiecare pas se adaugă o muchie sigură. +<spoiler Exemplu>
- +
  
-===== Exemplu ​de rulare =====+{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab11-spanning-tree-example.png?​800| Arbore ​de acoperire }}
  
-Se consideră graful ​ din figura ​următoare:+În exemplul atașat, avem un graf **neorientat** și **conex** cu următoare ​configurație:
  
-Fiecare subarbore va fi colorat diferit. Cum iniţial fiecare nod reprezintă un subarborenodurile au culori diferite. Pe măsură ce subarborii sunt uniţinodurile aparţinând aceluiaşsubarbore vor fi coloraţidentic. Costurile muchiilor sunt sortate în ordine crescătoare.+  * ''​%%n = 5%%''​''​%%= 5%%''​ 
 +  * Pentru acest exempluavem **3** arbori de acoperire evidențiațîn aceeașfigură (rândul 2).
  
-{{ pa:laboratoare:​92.jpg |}}+ObservațieUn graf **conex** are cel puțin un arbore de acoperire!
  
-**Pas 1**+</​spoiler>​ \\
  
-Se alege prima muchie, (1,4). Se observă că uneşte subarborii {1} şi {4}, deci muchia e adăugată la MuchiiAMA, iar cei doi subarbori se unesc. 
-{{ pa:​laboratoare:​93.jpg |}} 
-MuchiiAMA = {(1,4)}. 
  
-**Pas 2** 
  
-Următoarea muchie este (7,8), care uneşte {7} şi {8}. Se adaugă la MuchiiAMA şi se unesc cei doi subarbori. 
-{{ pa:​laboratoare:​94.jpg |}}  
-MuchiiAMA = {(1,​4),​(7,​8)}. 
  
-**Pas 3**+**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**).
  
-Următoarea muchie este (5,6), care uneşte {5} şi {6}. Se adaugă la MuchiiAMA şi se unesc cei doi subarbori. +<spoiler Exemplu>
-{{ pa:​laboratoare:​95.jpg |}}  +
-MuchiiAMA = {(1,​4),​(7,​8),​(5,​6)}.+
  
-**Pas 4**+{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab11-spanning-forest-example.png?​800| Pădure de arbori de acoperire }}
  
-Următorul cost este 4. Se observă că muchiile (1,2) şi (2,4) au costul 4 şi unesc {2} cu {1,4}. Se adaugă la MuchiiAMA una dintre cele două muchii, fie ea (1,2), şi se unesc cei doi subarbori. Alegerea muchiei (2,4) va duce la găsirea unui alt AMA. [Am spus anterior că un graf poate avea mai mulţarbori minimi de acoperire, ​cu acelaşi cost, dacă există muchii diferite cu acelaşi cost.] +În exemplul atașatavem un graf **neorientat** ș**neconex** ​cu următoare configurație:
-{{ pa:laboratoare:​96.jpg |}}  +
-MuchiiAMA = {(1,​4),​(7,​8),​(5,​6),​(1,​2)}.+
  
-**Pas 5**+  ​''​%%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).
  
-Următoarea muchie ​de cost minim este (5,8), care uneşte {5,6} şi {7,8}. Se adaugă la MuchiiAMA şi se unesc cei doi subarbori, rezultând {5,​6,​7,​8}. +Observație:​ Un graf **neconex** are cel puțin o pădure de arbori de acoperire!
-{{ pa:​laboratoare:​97.jpg |}}  +
-MuchiiAMA = {(1,​4),​(7,​8),​(5,​6),​(1,​2),​ (5,8)}.+
  
-**Pas 6**+</​spoiler>​ \\
  
-Muchia (5,7), care are cel mai mic cost actual, are ambele extremităţi în subarborele {5,6,7,8}. În consecinţă,​ nu se efectuează nicio schimbare. 
  
-MuchiiAMA = {(1,​4),​(7,​8),​(5,​6),​(1,​2),​(5,​8)}. 
  
-**Pas 7** 
  
-Următorul cost este 7. Se observă că muchiile ​(1,6şi (4,5au costul ​7 şunesc subarborii {1,2,4} şi {5,6,7,8}. Se adaugă la MuchiiAMA ​(1,6), şi se unesc cei doi subarbori. Alegerea muchiei (4,5) va duce la găsirea unui alt AMA. +> **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 șnoțiunea de **pădure de arbori minimi de acoperire** ​(pornind de la cea de pădure de arbori de acoperire).
-{{ pa:​laboratoare:​98.jpg |}}  +
-MuchiiAMA = {(1,​4),​(7,​8),​(5,​6),​(1,​2),​(5,​8),​(1,​6)}.+
  
-**Pas 8**+<spoiler Exemplu>
  
-Muchia (4,6) de cost 8 are capetele în acelaşi subarbore, deci nu se produc schimări.+{{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}}
  
-MuchiiAMA = {(1,4),​(7,​8),​(5,​6),​(1,​2),​(5,​8),​(1,​6)}.+În exemplul atașatavem un graf **neorientat** și **conex** cu următoare configurație:​
  
-**Pas 9**+  ​''​%%n = 5%%'',​ ''​%%m = 5%%''​ 
 +  ​În figura următoare evidențăm toți arborii de acoperire. Doar 2 dintre aceștia au cost minim **5**.
  
-Muchia (1,3) de cost 9 uneşte cei doi subarbori rămaşi, {1,​2,​4,​5,​6,​7,​8} şi {3}. Deci după unire obţinem ​un singur ​arbore. (1,3) se adaugă la MuchiiAMA, care va conţine acum 7 muchii, iar algoritmul se opreşte.  +Observație:​ Un graf **conex** are cel puțin ​un arbore minim de acoperire!
-{{ pa:​laboratoare:​91.jpg |}}  +
-Arborele ​minim de acoperire ​obţinut este {(1,​4),​(7,​8),​(5,​6),​(1,​2),​(5,​8),​(1,​6),​ (1,​3)}.Costul său se calculează însumând costurile tuturor muchiilor:+
  
-Cost(MuchiiAMA) = 1 + 2 + 3 + 4 + 5 + 7 + 9 = 31 +</​spoiler>​ \\
  
-Alţi arbori minimi de acoperire pentru exemplul propus sunt: 
-* {(1,​4),​(7,​8),​(5,​6),​(1,​2),​(5,​8),​(4,​5),​ (1,3)} 
-* {(1,​4),​(7,​8),​(5,​6),​(2,​4),​(5,​8),​(1,​6),​ (1,3)} 
-* {(1,​4),​(7,​8),​(5,​6),​(2,​4),​(5,​8),​(4,​5),​ (1,3)}. 
  
-Pentru alte exemple explicate consultaţi [2], [3] şi [5].+===== Algoritmi =====
  
-===== Complexitate =====+Pentru **minimum spanning tree problem** există mai mulți algoritmi, dintre care amintim:
  
-Timpul de execuţie depinde de implementarea structurilor de date pentru mulţimi disjuncteVom presupune ​că se foloseşte ​pădure cu mulţimi disjuncte ​[2][11][12]. Iniţializarea se face într-un timp O(|V|). Sortarea muchiilor ​în funcţie de cost se face în O(|E|log|E|)În bucla principală se execută |E| operaţii care presupun două operaţii de găsire a subarborilor din care fac parte extremităţile muchiilor şi eventual o reuniune a acestor arbori, într-un timp O(|E|log|E|). +  * **Kruskal**:​ abordare greedy care se bazează pe sortarea muchiile după costMai multe detalii în **Introduction to Algorithms** [0]. 
 +  * **Prim**: abordare greedy care extinde arborele ​utat pornind de la 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].
  
-Decicomplexitatea totală este: O(|V|) + O(|E|log|E|) + O(|E|log|E|) = O(|E|log|E|).+În acest laborator vom studia și vom implementa **algoritmul lui Kruskal**. De asemeneaoferim ca studiu de caz pentru acasă, un material pentru algoritmul lui Prim.
  
-Cum |E| <|V|^2, şi O(log(|V|^2)) ​O(2*log(|V|)) ​O(log(|V|)),​ rezultă o complexitate O(|E|log|V|).+===== Kruskal =====
  
-===== Algoritmul ​Prim =====+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!
  
-Algoritmul a fost prima oară dezvoltat în 1930 de matematicianul ceh Vojtěch Jarnik, şi independent în 1957 de informaticianul Robert Prim, al cărui nume l-a luat. Algoritmul consideră iniţial că fiecare nod este un subarbore independent,​ ca şi Kruskal. Însă spre deosebire de acesta, nu se construiesc mai mulţi subarbori care se unesc şi în final ajung să formeze AMA, ci există un arbore principal, iar la fiecare pas se adaugă acestuia muchia cu cel mai mic cost care uneşte un nod din arbore cu un nod din afara sa. Nodul rădăcină al arborelui principal se alege arbitrar. Când s-au adăugat muchii care ajung în toate nodurile grafului, s-a obţinut AMA dorit. Abordarea seamănă cu algoritmul Dijkstra de găsire a drumului minim între două noduri ale unui graf.+==== Kruskal - Pseudocod ====
  
-Pentru o implementare eficientă, următoarea muchie de adăugat la arbore trebuie să fie uşor de selectat. Vârfurile care nu sunt în arbore trebuie sortate în funcţie de distanţa până la acesta (de fapt costul minim al unei muchii care leagă nodul dat de un nod din interiorul arborelui). Se poate folosi pentru aceasta o structură de heap. Presupunând că (u, v) este muchia de cost minim care uneşte nodul u cu un nod v din arbore, se vor reţine două informaţii:​ 
  
-  * d[u] = w[u,v] distanţa de la u la arbore +<​note>​
-  * p[u] = v predecesorul lui u în drumul minim de la arbore la u.+
  
-La fiecare pas se va selecta nodul u cel mai apropiat de arborele principal, reunind apoi arborele principal cu subarborele corespunzător nodului selectat. Se verifică apoi dacă există noduri mai apropiate ​de u decât de nodurile ​care erau anterior în arbore, caz în care trebuie modificate distanţele dar şi predecesorul. Modificarea unei distanţe impune şi refacerea structurii de heap.+Algoritmul lui Kruskal folosește o structură de date care suportă 2 operații:
  
-===== Pseudocod =====+  * Î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> <code cpp>
-Prim(G(V,E), w, root) +// apply Kruskal'​s algorithm for computing a Minimum Spanning Tree (MST). 
-1. MuchiiAMA <- ∅; +// 
-2. for each u in V do +// nodes     = list of all nodes from G 
-3. d[u] = INF; //iniţial distanţele sunt infinit +// edges     = the list of all edges in G 
-4. p[u] = NIL; //şi nu există predecesori +//             example: edge (nodeneigh, weight) 
-5. d[root] = 0; //distanţa de la rădăcină la arbore e 0 +// 
-6. H = Heap(V,d); //se construieşte heap-ul +// returns: cost, mst 
-7. while (H not empty) do //cât timp mai sunt noduri neadăugate +//          cost the cost of the MST 
-8. u GetMin(H); //se selectează cel mai apropiat nod u +//          mst = the actual set of edges in MST 
-9. MuchiiAMA = MuchiiAMA ∪ {(u, p[u])};//se adaugă muchia care uneşte u cu un nod din arborele principal ​ +// 
-10. for each v in Adj(udo +Kruskal(G=(nodes, edges)) { 
- //pentru toate nodurile adiacente lui u se verifică dacă +  // STEP 0: initialize results 
- //trebuie făcute modificări +  cost = 0; // cost of the built MST 
-11. if w[u][v] < d[v] then +  ​mst ​{}; // the actual set of edges (uvin the built MST
-12. d[v] ​w[u][v]; +
-13. p[v] = u; +
-14. Heapify(v,​ H); //refacerea structurii de heap +
-15. MuchiiAMA = MuchiiAMA \ {(rootp[root])}; +
-16. return MuchiiAMA;​ +
-</​code> ​+
  
-===== Exemplu de rulare =====+  // STEP 1: initialize disjoint set data structure 
 +  // (a tree is created for each node) 
 +  disjointset ​DisjointSet(nodes);​
  
-Se consideră graful folosit pentru exemplificarea algoritmului Kruskal.+  // STEP 2: 
 +  sort(edges, compare=nondecreasing order by weight);
  
-Iniţial fiecare nod reprezintă un arbore independent,​ şi are o culoare unicăSe alege ca rădăcină a arborelui principal nodul 1. +  // 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);
  
-{{ pa:laboratoare:​99.jpg |}}  +      // STEP 3.3Extend MST with the current edge. 
-**Pas 1**+      cost += w; 
 +      mst += { (u, v) }
 +    ​
 +  }
  
-Se alege muchia de cost minim care uneşte rădăcina cu un alt nod: (1,4) de cost 1 si se adaugă arborelui principal. +  return ​cost, mst; 
-{{ pa:​laboratoare:​910.jpg |}}  +}
-MuchiiAMA = {(1,4)}.+
  
-**Pas 2**+// 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.
  
-Se alege muchia de cost minim care uneşte 1 sau 4 cu un alt nod. (1,2) ş(2,4) au ambele costul 4. Se alege una dintre elefie ea (1,2). +  * Dacă o muchie $(uv)$ are capetele în arbori diferiți, atunci prin adăugarea acesteia, nu vom crea cicluci vom crea un arbore mai mare (din cei mici reuniți). 
-{{ pa:​laboratoare:​911.jpg |}}  +  * Altfelnu putem adăuga muchiadeoarece am crea un ciclu. Această muchie se ignoră.
-MuchiiAMA = {(1,4),(1,2)}.+
  
-**Pas 3**+==== Exemple ====
  
-Se alege următoarea muchie care uneşte {1,4,2} cu alt nod. (1,6) şi (4,5) au acelaşi cost, dar adaugă arborelui noduri diferite. La acest pas se selectează de exemplu (1,6). Selectând la acest pas (4,5) s-ar obţine un alt arbore de acoperire. +=== Exemplu Kruskal ===
-{{ pa:​laboratoare:​912.jpg |}}  +
-MuchiiAMA ​{(1,​4),​(1,​2),​(1,​6)}.+
  
-**Pas 4**+{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab11-kruskal.png?​900| Exemplu Kruskal}}
  
-Se caută muchia de cost minim care uneşte ​{1,4,2,6} cu un alt nod, şi se găseşte (6,5de cost 3. Nodul 5 va fi adăugat arborelui principal. +Un **MST** găsit cu algoritmul lui Kruskal este: $(1, 2)(2, 4)(2, 3)(3, 5)}$ de cost **5**.
-{{ pa:​laboratoare:​913.jpg |}}  +
-MuchiiAMA = {(1,4),(1,2),(1,6),(6,5)}.+
  
-**Pas 5**+<spoiler Explicație pas cu pas>
  
-Se alege muchia de cost minim care uneşte {1,4,​2,​6,​5} ​cu un alt nod. (5,8) de cost 5 se adaugă listei de muchii. +În exemplul atașatavem un graf **neorientat** ​cu următoare configurație:
-{{ pa:laboratoare:​914.jpg |}}  +
-MuchiiAMA = {(1,​4),​(1,​2),​(1,​6),​(5,​6),​(5,​8)}+
  
-**Pas 6**+  ​''​%%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.
  
-Se alege muchia ​de cost minim (8,7) care uneşte {1,4,2,6,5,8} cu nodul 7. +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)(23)(35)}}$), dar cu același cost **5**.
-{{ pa:​laboratoare:​915.jpg |}}  +
-MuchiiAMA = {(1,4),(1,2),(1,6),(5,6),(5,8),(8,7)}.+
  
-**Pas 7** +</​spoiler>​ \\
-{{ pa:​laboratoare:​916.jpg |}}  +
-Se alege muchia (1,3) de cost 9, care uneşte arborele principal cu ultimul nod ramas, şi se adaugă mulţimii de muchii. Cum toate nodurile sunt acoperite, am obţinut un arbore minim de acoperire.+
  
-MuchiiAMA = {(1,​4),​(1,​2),​(1,​6),​(5,​6),​(5,​8),​(8,​7),​(1,​3)}. 
  
-Cost(MuchiiAMA) ​1 + 4 + 7 + 3 + 5 + 2 + 9 31 +==== Complexitate ====
  
-Alţi arbori minimi de acoperire pentru exemplul propus se pot obţine alegând diferit muchiile cu acelaşi cost (vezi paşii 2 şi 3.+  * **complexitate temporală**:​ $T = O(m * log m)\ sau\ O(|E| * log |E|)$ 
 +  * **complexitate spațială** : $S = O(n)$
  
-Pentru alte exemple explicate consultaţi [2], [4] şi [6].+<spoiler Detalii (analiză + optimizări)>​
  
-===== Complexitate =====+  * **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.
  
-Iniţializările se fac în O(|V|). Bucla principală while se execută de |V| ori. Procedura GetMin() are nevoie de un timp de ordinul O(lg|V|), deci toate apelurile vor dura O(|V|lg|V|). Bucla for este executată în total de O(|E|) ori, deoarece suma tuturor listelor de adiacenţă este 2|E|. Modificarea distanţei, a predecesorului,​ şi refacerea heapului se execută într-un timp de O(1), O(1) şi respectiv O(lg|V|). Deci în total bucla interioară for durează O(|E|lg|V|).+</​spoiler>​ \\
  
-În consecinţă,​ timpul total de rulare este O(|V|lg|V|+|E|lg|V|),​ adică O(|E|lg|V|). Aceeaşi complexitate s-a obţinut şi pentru algoritmul Kruskal. Totuşi, timpul de execuţie al algoritmului Prin se poate îmbunătăţi până la O(|E|+|V|lg|V|),​ folosind heap-uri Fibonacci. 
  
-===== Concluzii ​=====+===== [Studiu de caz] Prim =====
  
-Un arbore minim de acoperire al unui graf este un arbore care conţine toate nodurile, şi în plus acestea sunt conectate prin muchii care asigură un cost total minimDeterminarea unui arbore minim de acoperire pentru un graf este o problemă cu aplicaţii ​în foarte multe domenii: reţeleclusteringprelucrare ​de imagini. Cei mai cunoscuţi algoritmiPrim şi Kruskalrezolvă problema în timp polinomial. Performanţa algoritmilor depinde ​de modul de reprezentare a structurilor de date folosite+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 = (VE)** 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.
  
-===== Referinţe =====+==== Prim Pseudocod ​====
  
-[1] – [[http://​en.wikipedia.org/​wiki/​Minimum_spanning_tree]]+<spoiler Prim - Pseudocod>​
  
-[2] – TCormenCLeisersonRRivestCStein – Introducere în Algoritmicap24+<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( (sourced[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 += { (nodep[node]) }; 
 +    } 
 + 
 +    // STEP 3.3: relax all edges (nodeneigh) 
 +    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>​ \\
  
-[3] – [[http://​en.wikipedia.org/​wiki/​Kruskal%27s_algorithm]] 
  
-[4] – [[http://​en.wikipedia.org/​wiki/​Prim%27s_algorithm]]+==== Complexitate ====
  
-[5] – [[http://​w3.cs.upt.ro/​~calin/​resources/​sdaa/​kruskal.ppt]]+  * **complexitate temporală**$T = O(m * log n)\ sau\ O(|E| * log |V|)$ 
 +  * **complexitate spațială** : $S = O(n)$
  
-[6] – [[http://​www.cs.upt.ro/​~calin/​resources/​sdaa/​prim.ppt]]+<spoiler Detalii (analiză + optimizări)>​
  
-[7] – [[http://www.cs.princeton.edu/​~wayne/​kleinberg-tardos/​04mst.pdf]]+  * **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)$.
  
-[8] – [[http://​hc.ims.u-tokyo.ac.jp/​JSBi/​journal/​GIW01/​GIW01F03.pdf]]+</spoiler> \\
  
-[9] – [[http://​www4.ncsu.edu/​~zjorgen/​ictai06.pdf]] 
  
-[10] – C. Giumale – Introducere în Analiza Algoritmilor,​ cap.5.5+===== TLDR =====
  
-[11] - [[https://​www.infoarena.ro/problema/​disjoint|Păduri ​de mulțimi disjuncte - Infoarena]]+  * 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**.
  
-[12] - [[https://​en.wikipedia.org/​wiki/​Disjoint-set_data_structure|Păduri de mulțimi disjuncte - Wikipedia]]+===== Exerciții =====
  
-====== Exercitii ====== 
  
 <​note>​ <​note>​
-In acest laborator vom folosi scheletul de laborator din arhiva {{pa:​new_pa:​skel-lab10.zip}}. 
-</​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> <note warning>
-Inainte de a rezolva exercitiile,​ asigurati-va ca ati citit si inteles toate precizarile din sectiunea 
-[[https://​ocw.cs.pub.ro/​courses/​pa/​skel_graph | Precizari laboratoare 07-12]]. 
  
-Prin citirea acestor ​precizari va asigurati ca+Î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]]. 
-   ​cunoasteti ​**conventiile** folosite + 
-   ​evitati ​**buguri** +Prin citirea acestor ​precizări vă asigurați că
-   ​evitati ​**depunctari** la lab/​teme/​test+ 
 +  ​știți ​**convențiile** folosite 
 +  evitați ​**buguri** 
 +  evitați ​**depunctări** la lab/​teme/​test 
  
 </​note>​ </​note>​
  
-=== Kruskal ===+==== 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ă.
  
-Se da un graf **neorientat conex** cu **n** noduri si **m** muchii. 
-Folositi algoritmul lui Kruskal pentru a calcula costul minim al unui arbore de acoperire. 
  
 <note warning> <note warning>
-Restrictii si precizari:+ 
 +Restricții și precizări: 
   * $ n <= 2 * 10^5 $   * $ n <= 2 * 10^5 $
   * $ m <= 4 * 10^5 $   * $ m <= 4 * 10^5 $
-  * $ -10^3 <= c <= 10^3 $, unde $ceste costul unei muchii+  * $ -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>​ </​note>​
  
-=== Prim === +==== BONUS ==== 
-Aceeasi cerinta si aceleasi restrictii ca mai susdoar ca se cere gasirea costului minim al unui arbore de acoperire folosind algoritmul lui Prim+ 
 +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. KleinRobert 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.1555697487.txt.gz · Last modified: 2019/04/19 21:11 by radu.iacob
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