COPYRIGHT SEGO LICENSE Questo documento viene fornito così come è: se pensate che faccia schifo problemi vostri, nessuno vi obbliga a leggerlo. Se pensate che sia qualcosa di positivo e/o avete suggerimenti su come migliorarlo o altro materiale da aggiungere mandatemi pure una mail all indirizzo di posta che trovate sulla mia home page. Sarò felice di rispondervi e di collaborare con voi al miglioramento del documento stesso. In caso vogliate darmi una mano nella realizzazione di altri appunti mandatemi pure una mail. Se volete aiutarmi nella correzione di questo documento (nessuno è perfetto ) scrivetemi pure. Si prega di lasciare questo documento integro o almeno se lo modificate, lasciate un riferimento all autore originale del testo, magari indicando la sua pagina web. SEGO owner/webmaster of theskulls.com e-mail: sego@the-skulls.com VISITA IL SITO http://www.the-skulls.com PER ALTRO MATERIALE E GUIDE
ALBERI DI COPERTURA MINIMI Definizione 1 Dati G(V,E) e G (V,E ) sottografo di G, G si dice sottografo di copertura se tutti gli archi di G sono incidenti a tutti i vertici di G. FIGURA 20. posso togliere questo arco e resta un sottografo di copertura In verde il sottografo di copertura Nota: Per verificare se e ricoprente basta verificare che tutti i vertici in V sia toccati, utilizzando gli archi che si hanno a disposizione in E. Albero di copertura: un grafo di copertura che allo stesso tempo e anche albero(ossia e connesso e aciclico allo stesso tempo: E = V -1). Nota: Possono esistere anche piu alberi di copertura. FIGURA 21. b c e a d NOTA: Non posso più aggiungere un arco senza creare un ciclo f Definizione 2 Si dicono grafi pesati, dei grafi ai quali viene associata una funzione peso che caratterizza gli archi. W: E R funzione peso Dato T E abbiamo che W(T) = w( u, v) dove W(T) e il peso totale FIGURA 22. ( u, v) T a 5 b 2 6 c 8 11 7 3 d e 2 NOTA: In verde il minimum spannin tree 10 f
A noi interessera trovare il MST (Minimum Spannin Tree) ossia l albero di copertura il cui peso e minimo. Definizione 3 Dato G(V,E) grafo non orientato, connesso e A E Preso (u,v) E e (u,v) A si dice che (u,v) e sicuro per A se A MST A {(u,v)} MST ALGORITMO GENERALE PER MST 1. A:=Ø 2. while A non forma un MST (A < V -1) 3. trova un arco (u,v) sicuro per A 4. A:=A {(u,v)} 5. return A Definizione 4 Taglio: e una partizione del tipo (S,V-S) con S V Definizione 5 (u,v) E attraversa il taglio (S,V-S) se u S v V-S Definizione 6 (u,v) E e un arco leggero per il taglio (S,V-S) se - (u,v) attraverso il taglio - w(u,v) <= w(x,y) x,y E che attraversa (S,V-S) ossia e un arco che attraversa il taglio e che ha peso minore o uguale a qualsiasi altro arco che attraversa il taglio Definizione 7 Il taglio (S,V-S) rispetta A E se nessun arco di A attraverso il taglio TEOREMA ALLA BASE DEGLI ALGORITMI MST Sia G(V,E) un grafo non orientato e connesso e a) A E contenuto in qualche MST b) (S,V-S) un taglio qualsiasi che rispetti A c) (u,v) E un arco leggero per (S,V-S) Allora (u,v) e un arco sicuro per A. Dimostrazione A {( u, v)} in qualche MST A E T sia un MST con A T FIGURA 23. S x y V \ S (x,y) T u v
Supponiamo che (u,v) T T =(T \ {(x,y)}) {(u,v)} W( T )=W(T)-W(x,y)+W(u,v) Siccome (u,v) è arco leggero W(u,v) W(x,y) W(T) W( T ) W(T) W( T )=W(T) (u,v) è sicuro per A? sì infatti: A {(u,v)} T, con (x,y) A Un COROLLARIO derivato da quanto detto sopra è il seguente: Sia G=(V,E) un grafo non orientato connesso e siano: (a) A E contenuto in un qualche MST (b) Dato G A = ( V, A) sia C componente connessa di G A (c) (u,v) un arco leggero che abbia un estremità in C e l altra in V \ C Allora (u,v) è sicuro per la foresta A. ALGORITMO DI KRUSKAL (G, w) Prima di entrare a parlare in dettaglio di questo algoritmo introduciamo il concetto di INSIEMI DISGIUNTI. Diamo un insieme S siffatto: S={ S 1, S2, S3,..., Sn } con S i S j = Ø i, j tale che i j NOTA: Si può variare dinamicamente. OSSERVAZIONI (alcune operazioni che si possono effettuare con gli insiemi disgiunti) 1) Make_set(u) 2) Find_set(u) 3) Union(u,v) Ricordiamo che ogni insieme disgiunto è identificato da un rappresentante. (1)MAKE_SET(u) Questa operazione non fa altro che creare un nuovo insieme contenente il solo elemento u {u} (2)FIND_SET(u) Questa operazione trova l indice i tale che u Si o per dirla in un altra maniera, si trova il rappresentante dell insieme a cui appartiene u. (3)UNION(u,v)
Unisce gli insiemi a cui appartengono u e v. NOTA: Nel caso di rappresentazione con liste: Make_set(u) viene creata una nuova lista con il solo elemento u Find_set(u) si scorrono le liste per trovare l elemento u (e per vedere a quale lista appartiene) Union(u,v) si concatenano le due liste. Nel caso di rappresentazione mediante alberi: Make_set(u) si crea un nuovo albero con il solo elemento u Find_set(u) viene fatta una ricerca all interno degli alberi (similmente quindi a quanto fatto per le liste) Union(u,v) viene creato un unico albero Per convenzione per quanto riguarda gli alberi si tende a considerare come rappresentante dell insieme la radice dell albero. KRUSKAL(G,w) 1. A:=Ø 2. foreach u V[G] do O(n) 3. Make_set(u) 4. ordina gli archi di E per peso non decrescente O(m log m) 5. foreach (u,v) E ordinato in modo non decrescente 6. if Find_set(u) Find_set(v) then 7. A:=A {(u,v)} 8. Union(u,v) 9. return A mα (n.m) dove α (n.m) <= 4 e α (n.m) = O(log m) O(m log m) Posto n= V e m= E, come si puo osservare da sopra la complessita totale e O(m log m) ALGORITMO DI PRIM (G, w, r) In Kruskal ad ogni passo dell esecuzione A e una foresta. In Prim si parte da un vertice e lo si espande mantenendo l invariante che formi un albero. IDEA BASE A Prendo l arco + leggero Devo mantenere due strutture dati particolari - key[u] - π [u] e l insieme Q che contiene i vertici non ancora visitati. All inizio avro che V-Q=Ø. Gli elementi di Q sono ordinati mediante key[u] abbiamo una coda a priorita In key[u] va memorizzato il peso dell arco piu leggero del tipo (u,v) Key[u]=min{w(u,v) v (V-Q) N(u)} dove N(u) insieme dei vertici adiacenti a u π [u]= predecessore di u nell albero (infatti che ho stabilito quale sia la radice all interno del mio albero libero, risulta automatica la relazione predecessore/successore)
PRIM(G, w, r) 1. Q:=V[G] 2. foreach u Q do 3. key[u]:= //inizializzo 4. key[r]=0 //chiave della sorgente o radice 5. π [r]=nil //predecessore della radice 6. while Q Ø do //finche ho ancora vertici da visitare 7. u=extractmin(q) 8. foreach v Adj[u] //per ogni vertice adiacente 9. if v Q && w(u,v) < key[v] then 10. key[v]=w(u,v) 11. π [v]=u 12. return A={(u, π [u]) E u V-{r}} COMPLESSITA PRIM (poniamo m= E, n= V ) L inizializzazione delle strutture dati linee 1-5 ha complessita O(n), devo infatti inizializzare V vertici. Il ciclo while, linea 6, verra eseguito O(n), infatti all inizio la coda con priorita contiene nnumero dei vertici. Il costo di una extractmin(q), linea 7, e O(log n), quindi il costo totale di tutte le extractmin sara di O(n log n). Le righe 8-11 hanno saranno eseguite al max O(m), perche poiche stiamo parlando di vertici adiacenti sappiamo che u deg( u) = 2 E. trovo un possible arco + leggero e eventualmente modifico i valori dell adiacente in questione Ricordiamo inoltre che la riga 10, che effettua una modifica della key[n], poiche usiamo una coda con priorita, impiega O(log n) (viene infatti fatta una DecreaseKey). Concludendo quindi possiamo dire che il blocco delle linee 6-11 ha complessita totale O(m log n). La complessita totale dell algoritmo e quindi O(n log n + m log n). Pero n log n <= m log n O(m log n) 2 Ma siccome m <= n log m <= 2 log n m log m <= 2 m log n Asintoticamente quindi possiamo dire O(m log m) = O(m log n). Prim risulta cosi essere asintoticamente uguale a Kruskal.