La presentazione è in caricamento. Aspetta per favore

La presentazione è in caricamento. Aspetta per favore

Strutture dati per insiemi disgiunti Servono a mantenere una collezione C = {S 1, S 2,..., S k } di insiemi disgiunti. Ogni insieme della collezione è

Presentazioni simili


Presentazione sul tema: "Strutture dati per insiemi disgiunti Servono a mantenere una collezione C = {S 1, S 2,..., S k } di insiemi disgiunti. Ogni insieme della collezione è"— Transcript della presentazione:

1 Strutture dati per insiemi disgiunti Servono a mantenere una collezione C = {S 1, S 2,..., S k } di insiemi disgiunti. Ogni insieme della collezione è individuato da un rappresentante che è uno degli elementi dellinsieme.

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

3 Esercizio 29 Sia data una rete con n nodi x 1, x 2, …, x n ed m canali di trasmissione c 1, c 2, …, c m e per ogni canale c i siano dati i due nodi x i ed y i 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.

4 Rappresentazione con liste Il modo più semplice per rappresentare una collezione di insiemi disgiunti è usare una lista circolare per ciascun insieme. cheb

5 I nodi hanno i seguenti campi: info : linformazione contenuta nel nodo r : il puntatore al rappresentante succ : il puntatore al nodo successivo Le operazioni sono: MakeSet(x) x.r = x x.succ = x FindSet(x) return x.r che x f x

6 cheb fgd x y cheb fgd x y Union(x,y)

7 // 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(n 2 ) dove n 2 è la lunghezza della seconda lista.

8 Consideriamo la sequenza di 2n-1 operazioni: MakeSet(x 1 )// costo 1 MakeSet(x 2 )// costo MakeSet(x n )// costo 1 Union(x 2, x 1 )// costo 1 Union(x 3, x 1 )// costo 2 Union(x 4, x 1 )// costo Union(x n, x 1 )// costo n-1 Il costo totale è proporzionale ad n+n(n-1)/2 ed è (n 2 ) e le operazione hanno costo ammortizzato O(n).

9 Euristica dellunione pesata La complessità (n 2 ) dellesempio è 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. Leuristica dellunione pesata sceglie sempre la lista più corta per aggiornare i puntatori al rappresentante.

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

11 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

12 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

13 Dimostreremo che con leuristica dellunione 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)

14 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.

15 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 linsieme 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ù log 2 n volte. Quindi N n log 2 n.

16 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 linformazione, hanno soltanto un campo p che punta al padre.

17 b eh c g d f

18 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 ? Implementazione semplice:

19 b eh c g d f b eh c g d f x y x y

20 Osservazione. Sia nella rappresentazione con liste circolari che in quella con alberi non abbiamo indicato nessun puntatore esterno alla lista o allalbero. 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.

21 Complessità dellimplementazione semplice La complessità di FindSet(x) è pari alla lunghezza del cammino che congiunge il nodo x alla radice dellalbero. 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(n 2 ).

22 Possiamo migliorare notevolmente lefficienza usando due euristiche: Leuristica dellunione per rango: E simile a quella dellunione pesata per le liste. In ogni nodo x manteniamo un campo rank che è un limite superiore allaltezza del sottoalbero di radice x ed è anche una approssimazione del logaritmo del numero di nodi del sottoalbero. Loperazione Union mette la radice con rango minore come figlia di quella di rango maggiore.

23 Leuristica della compressione dei cammini: Quando effettuiamo una FindSet(x) attraversiamo il cammino da x alla radice. Possiamo approfittarne per far puntare alla radice dellalbero 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.

24 Limplementazione con entrambe le euristiche è la seguente: MakeSet(x) x.p = x x.rank = 0

25 b eh c g d f x b e hc g d f x FindSet(x) if x.p x x.p = FindSet(x.p) return x.p

26 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 Union(x, y) x = FindSet(x) y = FindSet(y) Link(x, y)

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

28 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

29 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.

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

31 Possiamo calcolare A k (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

32 Se il livello k è fissato possiamo calcolare A k (x) iterativamente con k cicli for annidati: Ak(j)Ak(j) a = j, n k-1 = a+1 for i k-1 = 1 to n k-1 // calcola A k-1 (a+1) (j) n k-2 = a+1 for i k-2 = 1 to n k-2 // calcola A k-2 (a+1) (j) ………… n 1 = a+1 for i 1 = 1 to n 1 // calcola A 1 (a+1) (j) n 0 = a+1 for i 0 = 1 to n 0 // calcola A 0 (a+1) (j) a = a+1 return a

33 Come cresce la funzione di Ackermann? Calcoliamo A 1 (j) sapendo che A 0 ( j) = j+1 Quindi

34 Calcoliamo A 2 ( j) Quindi

35 Calcoliamo A 3 (1) e A 4 (1)

36 La funzione inversa è In ogni applicazione pratica α(n) 4. Il numero stimato di atomi delluniverso è !!!


Scaricare ppt "Strutture dati per insiemi disgiunti Servono a mantenere una collezione C = {S 1, S 2,..., S k } di insiemi disgiunti. Ogni insieme della collezione è"

Presentazioni simili


Annunci Google