Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che...

42
Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione del numero m di operazioni MakeSet, FindSet e Link così ottenute. Union(x, y) x = FindSet(x) y = FindSet(y) Link(x, y)

Transcript of Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che...

Page 1: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Per valutare la complessità ammortizzata scomponiamo ogni Union:

nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione del numero m di operazioni MakeSet, FindSet e Link così ottenute.

Union(x, y) x = FindSet(x) y = FindSet(y) Link(x, y)

Page 2: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Se m' è il numero di operazioni MakeSet, FindSet e Union della sequenza originale allora m' m 3m'

Quindi possiamo calcolare la complessità asintotica in funzione di m invece che di m'.

Useremo il metodo del potenziale. Per definire la funzione potenziale ci servono alcune premesse.

Page 3: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

MakeSet(x) x.p = x x.rank = 0

FindSet(x) if x.p x x.p = FindSet(x.p) return x.p

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

Page 4: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Proprietà dei ranghi

Per ogni nodo x

a) x viene creato come radice con x.rank = 0

b) x.rank aumenta soltanto finché x resta radice, dopo di che rimane invariato.

c) Se x non è radice x.rank < x.p.rank

d) x.p.rank può solo aumentare.

e) dopo aver eseguito n operazioni x.rank < n

Page 5: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Le due funzioni level(x) e iter(x)

).(..: max)( rankxArankpxkxlevel k

).(..: max)( )()( rankxArankpxixiter i

xlevel

Valgono le seguenti diseguaglianze

)()(0 nxlevel

rankxxiter .)(1

Per ogni nodo x non radice definiamo due funzioni che “misurano” la differenza di rango tra x e x.p.

Page 6: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Dimostrazione:

0)(

).(1... 0

xlevel

rankxArankxrankpx

)()(

..)1().( )()(

nxlevel

rankpxnArankxA nn

1)(

).().(.. )1()()(

xiter

rankxArankxArankpx xlevelxlevel

rankxxiter

rankpxrankxArankxA xlevelrankx

xlevel

.)(

..).().( 1)()1.(

)(

Page 7: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Ad ogni nodo x attribuiamo un potenziale (o credito)

rankxnx .)()(0 (x) soddisfa le diseguaglianze

altrimenti )(.)()(

0. oppure radice è se .)()(

xiterrankxxleveln

rankxxrankxnx

Nel primo caso rankxnx .)()(0

Page 8: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

rankxnrankxn

xiterrankxxlevelnx

.)(1.)0)((

)(.))()(()(

Nel secondo caso

0)(.1

)(.))()(()(

xiterrankx

xiterrankxxlevelnx

Page 9: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

x

x)(

Definiamo un potenziale globale come somma dei potenziali di tutti i nodi

Valutiamo la differenza di potenziale dovuta alla i-esima operazione:

x x iiiii xx )()( 11

Page 10: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Se la i-esima operazione è una MakeSet(x) il potenziale del nodo x è 0 mentre il potenziale degli altri nodi rimane invariato. Quindi

0 i

Page 11: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Dimostriamo che se x non è radice il potenziale di x non può aumentare, ossia

0)( xi

Ed inoltre che se x.rank 1 e la i-esima operazione modifica o level(x) o iter(x) il potenziale diminuisce

1)( xi

Supponiamo quindi che la i-esima operazione sia una Link o FindSet.

Page 12: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Dimostrazione:Se x non è radice x.rank non cambia. Inoltre Link e FindSet non cambiano n e quindi anche (n) non cambia.

Se x.rank = 0 0)( xi

Supponiamo x.rank 1. 0)( xiSe level(x) e iter(x) non cambiano

Se level(x) non cambia ma cambia iter(x) allora iter(x) può solo crescere e quindi

1)( xi

Page 13: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Se level(x) cambia, iter(x) può anche diminuire ma, per i limiti visti, un aumento di level(x) diminuisce (x) di almeno x.rank mentre una diminuzione di iter(x) aumenta (x) al più di x.rank-1. Anche in questo caso 1)( xi

Page 14: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Calcoliamo il costo ammortizzato delle tre operazioni

MakeSet(x) x.p = x x.rank = 0

FindSet(x) if x.p x x.p = FindSet(x.p) return x.p

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

Page 15: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Costo ammortizzato di MakeSet(x)

0 i

)1(1ˆ Occ i

Page 16: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Costo ammortizzato di Link(x,y)

Il potenziale (x) dipende soltanto da x.rank e x.p.rank. Quindi gli unici elementi il cui potenziale può cambiare sono x, y e i figli di y.

I figli di y non sono radici e il loro potenziale non può aumentare.

Page 17: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

x è radice e diventa figlio di y ma il suo rango non cambia.

0)(.)(

.)()(

.))()(()(

xiterrankxxlevel

rankxnxiter

rankxxlevelnxi

Altrimenti

Se x.rank = 0

0.)(.)()( rankxnrankxnxi

Page 18: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

y rimane radice e y.rank o rimane invariato o aumenta di 1. Quindi

)(.)(.)()( 1 nrankynrankyny iii

)(ni

))(()(1ˆ nOncc i

Dunque

ed il costo ammortizzato di Link è

Page 19: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Costo ammortizzato di FindSet(x)Il costo reale è proporzionale al numero s di nodi del cammino percorso. Il potenziale della radice non cambia mentre i potenziali degli altri nodi possono solo diminuire.

Consideriamo dapprima come cambia il potenziale dei nodi x del cammino che hanno x.rank > 0 e che sono seguiti da almeno un nodo y diverso dalla radice e tale che level(y) = level(x).

Page 20: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

).()).((

)..().(..)1)(())(( rankxArankxAA

rankpxArankyArankpyxiter

kxiter

kk

kk

Sia k = level(y) = level(x). Allora

dopo la compressione

).(.... )1)(( rankxArankpyrankpx xiterk

Dunque iter(x) aumenta di almeno 1 e quindi

1)( xi

Page 21: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

I nodi rimanenti sono la radice, il primo nodo se ha rango 0 e per ogni livello k l’ultimo nodo del cammino avente level(x) = k.Siccome i livelli distinti sono al più (n) ci sono al più (n)+2 nodi il cui potenziale può rimanere invariato mentre il potenziale degli altri s-(n)-2 diminuisce di almeno 1. Quindi

2)( nsi

))((2)(ˆ nOnsscc i

ed il costo ammortizzato di FindSet è

Page 22: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Esercizio 31 . (Problema 22-3 del libro.)In un albero T il minimo antenato comune di due nodi u e v è il nodo w a profondità massima che è antenato sia di u che di v.

uv

w

Page 23: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Nel problema dei minimi comuni antenati off-line sono dati un albero T con n nodi ed una lista

L = (u1,v1), ... ,(um,vm)

di m coppie di nodi

Si chiede di calcolare la lista

W = w1, ... ,wm

dei minimi comuni antenati di ogni coppia.

Page 24: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

L’algoritmo di Tarjan risolve il problema in tempo lineare O(n+m) con una sola percorrenza dell’albero.

I nodi dell’albero hanno i seguenti campi:

- child : punta al primo figlio

- sibling : punta al fratello

- ancestor : punta ad un antenato- color : white all’inizio e poi diventa black.

oltre ai campi p e rank per insiemi disgiunti.

Page 25: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

MinComAnt(u) MakeSet(u) u.ancestor = u v = u.child while v ≠ nil // percorre la lista dei figli MinComAnt(v) Union(u,v) FindSet(u).ancestor = u v = v.sibling u.color = black for “ogni v tale che (u,v) ≡ (ui ,vi) L” if v.color == black wi = FindSet(v).ancestor

L’algoritmo di Tarjan:

Page 26: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

x0

x1

x2

......

r0

r1

rk-1

S0

S1

S2

Sk-1xk-1

r2

ri = rappresentanteMinComAnt(u) x0 = root, xk = u

xk

Page 27: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Esercizio 30 . (Problema 22-1 del libro)Sia dato l’insieme T = {1, 2, ... , n} ed una sequenza S = {o1 , o2 , ... , on+m } di n+m operazioni delle quali n sono Insert che inseriscono gli elementi di T una e una sola volta ed m sono ExtractMin che estraggono il minimo tra gli elementi inseriti e non ancora estratti. Supponiamo che ogni ExtractMin della sequenza sia preceduta da più Insert che ExtractMin.

Page 28: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Una possibile sequenza con n = 9 ed m = 6 è:

In cui le Insert sono indicate con i valori 1, 2,..., n inseriti mentre le ExtractMin sono indicate con Ej per j = 1,…, m.

S = { 4, 8, E1, 3, E2, 9, 2, 6, E3, E4, E5, 1, 7, E6, 5 }

Page 29: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Si chiede un algoritmo efficiente che data la sequenza S di operazioni calcoli la sequenza X = {x1, x2, ..., xm} degli m elementi estratti.

Il risultato dovrebbe essere

Ad esempio, con

X = {4, 3, 2, 6, 8, 1}

S = { 4, 8, E1, 3, E2, 9, 2, 6, E3, E4, E5, 1, 7, E6, 5 }

Page 30: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Soluzione.

Per ogni valore i = 1,…,n calcoliamo l’indice j dell’operazione ExtractMin che lo estrae e poniamo Xj = i.

Se i non viene estratto poniamo j = m+1 e non memorizziamo i in X.

Page 31: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

S ={4, 8, E1, 3, E2, 9, 2, 6, E3, E4, E5, 1, 7, E6, 5, C7}

Consideriamo la sequenza dell’esempio in cui abbiamo aggiunto alla fine un contenitore C7 a cui associare i valori non estratti.

L’elemento 1 viene estratto da E6.

Poniamo quindi X6 = 1 e a questo punto l’estrazione E6 è stata effettuata.

Page 32: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

L’elemento 2 viene estratto da E3. Quindi X3 = 2

S ={4, 8, E1, 3, E2, 9, 2, 6, E3, E4, E5, 1, 7, E6, 5, C7}X ={#, #, 2, #, #, 1}

S ={4, 8, E1, 3, E2, 9, 2, 6, E3, E4, E5, 1, 7, E6, 5, C7}

X ={#, #, #, #, #, 1}

La situazione è:

L’elemento 3 viene estratto da E2. Quindi X2 = 3

S ={4, 8, E1, 3, E2, 9, 2, 6, E3, E4, E5, 1, 7, E6, 5, C7}X ={#, 3, 2, #, #, 1}L’elemento 4 viene estratto da E1. Quindi X1 = 4

Page 33: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

L’elemento 5 non viene estratto e rimane associato a C7. Noi infatti manteniamo associato ogni valore alla ExtractMin successiva non ancora effettuata o a C7 se non ce ne sono altre.

L’elemento 6 è associato ad E4 e quindi X4 = 6.

S ={4, 8, E1, 3, E2, 9, 2, 6, E3, E4, E5, 1, 7, E6, 5, C7}X ={4, 3, 2, #, #, 1}

La situazione è

Page 34: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

L’elemento 7 è associato a C7 e viene scartato.

L’elemento 9 è associato a C7 e viene scartato.

Il risultato è quindi X = {4, 3, 2, 6, 8, 1}

S ={4, 8, E1, 3, E2, 9, 2, 6, E3, E4, E5, 1, 7, E6, 5, C7}X ={4, 3, 2, 6, #, 1}

L’elemento 8 è associato ad E5 e quindi X5 = 8.

S ={4, 8, E1, 3, E2, 9, 2, 6, E3, E4, E5, 1, 7, E6, 5, C7}X ={4, 3, 2, 6, 8, 1}

La situazione è

Page 35: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Implementazione.Usiamo una struttura dati per insiemi disgiunti.

Manteniamo raggruppati in un insieme Ij gli elementi associati ad ogni Ej non ancora effettuata e in un insieme Im+1 quelli associati a Cm+1.

Page 36: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Su questi insiemi vogliamo eseguire in tempo costante le operazioni MakeSet(i), Union(i,h), FindSet(i) ed inoltre FindIndex(i) che calcola l’indice j della Ej non effettuata (o l’indice m+1 di Cm+1) a cui l’elemento i risulta associato.

Le prime tre sono quelle standard e per poter eseguire la FindIndex(i) basta memorizzare nel rappresentante di ogni insieme l’indice j della Ej non effettuata (o l’indice m+1 di Cm+1) a cui l’insieme è associato.

Page 37: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Rimane un problema: qualcuno degli insiemi può essere vuoto mentre la struttura dati per insiemi disgiunti richiede che essi siano tutti non vuoti.

All’inizio creiamo quindi m+1 insiemi I1 ,…,Im+1 dove Ij = {n+j} contiene soltanto l’elemento fittizio e memorizziamo l’indice j nell’unico nodo.Tempo O(m).

Per ovviare a questo aggiungiamo ad ogni insieme Ij un elemento fittizio di valore i = n+j.

Page 38: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Percorriamo quindi la sequenza delle operazioni e, finché non troviamo la prima ExtractMin, aggiungiamo gli elementi inseriti all’insieme I1.

Dopo di che gli elementi inseriti prima della seconda ExtractMin vengono aggiunti a I2 e così via fino ad aggiungere ad Im+1 tutti gli elementi inseriti dopo l’ultima ExtractMin.

Tempo O(n+m).

Page 39: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Per aggiungere un nuovo elemento i ad un insieme Ij dobbiamo usare la MakeSet(i) e poi usare la Union(i, m+j) per riunire i due insiemi (ricordiamo che m+j è l’elemento fittizio di Ij ).

Naturalmente se l’unione cambia il rappresentante occorre ricordarsi di memorizzare l’indice j dell’insieme nel nuovo rappresentante.

Page 40: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Quando il nostro algoritmo decide che l’elemento i viene estratto da Ej dobbiamo porre

X[j] = i

A questo punto, siccome Ej è stata eseguita, gli elementi dell’insieme Ij a cui appartiene i devono essere associati alla successiva ExtractMin Ek non ancora eseguita o a Cm+1 se tutte le ExtractMin successive sono state eseguite.

Page 41: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Dobbiamo però prima trovare h. Per fare questo manteniamo i rappresentanti degli insiemi Ij in una lista doppiamente concatenata e ordinata rispetto all’indice j.

Occorre quindi effettuare una Union(i,h) dove h è un elemento di Ik associato ad Ek o di Im+1 associato a Cm+1.

Page 42: Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.

Supponiamo di aver elaborato gli elementi 1,…,i-1.

Per trovare l’indice j della ExtractMin non saturata che estrae i basta calcolare j = FindIndex(i).

Se j = m+1 l’elemento i è stato inserito dopo l’ultima ExtractMin non saturata e quindi non viene estratto.Altrimenti esso è estratto da Ej e possiamo porre

X[j] = i.

Tutto questo richiede un tempo costante per ciascun elemento e quindi tempo O(n) in totale.