La presentazione è in caricamento. Aspetta per favore

La presentazione è in caricamento. Aspetta per favore

Lezione 13 Strutture dati per insiemi disgiunti Alberi di copertura minimi.

Presentazioni simili


Presentazione sul tema: "Lezione 13 Strutture dati per insiemi disgiunti Alberi di copertura minimi."— Transcript della presentazione:

1 Lezione 13 Strutture dati per insiemi disgiunti Alberi di copertura minimi

2 Sommario Strutture dati per insiemi disgiunti Determinazione delle componenti connesse di un grafo Alberi di copertura minimi per grafi pesati –Algoritmo di Kruskal –Algoritmo di Prim

3 Strutture dati per insiemi disgiunti In alcune applicazioni siamo interessati a mantenere gruppi distinti di oggetti per queste applicazioni risultano importanti operazioni quali: determinare a quale insieme appartiene un oggetto e unire più insiemi

4 Insiemi disgiunti Una struttura dati per insiemi disgiunti mantiene una collezione S={S 1,S 2,…,S k } di insiemi dinamici disgiunti ogni insieme S i è identificato da un rappresentante un rappresentante per un insieme S i può essere –un qualsiasi membro dell’insieme S i –un membro particolare dell’insieme S i

5 Rappresentante di un insieme Se il rappresentante di un insieme è un qualunque elemento allora: –a fronte di una serie di operazioni di ricerca del rappresentante di uno stesso insieme deve sempre essere restituito lo stesso rappresentante –solo nel caso in cui l’insieme venga modificato tramite l’unione con un altro insieme l’elemento rappresentante può cambiare

6 Rappresentante di un insieme Il rappresentante può essere un elemento specifico dell’insieme si devono definire delle caratteristiche degli insiemi e una regola per caratterizzare il rappresentante Ex: l’elemento più piccolo/grande di un insieme

7 Operazioni Dato un elemento x le operazioni definibili su una struttura dati per insiemi disgiunti sono: –Make-Set(x) –Union(x,y) –Find-Set(x)

8 Make-Set Crea un nuovo insieme il cui unico membro e rappresentante è x. Dato che gli insiemi sono disgiunti si deve garantire che x non appartenga già ad un altro insieme

9 Union Unisce due insiemi S x e S y che contengono gli elementi x e y. Si deve garantire che gli insiemi siano disgiunti. L’elemento rappresentante può essere un qualsiasi elemento dell’insieme unione S x  S y. Di solito si utilizza il rappresentante di S x o di S y come rappresentante finale. Dato che gli insiemi sono disgiunti l’operazione di unione deve distruggere i vecchi insiemi S x e S y.

10 Find-Set Restituisce il rappresentante dell’unico insieme contenete x

11 Implementazione tramite liste concatenate Ogni insieme viene rappresentato con una lista concatenata il primo oggetto di una lista viene utilizzato come rappresentante dell’insieme ogni elemento nella lista contiene: –un oggetto –un puntatore all’elemento successivo –un puntatore al rappresentante

12 Visualizzazione cheb

13 Operazioni Le operazioni Make-Set e Find-Set sono semplici e richiedono un tempo O(1): –Make-Set(x): crea una nuova lista concatenata in cui l’unico oggetto è x e inizializza il puntatore al rappresentante a x stesso –Find-Set(x): restituisce il puntatore da x al rappresentante L’operazione Union(x,y) richiede più tempo: la versione più semplice è quella in cui si appende la lista contentente x alla lista contentente y l’elemento rappresentante per l’unione è il rappresentante della lista contenente y

14 Visualizzazione dell’unione cheb fgdz chebfgdz xy Union(x,y)

15 Costo dell’unione L’operazione di Union richiede la modifica di tutti i puntatori al rappresentante dell’oggetto x e questo richiede in media un tempo lineare nella lunghezza della lista contentente x infatti se si creano n insiemi di un unico oggetto e si uniscono nel seguente ordine: –Union(x 1,x 2 ) –Union(x 2,x 3 ) –Union(x 3,x 4 ) –Union(x 4,x 5 ) –Union(x 5,x 6 ) si devono fare 1, 2, 3,…,n modifiche ai puntatori, per un totale di O(n 2 ) operazioni

16 Eurisitica peso Una strategia per diminuire il costo dell’operazione Union consiste nel: –memorizzare l’informazione della lunghezza della lista –appendere la lista più corta a quella più lunga risulta poi facile mantenere l’informazione di lunghezza della lista dopo l’unione in tempi O(1)

17 Implementazione tramite foreste Si può realizzare una implementazione più veloce delle strutture dati per insiemi disgiunti rappresentando ogni insieme tramite un albero radicato ogni nodo dell’albero contiene l’oggetto e un puntatore al padre l’oggetto rappresentante è l’oggetto contenuto nella radice dell’albero la radice ha come padre un puntatore a se stessa

18 Visualizzazione c e h b f d g c e h b f d g

19 Operazioni Make-Set(x): crea un albero con un unico nodo x e con padre se stesso Find-Set(x): risale la lista dei padri di x fino a trovare la radice e restituisce la radice come oggetto rappresentante Union-Set(x,y): determina le radici dei due alberi contenteti x e y e fa diventare la radice dell’albero contentente x un figlio della radice dell’albero contentente y

20 Euristiche Di per sé la rappresentazione tramite foreste non è più veloce della rappresentazione con liste, ma su questa rappresentazione è possibile introdurre delle euristiche che rendono questa implementazione la più veloce conosciuta Euristiche: –unione per rango –compressione dei cammini

21 Euristica del rango Definizioni: Si mantiene una informazione aggiuntiva per ogni nodo: il rango memorizzata nell’attributo rank[x] il rango di un nodo è il limite superiore all’altezza di x, ovvero il numero di archi del cammino più lungo fra x e una foglia sua discendente

22 Euristica del rango E’ simile all’unione pesata con le liste concatenate L’idea è di fare sì che mediamente l’albero più basso diventi un sottoalbero di quello più alto ovvero che la radice dell’albero più basso punti alla radice dell’albero più alto in una operazione di Union

23 Euristica del rango Nota:il rango non è l’altezza dell’albero, ma solo un limite superiore. Questo rende più semplice la sua manipolazione e meno costoso il suo mantenimento. In particolare se gli alberi sono manipolati in modo da diminuire l’altezza di un nodo, questo non si riflette sul rango che rimane inalterato

24 Euristica della compressione dei cammini La compressione dei cammini viene usata per le operazioni Find-Set(x) l’idea è di modificare un albero in modo che in ricerche successive di x l’elemento rappresentante venga restituito in O(1) per fare questo si modifica il puntatore al padre di ogni nodo sul cammino da x alla radice perché punti direttamente alla radice

25 Visualizzazione compressione dei cammini per l’operazione Find-Set(f) b e d f b edf

26 Visualizzazione compressione dei cammini per l’operazione Find-Set(d) b e d f b ed f

27 Spiegazione intuitiva Quando viene creato un nuovo insieme con Make- Set il rango iniziale dell’unico nodo nell’albero è 0 l’esecuzione dell’operazione Find-Set non modifica i ranghi –infatti non cambia il limite superiore dell’altezza (riduce l’altezza degli alberi, non la incrementa) per l’operazione di Union –si rende la radice con rango maggiore padre di quella con rango minore; il rango della radice non cambia –in caso di eguaglianza si sceglie arbitrariamente una delle due radici come padre; il rango della radice aumenta di 1

28 Pseudocodice Make-Set(x) 1p[x]  x 2rank[x]  0 Union(x,y) 1Link(Find-Set(x),Find-Set(y)) Link(x,y) 1if rank[x] > rank[y] 2thenp[y]  x 3elsep[x]  y 4if rank[x]=rank[y] 5thenrank[y]  rank[y]+1

29 Pseudocodice Find-Set(x) 1if x  p[x] 2then p[x]  Find-Set(p[x]) 3return p[x]

30 Find-Set La procedura Find-Set è una procedura a due passate: –nella prima si risale il cammino di accesso per determinare la radice –nella seconda si discende e si aggiornano tutti i nodi in modo che puntino alla radice se x=p[x], ovvero siamo arrivati alla radice, la procedura restituisce p[x]. Questo è il passo base della ricorsione. altrimenti si chiama ricorsivamente la procedura aspettando di determinare e poi restituire il puntatore alla radice

31 Analisi L’uso delle euristiche dell’unione per rango e della compressione dei cammini con la rappresentazione di foreste di insiemi disgiunti porta ad una efficienza pari a O(n) in tutti i casi di applicazioni pratiche con n pari al numero di operazioni Make,Union,Find che si vogliono eseguire sugli insiemi disgiunti.

32 Componenti connesse di un grafo Una applicazione delle strutture dati per insiemi disgiunti consiste nella determinazione delle componenti connesse di un grafo non orientato una componente connessa di un grafo G è un sottografo G’ tale per cui esiste un cammino semplice (tutti i vertici del cammino sono distinti) per ogni coppia di vertici di G’

33 Visualizzazione di componenti connesse di un grafo non orientato ab cd ef g h i j

34 Idea intuitiva L’idea è di partire da componenti connesse costituite da un unico vertice ogni componente connessa viene poi unita ad altre componenti connesse tramite l’operazione di Union l’unione fra due insiemi viene fatta se esiste un arco fra due qualsiasi vertici in tali insiemi

35 Idea intuitiva Infatti se abbiamo che: –un vertice x è raggiungibile da ogni altro vertice in un insieme S x –e analogamente per un vertice y in un insieme S y –ed esiste un arco che collega x e y allora ne consegue che l’unione fra S x e S y è un insieme connesso dato che da ogni elemento in S x si può adesso raggiungere un qualsiasi elemento in S y tramite l’arco (x,y) e viceversa

36 Pseudocodice Connected-Components(G) 1for ogni vertice v in V[G] 2doMake-Set(v) 3for ogni arco (u,v) in E[G] 4do if Find-Set(u)  Find-Set(v) 5then Union(u,v)

37 Esempio ArcoCollezione insiemi disgiunti abcdefghi (b,d)ab,dcefghi (e,g)ab,dce,gfhi (a,c)a,cb,de,gfhi (h,i)a,cb,de,gfh,i (a,b)a,c,b,de,gfh,i (e,f)a,c,b,de,g,fh,i (b,c)a,c,b,de,g,fh,i ab cd ef g h i

38 Albero di copertura minimo Un problema di notevole importanza consiste nel determinare come interconnettere fra di loro diversi elementi minimizzando certi vincoli sulle connessioni un esempio classico è quello della progettazione dei circuiti elettronici dove si vuole minimizzare la quantità di filo elettrico per collegare fra loro i morsetti di diversi componenti

39 Albero di copertura minimo il problema può essere modellato con un grafo non orientato e connesso in cui le interconnessioni sono archi pesati (u,v) e dove il peso specifica il costo per connettere u con v la soluzione del problema consiste nella determinazione di un sottografo aciclico T  E che connetta tutti i vertici in modo da minimizzare il peso totale w(t)=  (u,v)  T w(u,v) dato che T è aciclico e collega tutti i vertici deve essere un albero tale albero è chiamato albero di copertura minimo o minimum spanning tree MST

40 Esempio bcd a hgf ei

41 Unicità L’albero di copertura minimo non è unico ad esempio si possono dare due albero di copertura minimi per il grafo in esame bcd a hgf ei bcd a hgf ei

42 Algoritmo generico Verranno illustrati due algoritmi di tipo “greedy” o golosi: –Algoritmo di Kruskal –Algoritmo di Prim L’approccio greedy consiste nello scegliere fra più alternative quella più conveniente sul momento Nota: in generale non è detto che in ogni tipo di problema questo porti ad una soluzione globalmente ottima. Per la soluzione del problema dell’albero di copertura minima una soluzione greedy coincide con una soluzione globalmente ottima

43 Arco sicuro L’idea è di accrescere un sottoinsieme A di archi di un albero di copertura aggiungendo un arco alla volta Ad ogni passo si determina un arco che può essere aggiunto ad A mantenendo la proprietà per A di essere un sottoinsieme di archi di un albero di copertura Un arco di questo tipo è detto arco sicuro

44 Pseudocodice Generic-MST(G,w) 1A   2while A non forma un albero di copertura 3dotrova un arco sicuro (u,v) 4A  A  {(u,v)} 5return A

45 Algoritmi concreti Per poter implementare l’algoritmo generico abbiamo bisogno di determinare gli archi sicuri per caratterizzare gli archi sicuri dobbiamo introdurre alcune definizioni: –un taglio (S,V-S) di un grafo non orientato G=(V,E) è una partizione di V –un arco attraversa il taglio se uno dei suoi estremi è in S e l’altro è in V-S –un taglio rispetta un insieme di archi A se nessun arco di A attraversa il taglio –un arco leggero è un arco con peso minimo

46 Visualizzazione dei concetti bcd a hgf ei Insieme A: archi in grigio il taglio rispetta A Taglio Arco leggero che attraversa il taglio 10

47 Archi sicuri La regola per riconoscere gli archi sicuri è data dal seguente: Teorema: –Sia G=(V,E) un grafo non orientato e connesso con una funzione peso w a valori reali definita su E. Sia A un sottoinsieme di E contenuto in un qualche albero di copertura minimo per G. Sia (S,V-S) un qualunque taglio che rispetta A. Sia (u,v) un arco leggero che attraversa il taglio. Allora l’arco (u,v) è sicuro per A

48 Visualizzazione arco non sicuro perché taglio non rispetta bcd a hgf ei bcd a hgf ei bcd a hgf ei Arco sicuro Arco non sicuro

49 Visualizzazione arco non sicuro perché non leggero bcd a hgf ei bcd a hgf ei bcd a hgf ei Arco sicuro Arco non sicuro

50 Archi sicuri Dimostrazione: –sia T l’albero di copertura minimo che contiene A –l’arco (u,v) o appartiene a T, e quindi è sicuro per A –oppure non appartiene a T: –in questo caso faremo vedere che sostituendo un arco in T con il nuovo arco (u,v) otteniamo un T’ che è sempre un albero di copertura minimo infatti deve esserci un arco (x,y) di T che attraversa il taglio perché T è un insieme connesso e quindi deve esserci un percorso fra u e v che sono da parti opposte del taglio inoltre (x,y) non è in A perché il taglio rispetta A se costruisco T’ come T-(x,y)+(u,v) ho creato un insieme connesso con costo complessivo  di T che copre tutti i vertici e che ha costo minimo e quindi deve essere un albero di copertura minimo (in pratica (u,v) deve essere un arco con costo equivalente a (x,y))

51 Archi sicuri –se T’ è un albero di copertura minimo allora dato che A è compreso in T e che (x,y) non era in A allora A è anche compreso in T’ –infatti l’unico cambiamento da T in T’ è stato solo per l’arco (x,y) e (u,v) che non sono in A, gli altri archi sono rimasti inalterati –di conseguenza aggiungere (u,v) ad A mantiene A un sottoinsieme dell’albero di copertura (T’ questa volta) e dunque è un arco sicuro per A

52 Archi sicuri Corollario: –Sia G=(V,E) un grafo non orientato e connesso con una funzione peso w a valori reali definita su E. Sia A un sottoinsieme di E contenuto in un albero di copertura minimo per G e sia C una componente connessa (un albero) nella foresta G A =(V,A). Se (u,v) è un arco leggero che connette C a qualche altra componente in G A allora (u,v) è sicuro per A Dimostrazione: –il taglio (C,V-C) rispetta A: quindi l’arco leggero (u,v) è un arco sicuro per A per il teorema precedente

53 Algoritmo di Kruskal L’idea dell’algoritmo di Kruskal è di ingrandire sottoinsiemi disgiunti dell’albero di copertura minimo connettendoli fra di loro fino ad avere l’albero complessivo in particolare si individua un arco sicuro da aggiungere alla foresta scegliendo un arco (u,v) di peso minimo tra tutti gli archi che connettono due distinti alberi (componenti connesse) della foresta l’algoritmo è greedy perché ad ogni passo si aggiunge alla foresta un arco con il peso minore possibile

54 Algoritmo di Kruskal L’idea è che, come richiesto dall’algoritmo astratto: –ogni componente connessa in A appartiene all’albero di copertura –unendo componenti connesse tramite archi leggeri (per il corollario) si stanno aggiungendo archi sicuri –ad ogni fusione di componenti connesse stiamo espandendo A –si può continuare fino a quando non si sono acquisiti tutti i vertici del grafo

55 Implementazione L’algoritmo presentato è simile a quello usato per calcolare le componenti connesse si usa una struttura dati per insiemi disgiunti ogni insieme contiene i vertici di un albero della foresta corrente si può determinare se due vertici appartengono allo stesso albero verificando l'eguaglianza degli elementi rappresentanti restituiti da Find-Set si fondono due alberi tramite la Union

56 Pseudocodice Kruskal MST-Kruskal(G,w) 1A   2for ogni vertice v in V[G] 3do Make-Set(v) 4ordina gli archi di E per peso w non decrescente 5for ogni arco (u,v) in E in ordine di peso non decrescente 6doif Find-Set(u)  Find-Set(v) 5then A  A  {(u,v)} 6Union(u,v) 7return A

57 Spiegazione dello pseudocodice Le linee 1-3 inizializzano l’insieme A con l’insieme vuoto e creano |V| alberi, uno per ogni vertice la linea 4 ordina gli archi per peso nelle linee 5-8 il ciclo for controlla che i vertici di ogni arco appartengano ad alberi diversi in caso affermativo –l’arco viene aggiunto ad A –la linea 8 fonde i due alberi in un unico insieme –Nota: se i vertici appartenessero allo stesso albero collegheremmo due vertici di un albero ottenendo un ciclo, facendo venire meno la condizione di aciclicità del sottografo di ricoprimento

58 Visualizzazione bcd a hgf ei bcd a hgf ei bcd a hgf ei bcd a hgf ei

59 Visualizzazione bcd a hgf ei bcd a hgf ei bcd a hgf ei bcd a hgf ei

60 Visualizzazione bcd a hgf ei bcd a hgf ei bcd a hgf ei bcd a hgf ei

61 Visualizzazione bcd a hgf ei bcd a hgf ei

62 Analisi Il tempo di esecuzione per l’algoritmo di Kruskal dipende dalla realizzazione della struttura dati per insiemi disgiunti se si utilizza la realizzazione con foreste con le euristiche: –l’inizializzazione richiede O(V) –il tempo necessario per ordinare gli archi è O(E lg E) –in totale si fanno O(E) operazioni sulla foresta di insiemi disgiunti, il che richiede un tempo O(E) In totale il tempo di esecuzione dell’algoritmo di Kruskal è O(V+E lg E + E)=O(E lg E)

63 Algoritmo di Prim L’algoritmo di Prim procede mantenendo in A un singolo albero (una singola componente connessa) l’albero parte da un vertice arbitrario r (la radice) e cresce fino a quando non ricopre tutti i vertici ad ogni passo viene aggiunto un arco leggero che collega un vertice in A con un vertice in V-A per il corollario questi archi sono sicuri per A e quindi quando l’algoritmo termina, in A vi è un albero di copertura minimo anche questo algoritmo è greedy poiché l’albero viene esteso ad ogni passo scegliendo l’arco di peso minimo tra quelli possibili

64 Algoritmo di Prim In questo caso A ha una unica componente connessa che è l’intero albero di copertura che sta crescendo il taglio (A, V-A) rispetta A e quindi per il corollario qualsiasi arco leggero che attraversa il taglio è un arco sicuro

65 Algoritmo di Prim Per avere un algoritmo efficiente si deve prestare attenzione a come rendere facile la scelta di un nuovo arco da aggiungere ad A questo viene fatto memorizzando tutti i vertici che non sono nell’albero in costruzione in una coda con priorità Q per ogni nodo la priorità è basata su un campo key[v] che contiene il minimo tra i pesi degli archi che collegano v ad un qualunque vertice dell’albero in costruzione per ogni nodo si introduce un campo parent p[x] che serve per poter ricostruire l’albero

66 Algoritmo di Prim L’insieme A è mantenuto implicitamente come A={(v,p[v]) : v in V - {r} - Q} quando l’algoritmo termina Q è vuota e l’albero di copertura in A è dunque: A={(v,p[v]) : v in V - {r}}

67 Pseudocodice Prim MST-Prim(G,w,r) 1Q  V[G] 2for ogni u in Q 3do key[u]   4key[r]  0 5p[r]  NIL 6while Q   7dou  Extract-Min(Q) 8for ogni v in Adj[u] 9doif v in Q e w(u,v)

68 Spiegazione pseudocodice Le linee 1-4 inizializzano la coda Q con tutti i vertici e pongono a  l’attributo key per ogni vertice ad eccezione del vertice r per il quale key[r]=0 in modo da estrarre r come elemento minimo nella fase iniziale durante l'algoritmo l’insieme V-Q contiene i vertici dell’albero che si sta costruendo la linea 7 identifica un vertice u incidente su di un arco leggero che attraversa il taglio (V-Q,Q) si elimina u da Q e lo si aggiunge ai vertici dell’albero

69 Spiegazione pseudocodice Le linee 8-11 aggiornano i campi key e p di ogni vertice v adiacente a u che non appartiene ancora all’albero il fatto che il campo key[v] rappresenti il costo minimo tra i pesi degli archi che collegano v ad un qualunque vertice dell’albero in costruzione è preservato perché se si trova un arco che collega v con l’albero (in realtà con il vertice u corrente) che costa meno, si aggiorna key al nuovo valore

70 Spiegazione dello pseudocodice Al ciclo successivo si esaminerà la coda Q e si troverà che uno dei v esaminati precedentemente è diminuito tanto da essere il vertice con chiave più piccola allora si aggiungerà implicitamente v all’albero, fissando la relazione padre-figlio migliore trovata e si procederà ad espandere la frontiera dei vertici adiacenti a v, stabilendo nuove potenziali relazioni padre-figlio

71 Visualizzazione bcd a hgf ei bcd a hgf ei bcd a hgf ei bcd a hgf ei

72 Visualizzazione bcd a hgf ei bcd a hgf ei bcd a h gf ei bcd a h gf ei

73 Visualizzazione bcd a h gf ei

74 Analisi L’efficienza dell’algoritmo di Prim dipende da come viene realizzata la coda con priorità Q se Q viene realizzata con uno heap binario: –si usa Build-Heap per l’inizializzazione in tempo O(V) –il ciclo 6 viene eseguito |V| volte ed ogni operazione Extract- Min è O(lg V) –il ciclo for in 8 viene eseguito in tutto O(E) volte –il controllo di appartenenza in 9 può essere eseguito in O(1) usando un metodo di indirizzamento diretto –l’assegnazione in 11 implica una operazione di Decrease- Key sullo heap che costa O(lg V) Il tempo totale è pertanto un O(V+V lg V + E lg V)=O(E lg V) asintoticamente eguale a quello per l’algoritmo di Kruskal


Scaricare ppt "Lezione 13 Strutture dati per insiemi disgiunti Alberi di copertura minimi."

Presentazioni simili


Annunci Google