310 likes | 558 Views
Fibonacci Heaps e il loro utilizzo nell’algoritmo di Prim. Paolo Larcheri 52SI. Cosa vedremo. Cos’ è uno heap Cenni sull’analisi ammortizzata Cosa sono gli Heap di Fibonacci Gli algoritmi di Minimum Spanning Tree L’ut i lizzo degli Heap di Fibonacci nell’algoritmo di Prim
E N D
Fibonacci Heaps e il loroutilizzo nell’algoritmo di Prim Paolo Larcheri 52SI
Cosa vedremo • Cos’è uno heap • Cenni sull’analisi ammortizzata • Cosa sono gli Heap di Fibonacci • Gli algoritmi di Minimum Spanning Tree • L’utilizzo degli Heap di Fibonacci nell’algoritmo di Prim • Complessità dell’algoritmo di Prim • Esempio di funzionamento dell’algoritmo di Prim
Gli heap • Lo heap, in generale, è una struttura dati • atta a contenere un insieme di dati ordinabili • A prescindere dall’implementazione gli heap • devono fornire le seguenti operazioni: • INSERISCI: inserimento di un nuovo elemento • TROVA-MIN: ricerca dell’ elemento con chiave minima • ESTRAI-MIN: estrazione dell’elemento con chiave minima • UNIONE: “fusione” di due heap • DECREMENTA-CHIAVE: dato un elemento dello heap e un • nuovo e minore valore per la chiave dello stesso, aggiorna • la chiave al nuovo valore • CANCELLA: cancellazione di uno specifico elemento
Gli Heap di Fibonacci (1) • Introdotti da Fredman e Tarjan nel 1987 • Possono essere considerati un’ottima implementazione di Heap!! • Sono stati progettati ispirandosi all’analisi ammortizzata al fine di ottenere ben precisi costi.
Cos’è l’analisi ammortizzata • L’analisi ammortizzata mira ad esprimere il tempo di esecuzione di un’intera sequenza di operazioni attribuendo un costo nominale ad ogni tipologia di operazione (inserimento, cancellazione, …). • Anche se una singola operazione può forare il suo costo nominale, l’importante e che il costo complessivo dell’intera sequenza resti entro la somma dei costi nominali per le singole operazioni che la compongono. • Ovviamente vogliamo dichiarare dei costi nominali il piu bassi possibili!
Gli Heap di Fibonacci (2) • Le complessità delle operazioni: t(H): numero di radici dello Heap di Fibonacci
Gli Heap di Fibonacci (3) • Gli Heap di Fibonacci sono costituiti da una lista di alberi caratterizzati ciascuno dall’ “ordinamento parziale dello heap”: la chiave di ogni nodo è minore o uguale alla chiave dei figli; in questo modo è garantito che il nodo con chiave minima è una delle radici • Ogni elemento dello Heap di Fibonacci punta al nodo-padre • Ogni nodo punta alla lista dei figli
Gli Heap di Fibonacci (4) • Ogni istanza di Heap di Fibonacci è costituita dai seguenti attributi: • puntatore alla lista delle radici • puntatore al nodo con chiave minima • numero complessivo di alberi contenuti nello Heap • numero totale di nodi presenti nello Heap
Gli Heap di Fibonacci (5) • Ogni elemento dello Heap di Fibonacci è collegato direttamente al nodo-padre • Inoltre i suoi figli sono collocati in una lista • Gli attributi di ogni nodo sono: • puntatore al nodo-padre • puntatore alla lista dei figli • 2 puntatori: uno al suo fratello destro e uno al suo fratello sinistro • il grado (intero): numero dei suoi figli • chiave: il valore del nodo
Gli Heap di Fibonacci (6) • Rappresentazione “logica ” di uno Heap di Fibonacci all’interno di un calcolatore:
Minimum Spanning Tree:nozioni fondamentali • Dato un grafo G(N, E) connesso, pesato e non orientato è sempre possibile trovare il suo MST, cioè quell’albero T(N, E2) (E2ÍE) per cui la somma di tutti i pesi degli archi appartenenti a E2 è minima • Chiaramente se il grafo G è un albero, il suo MST sarà G stesso • Se G possiede archi con peso uguale è possibile che esistano più MST per G
Minimum Spanning Tree:algoritmi • Gli algoritmi più noti sono • Algoritmo di Prim • Algoritmo di Kruskal
Algoritmo di Kruskal • L’algoritmo di Kruskal è di tipo “Greedy”. Consiste nell’ordinare tutti gli archi del grafo secondo il loro peso. Inizialmente T è composto da i soli nodi di G. Vanno aggiunti (seguendo l’ordine di peso) uno a uno tutti gli archi che non generano cicli in T. • L’operazione più costosa è ordinare gli archi; utilizzando un “buon” algoritmo di ordinamento, l’algoritmo di Kruskal costa O(mlog n)
Algoritmo di Prim Grafo G(N, E1) -> grafo Albero T(N, E2) -> MST NB: E2 ÍE1 Albero Prim(Grafo) { considera T formato da un nodo e da nessun arco; while(esistono nodi in T adiacenti a un nodo non in T) { seleziona l’arco di peso minimo che collega un nodo in T con un nodo non in T; aggiungi a T sia l’arco selezionato che il nuovo nodo; } return T; }
Algoritmo di Prim • Nell’algoritmo di Prim è indispensabile utilizzare una struttura dati che contenga ad ogni passo della computazione tutti gli archi candidati all’inserimento in T, ossia gli archi che connettono un nodo in T con uno non in T • Bisogna quindi ad ogni iterazione effettuare degli inserimenti e o delle sostituzioni
Algoritmo di Prim:le sostituzioni • Ad ogni iterazione, per ogni nodo v non ancora in T, basta tenere traccia del miglior arco e(v) che lo connetta ad un nodo già in T. • Di fatto, per ogni nodo v non in T, terremo traccia dell’estremo in T di e(v), (NULL se nessun arco incidente in v ha l’altro estremo in T). = arco con chiave minima = archi presenti nella SD a a q p p q T T b min q < p
Algoritmo di Prim:utilizzo degli Heap di Fibonacci • Gli Heap di Fibonacci si prestano particolarmente “bene” a svolgere questa funzione mediante l’operazione DECR-CHIAVE • Come visto prima questa operazione ha costo (ammortizzato) costante
Algoritmo di Prim:utilizzo degli Heap di Fibonacci Albero Prim(Grafo G) { considera T formato da un nodo scelto a caso; FH.inserisci(tutti gli archi del nodo scelto); while(anew = FH.estraiMin()) { nnew = nodo dell’arco appena estratto che non appartiene a T; T.inserisci(nnew, anew); for each(x congiunto a nnew da un arco y) { if(x non appartiene a T) if(FH contiene arco z che congiunge x a T) FH.decrChiave(z, y); else FH.inserisci(y); } } return T; } n-1 2m
Algoritmo di Prim:complessità ammortizzata • WHILE: a ogni iterazione viene effettuata una ESTRAI-MIN che come abbiamo visto ha costo ammortizzato pari a O(lg n) • FOR EACH: a ogni iterazione viene eseguita una DECR-CHIAVE o una INSERISCI aventi entrambe costo ammortizzato costante • TOTALE: il costo totale ammortizzato è quindi O(m + n(lg n))
Algoritmo di Prim:osservazioni finali (1) • Il costo dell’algoritmo di Prim con gli Heap di Fibonacci dipende anche dal numero di archi • Quindi: • Per grafi densi la complessità risulta essere O(n²) • Per grafi sparsi la complessità risulta essere O(n(lg n))
Algoritmo di Prim:osservazioni finali (2) • ATTENZIONE: per grafi densi risulta essere più efficiente l’utilizzo di strutture dati semplici (es: liste) in quanto la complessità della struttura degli Heap di Fibonacci comporta un’espansione della costante moltiplicativa non rilevabile a causa della notazione asintotica • Per capirci: liste → O(an²) Heap di Fibonacci → O(bn²) a « b
Algoritmo di Prim:esempio (1) 2 18 6 6 1 8 13 4 10 17 12 15 8 1 14 7 4 3 3 20 5
Algoritmo di Prim:esempio (2) 2 18 6 6 1 8 13 4 10 17 12 15 8 1 14 7 4 3 3 20 5
Algoritmo di Prim:esempio (3) 2 18 6 6 1 8 13 4 10 17 12 15 8 1 14 7 4 3 3 20 5
Algoritmo di Prim:esempio (4) 2 18 6 6 1 8 13 4 10 17 12 15 8 1 14 7 4 3 3 20 5
Algoritmo di Prim:esempio (5) 2 18 6 6 1 8 13 4 10 17 12 15 8 1 14 7 4 3 3 20 5
Algoritmo di Prim:esempio (6) 2 18 6 6 1 8 13 4 10 17 12 15 8 1 14 7 4 3 3 20 5
Algoritmo di Prim:esempio (7) 2 18 6 6 1 8 13 4 10 17 12 15 8 1 14 7 4 3 3 20 5
Algoritmo di Prim:esempio (8) 2 18 6 6 1 8 13 4 10 17 12 15 8 1 14 7 4 3 3 20 5
Algoritmo di Prim:esempio (8) 2 18 6 6 1 8 13 4 10 17 12 15 8 1 14 7 4 3 3 20 5