Strutture dati per insiemi disgiunti

Slides:



Advertisements
Presentazioni simili
Strutture dati per insiemi disgiunti
Advertisements

Estendere i linguaggi: i tipi di dato astratti
RB-alberi (Red-Black trees)
Hash Tables Indirizzamento diretto Tabelle Hash Risoluzioni di collisioni Indirizzamento aperto.
Code a priorità (Heap) Definizione
Alberi binari di ricerca
Insiemi disgiunti.
Capitolo 3 Strutture dati elementari Algoritmi e Strutture Dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano.
Capitolo 3 Strutture dati elementari Algoritmi e Strutture Dati.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl 1 Ordinamenti ottimi.
Capitolo 4 Ordinamento Algoritmi e Strutture Dati.
Algoritmi e Strutture Dati
Capitolo 3 Strutture dati elementari Algoritmi e Strutture Dati.
Algoritmi e Strutture Dati
Fondamenti di Informatica II Ingegneria Informatica / Automatica (A-I) Meccanica Prof. M.T. PAZIENZA a.a – 3° ciclo.
Fondamenti di Informatica II Ingegneria Informatica / Automatica (A-I) Meccanica Prof. M.T. PAZIENZA a.a – 3° ciclo.
Fondamenti di Informatica 2 Ingegneria Informatica Docente: Giovanni Macchia a.a
Esercizi su alberi binari
U V U V (a) |cfc|=2 prima e dopo (b) |cfc|=2 prima e |cfc|=1 dopo
Il problema del minimo albero ricoprente in un grafo con archi privati
Algoritmi e strutture Dati - Lezione 7
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl 1 Capitolo 1 Unintroduzione.
Algoritmi e Strutture Dati
Capitolo 4 Ordinamento Algoritmi e Strutture Dati.
Capitolo 9 Il problema della gestione di insiemi disgiunti (Union-find) Algoritmi e Strutture Dati.
Algoritmi e Strutture Dati
Interrogazioni su un albero binario di ricerca Search(S,k) – dato un insieme S ed un valore chiave k restituisce un puntatore x ad un elemento in S tale.
Algoritmi e Strutture Dati Alberi Binari di Ricerca.
Algoritmi e Strutture Dati (Mod. A)
Algoritmi e Strutture Dati
Algoritmi e strutture dati
Albero: insieme di punti chiamati NODI e linee chiamate EDGES
Algoritmi e Strutture Dati
Modello dati ALBERO Albero: Albero: insieme di punti chiamati NODI e linee chiamate EDGES EDGE: linea che unisce due nodi distinti Radice (root): in una.
Modello dati ALBERO Albero: Albero: insieme di punti chiamati NODI e linee chiamate EDGES EDGE: linea che unisce due nodi distinti Radice (root): in una.
Algoritmo di Kruskal Parte con tutti i vertici e nessun lato (sottografo aciclico, o foresta, ricoprente) Ordina i lati per costo non decrescente.
Sottoinsiemi disgiunti
Heap binari e HeapSort.
Heap binomiali Gli heap binomiali sono strutture dati su cui si possono eseguire efficientemente le operazioni: Make(H) : crea uno heap vuoto Insert(H,
Fondamenti di Informatica
Fibonacci Heaps e il loro utilizzo nell’algoritmo di Prim
Tavole dinamiche Spesso non si sa a priori quanta memoria serve per memorizzare dei dati in un array, in una tavola hash, in un heap, ecc. Può capitare.
Anche la RB-Delete ha due fasi: Nella prima viene tolto un nodo y avente uno dei sottoalberi vuoto sostituendolo con la radice dellaltro sottoalbero. Per.
Metodo della moltiplicazione
Esercizio 10.* Un cassiere vuole dare un resto di n centesimi di euro usando il minimo numero di monete. a) Descrivere un algoritmo goloso per fare ciò.
1.Scrivere una funzione per cercare un numero x in una lista circolare di interi. La funzione deve restituire NULL se il numero non esiste. 2.Scrivere.
Radix-Sort(A,d) // A[i] = cd...c2c1
Corso di informatica Athena – Periti Informatici
Cerchiamo di rispondere alla seconda domanda 2)La soluzione trovata con lalgoritmo goloso è ottima o esistono anche soluzioni con più di quattro attività?
Ispezione lineare La funzione hash h(k,i) si ottiene da una funzione hash ordinaria h'(k) ponendo L’esplorazione inizia dalla cella h(k,0) = h'(k) e continua.
RB-insert(T, z) // z.left = z.right = T.nil Insert(T, z) z.color = RED // z è rosso. Lunica violazione // possibile delle proprietà degli alberi // rosso-neri.
Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.
Heap Ordinamento e code di priorità Ugo de Liguoro.
Cammini minimi da un sorgente
Implementazione di dizionari Problema del dizionario dinamico Scegliere una struttura dati in cui memorizzare dei record con un campo key e alcuni altri.
Alberi Alberi radicati : alberi liberi in cui un vertice è stato scelto come radice. Alberi liberi : grafi non orientati connessi e senza cicli. Alberi.
Capitolo 9 Il problema della gestione di insiemi disgiunti (Union-find) Algoritmi e Strutture Dati.
Risoluzione delle collisioni con indirizzamento aperto Con la tecnica di indirizzamento aperto tutti gli elementi stanno nella tavola. La funzione hash.
Algoritmi e Strutture Dati
Strutture dati per insiemi disgiunti Alberi di copertura minimi
Algoritmi e strutture Dati - Lezione 7 1 Algoritmi di ordinamento ottimali L’algoritmo Merge-Sort ha complessità O(n log(n))  Algoritmo di ordinamento.
Capitolo 12 Minimo albero ricoprente Algoritmi e Strutture Dati.
1 Analisi ammortizzata Si considera il tempo richiesto per eseguire, nel caso pessimo, una intera sequenza di operazioni. Se le operazioni costose sono.
Capitolo 9 Union-find Algoritmi e Strutture Dati.
Capitolo 12 Minimo albero ricoprente: Algoritmo di Kruskal Algoritmi e Strutture Dati.
Codici prefissi Un codice prefisso è un codice in cui nessuna parola codice è prefisso (parte iniziale) di un’altra Ogni codice a lunghezza fissa è ovviamente.
Capitolo 9 Il problema della gestione di insiemi disgiunti (Union-find) Algoritmi e Strutture Dati.
Algoritmi e Strutture Dati HeapSort. Select Sort: intuizioni L’algoritmo Select-Sort  scandisce tutti gli elementi dell’array a partire dall’ultimo elemento.
Capitolo 12 Minimo albero ricoprente: Algoritmo di Kruskal Algoritmi e Strutture Dati.
Prof.ssa Rossella Petreschi Lezione del 17 /10/2014 del Corso di Algoritmica Lezione n°5.
Transcript della presentazione:

Strutture dati per insiemi disgiunti Servono a mantenere una collezione C = {S1, S2, ... , Sk} di insiemi disgiunti. Ogni insieme della collezione è individuato da un rappresentante che è uno degli elementi dell’insieme.

Operazioni sugli insiemi disgiunti: MakeSet(x) : aggiunge alla struttura dati un nuovo insieme contenente solo l’elemento x. Si richiede che x non compaia in nessun altro insieme della struttura. FindSet(x) : ritorna il rappresentante dell’insieme che contiene x. Union(x, y) : riunisce i due insiemi contenenti x ed y in un unico insieme.

Esercizio 29 Sia data una rete con n nodi x1, x2, … , xn ed m canali di trasmissione c1, c2, … , cm e per ogni canale ci siano dati i due nodi xi ed yi che tale canale connette direttamente. Descrivere un algoritmo che utilizza una struttura dati per insiemi disgiunti per determinare se la rete è connessa, ossia se è sempre possibile mandare un messaggio da un qualsiasi nodo ad un qualsiasi altro nodo.

Rappresentazione con liste Il modo più semplice per rappresentare una collezione di insiemi disgiunti è usare una lista circolare per ciascun insieme. c h e b

I nodi hanno i seguenti campi: info : l’informazione contenuta nel nodo r : il puntatore al rappresentante succ : il puntatore al nodo successivo Le operazioni sono: f x MakeSet(x) x.r = x x.succ = x FindSet(x) return x.r c h e x

Union(x,y) c h e b f g d x y c h e b f g d x y

Union(x, y) // cambia i puntatori r nella lista di y y.r = x.r, z = y.succ while z ≠ y z.r = x.r, z = z.succ // concatena le due liste z = x.succ, x.succ = y.succ, y.succ = z La complessità di Union dipende dal numero di iterazioni richieste dal ciclo che cambia i puntatori al rappresentante dei nodi della lista contenente y. Quindi Union ha complessità O(n2) dove n2 è la lunghezza della seconda lista.

Consideriamo la sequenza di 2n-1 operazioni: MakeSet(x1) // costo 1 MakeSet(x2) // costo 1 ....... MakeSet(xn) // costo 1 Union(x2, x1) // costo 1 Union(x3, x1) // costo 2 Union(x4, x1) // costo 3 Union(xn, x1) // costo n-1 Il costo totale è proporzionale ad n+n(n-1)/2 ed è (n2) e le operazione hanno costo ammortizzato O(n).

Euristica dell’unione pesata La complessità (n2) dell’esempio è dovuta al fatto che, in ogni Union, la seconda lista, quella che viene percorsa per aggiornare i puntatori al rappresentante, è la più lunga delle due. L’euristica dell’unione pesata sceglie sempre la lista più corta per aggiornare i puntatori al rappresentante.

Per poter fare ciò basta memorizzare la lunghezza della lista in un nuovo campo L del rappresentante. c h e b 4 # L Si può risparmiare memoria usando un campo booleano b per distinguere il rappresentante. 4 c h e b 1 L

Naturalmente occorre modificare le funzioni: MakeSet(x) x.b = true x.L = 1 x.succ = x FindSet(x) if x.b return x else return x.r

Union(x, y) x = FindSet(x) y = FindSet(y) // se la lista di x è più corta scambia x con y if x.L < y.L z = x, x = y, y = z x.L = x.L + y.L // cambia rappresentante alla lista di y y.b = false, y.r = x, z = y.succ while z ≠ y z.r = x, z = z.succ // concatena le due liste z = x.succ, x.succ = y.succ, y.succ = z

Dimostreremo che con l’euristica dell’unione pesata una sequenza di m operazioni delle quali n sono MakeSet richiede tempo O(m + n log n) La complessità ammortizzata delle operazioni è quindi: Se il numero n di MakeSet è molto minore di m per cui n log n = O(m)

Dimostrazione Tutte le operazioni richiedono un tempo costante eccetto Union che richiede un tempo costante più un tempo proporzionale al numero di puntatori al rappresentante che vengono modificati. Il tempo richiesto dalla sequenza di m operazioni è quindi O(m + N) dove N è il numero totale di aggiornamenti dei puntatori al rappresentante eseguiti durante tutta la sequenza di operazioni.

Il numero massimo di oggetti contenuti nella struttura è n: il numero di MakeSet. MakeSet(x) crea un insieme con un solo elemento. x.r viene aggiornato quando l’insieme viene unito ad un insieme di cardinalità maggiore o uguale per cui la cardinalità diventa almeno il doppio. Siccome un insieme non può avere più di n elementi x.r può essere aggiornato al più log2 n volte. Quindi N ≤ n log2 n.

Rappresentazione con foreste Una rappresentazione più efficiente si ottiene usando foreste di insiemi disgiunti. Ogni insieme è rappresentato da un albero i cui nodi, oltre al campo info che contiene l’informazione, hanno soltanto un campo p che punta al padre.

c f h e d b g

Implementazione semplice: MakeSet(x) x.p = x FindSet(x) while x.p ≠ x x = x.p return x Union(x, y) x = FindSet(x) y = FindSet(y) x.p = y // serve controllare se x ≠ y ?

y b e h c g d f b e h c g d f x x y

Osservazione. Sia nella rappresentazione con liste circolari che in quella con alberi non abbiamo indicato nessun puntatore esterno alla lista o all’albero. In realtà una struttura dati per insiemi disgiunti non è pensata per memorizzare dei dati ma soltanto per raggruppare in insiemi disgiunti dei dati che sono già memorizzati in qualche altra struttura: array, pila, lista, albero, tavola hash, ecc.

Complessità dell’implementazione semplice La complessità di FindSet(x) è pari alla lunghezza del cammino che congiunge il nodo x alla radice dell’albero. La complessità di Union è essenzialmente quella delle due chiamate FindSet(x) e FindSet(y). Un esempio analogo a quello usato con le liste mostra che una sequenza di n operazioni può richiedere tempo O(n2).

Possiamo migliorare notevolmente l’efficienza usando due euristiche: L’euristica dell’unione per rango: E’ simile a quella dell’unione pesata per le liste. In ogni nodo x manteniamo un campo rank che è un limite superiore all’altezza del sottoalbero di radice x ed è anche una approssimazione del logaritmo del numero di nodi del sottoalbero. L’operazione Union mette la radice con rango minore come figlia di quella di rango maggiore.

L’euristica della compressione dei cammini: Quando effettuiamo una FindSet(x) attraversiamo il cammino da x alla radice. Possiamo approfittarne per far puntare alla radice dell’albero i puntatori al padre di tutti i nodi incontrati lungo il cammino. Le successive operazioni FindSet sui nodi di tale cammino risulteranno molto meno onerose.

L’implementazione con entrambe le euristiche è la seguente: MakeSet(x) x.p = x x.rank = 0

FindSet(x) if x.p ≠ x x.p = FindSet(x.p) return x.p x x b e h c g d f

Union(x, y) x = FindSet(x) y = FindSet(y) Link(x, y) Link(x, y) if x.rank > y.rank y.p = x else x.p = y if x.rank == y.rank y.rank = y.rank + 1

Considerazioni generali sulla complessità: Usate separatamente entrambe le euristiche (unione per rango e compressione dei cammini) migliorano le prestazioni. Con la sola euristica dell’unione per rango una sequenza di m operazioni delle quali n sono MakeSet richiede tempo O(m log n)

Con la sola euristica della compressione dei cammini una sequenza di m operazioni delle quali n sono MakeSet e k sono FindSet richiede tempo Esempio: m ≥ 1000, n = 100 e k = 900 Esempio: m ≥ 1000, n = 512 e k = 100

Le migliori prestazioni in assoluto si ottengono usando entrambe le euristiche. Una sequenza di m operazioni delle quali n sono MakeSet richiede tempo O(m α(n)) dove α(n) è una funzione che cresce estremamente lentamente: α(n) ≤ 4 in ogni concepibile uso della struttura dati. La complessità ammortizzata di una singola operazione risulta quindi O(α(n)): praticamente costante.

La funzione α(n) Iterazione di una funzione: Funzione di Ackermann: k è detto livello della funzione.

Possiamo calcolare Ak(x) ricorsivamente nel modo seguente: Ackerman(k, j) if k = 0 return j+1 else a = j for i = 1 to j+1 a = Ackerman(k-1, a) return a

Se il livello k è fissato possiamo calcolare Ak(x) iterativamente con k cicli for annidati: Ak(j) a = j, nk-1 = a+1 for ik-1 = 1 to nk-1 // calcola Ak-1(a+1) (j) nk-2 = a+1 for ik-2 = 1 to nk-2 // calcola Ak-2(a+1) (j) ………… n1 = a+1 for i1 = 1 to n1 // calcola A1(a+1) (j) n0 = a+1 for i0 = 1 to n0 // calcola A0(a+1) (j) a = a+1 return a

Come cresce la funzione di Ackermann? Calcoliamo A1(j) sapendo che A0( j) = j+1 Quindi

Calcoliamo A2( j) Quindi

Calcoliamo A3(1) e A4(1)

La funzione inversa è Il numero stimato di atomi dell’universo è 1080 !!! In ogni applicazione pratica α(n) ≤ 4.