Analisi comparativa di strutture dati per la

UNIVERSITÀ DEGLI STUDI ROMA 3
FACOLTÀ DI SCIENZE MATEMATICHE, FISICHE E NATURALI
CORSO DI LAUREA MAGISTRALE IN MATEMATICA
Tesi di Laurea Magistrale in Matematica
Analisi comparativa di strutture dati per la
rappresentazione di alberi
Candidato
Relatore
Roberta Tirocchi
Prof. Roberto Di Pietro
Correlatore
Prof. Alessandro Colantonio
Anno Accademico 2011/2012
2 CONCETTI BASE SUGLI ALBERI
1
Introduzione
La struttura ad albero, in inglese Tree, è una struttura dati non lineare che
segue un modello gerarchico. La sua caratteristica principale è che ciascun elemento
ha molti successori ed un unico predecessore. L’albero è molto usato nell’ambito
informatico perchè consente di modellare aspetti della vita reale, come ad esempio
l’organigramma di un’azienda, la tassonomia degli organismi animali e vegetali, le
gerarchie militari e la genealogia di una persona.
Nella letteratura esistono diverse strutture dati per rappresentare l’albero nel
calcolatore. Le principali distinzioni tra queste strutture si possono individuare
attraverso la valutazione delle prestazioni, in termini di memoria occupata, e del
tempo impiegato nell’esecuzione delle operazioni. Anche se la letteratura è molto
ricca per quanto riguarda la descrizione di queste strutture dati, tuttavia è molto
difficile trovare delle analisi comparative che mostrino l’efficienza di ogni forma
rispetto alle altre.
La tesi si occuperà di fare un’analisi comparativa delle metodologie note in
letteratura usate per rappresentare l’albero attraverso gli array e le matrici. Saranno
inoltre descritti due nuovi approcci, uno basato sull’uso di una matrice binaria, l’altro
composto da un insieme di array di dimensione variabile. In entrambi i casi l’idea
principale è quella di memorizzare tutti gli antenati di ciascun elemento dell’albero.
Nella tesi esamineremo pro e contro dei nuovi approcci rispetto alle metodologie
classiche. Inoltre analizzeremo il legame tra la teoria spettrale della forma matriciale,
già nota in letteratura, e la teoria dei grafi ed esamineremo anche le caratteristiche
spettrali della nuova matrice binaria.
Nel corso del lavoro saranno comparate tra loro tutte le implementazioni delle
rappresentazioni descritte, la complessità computazionale delle principali operazioni
sugli alberi, il tempo di esecuzione di ogni operazione ed il numero di celle usate per
allocare nel calcolatore l’intero albero. Quest’analisi comparativa sarà effettuata sia
in maniera formale che mediante implementazione nel linguaggio di programmazione
Java di tutti gli algoritmi, eseguendo test numerici su alberi con un numero variabile
di nodi. Da quest’analisi saranno evidenziati gli aspetti positivi di ogni forma
di rappresentazione e saranno messi in luce i miglioramenti che la nuova forma
apporterà nella gestione degli alberi, come la riduzione del tempo di esecuzione di
alcune operazioni.
2
Concetti base sugli alberi
In questo capitolo definiamo in modo rigoroso la struttura dati ad albero.
2
2 CONCETTI BASE SUGLI ALBERI
2.1
Definizioni
Nella teoria dei grafi, per grafo G intendiamo una coppia G = (V, E) dove V è
un insieme non vuoto, discreto e finito, i cui elementi sono i vertici del grafo ed E è
l’insieme degli spigoli del grafo ed è un sottoinsieme del prodotto cartesiano V ×V
[1].
Definizione 1. Un albero puó essere definito ricorsivamente come segue:
• G = ({v}, ∅) è un albero;
• se G1 e G2 sono alberi, allora G = (V, E) definito ponendo V = V (G1 ) ∪ V (G2 ),
E = E(G1 ) ∪ E(G2 ) ∪ {(v1 , v2 )}, con v1 ∈ V (G1 ) e v2 ∈ V (G2 ) Da questa definizione ricorsiva ricaviamo che
Definizione 2. Un albero è un grafo T = (V, E) connesso ed aciclico.
Ciò vuol dire che presi due vertici distinti sono sempre connessi da uno ed un
solo cammino e che non devono esistere cicli. La connessione e l’aciclicità di un
albero T sono due proprietà legate alla cardinalità dell’insieme E(T ). Infatti se si
aggiunge uno spigolo a T si perde l’aciclità, invece se si elimina uno spigolo si perde
la connessione. Osserviamo inoltre preso un albero T abbiamo che se |V (T )|= n;
allora |E(T )| =n−1. Questa relazione caratterizza in modo forte un albero; infatti
dato un grafo G = (V, E) con |E| = |V | − 1 e connesso allora è un albero. Tutte
queste proprietà sono riassunte nel seguente teorema di caratterizzazione degli alberi.
Teorema 1. Sia G = (V, E), un grafo non orientato.Le seguenti affermazioni sono
equivalenti :
1. G è un albero;
2. due vertici qualsiasi di G sono connessi da unico cammino semplice;
3. G è connesso, ma se qualunque spigolo di G venisse rimosso, allora G diventerebbe non connesso;
4. G è connesso e |E| = |V | − 1;
5. G è aciclico e |E| = |V | − 1;
6. G è aciclico, ma se venisse aggiunto uno spigolo qualsiasi il grafo risultante
non sarebbe più aciclico.
3
3 METODI DI RAPPRESENTAZIONE CLASSICI
Dato un albero T = (V, E) è possibile selezionare un vertice r ∈ V (T ) qualsiasi e
a partire da esso orientare tutti gli spigoli in modo naturale scegliendo i cammini
che da r portano a tutti i vertici dell’albero. Otteniamo in questo modo un albero
orientato e radicato. Il vertice r è detto radice dell’albero, unico vertice privo di
spigoli entranti ma può possedere un numero arbitrario di spigoli uscenti. Ogni nodo
dell’albero, diverso dalla radice, possiede diversi spigoli uscenti ma un solo spigolo
entrante. I vertici con solo spigoli entranti sono detti foglie. In un albero orientato
T = (V, E) se (u, v) ∈ E(T ), il vertice u si dice padre di v ed al tempo stesso v si
dice figlio di u. Se due nodi hanno lo stesso padre, sono fratelli. Le foglie sono quindi
vertici privi di figli, invece la radice è l’unico vertice che non ha padre. Definiamo
ora un albero ordinato, un albero radicato i cui figli di ciascun nodo sono ordinati,
cioè se un nodo ha k-figli abbiamo un primo, un secondo,. . . , un k-simo figlio. Se
abbiamo un albero dove ogni nodo possiede solo due figli, definiamo questi con il
nome di figlio destro e sinistro. Un nodo non foglia è interno. Il grado di un nodo
x ∈ V (T ), in un albero T radicato, è il numero dei suoi figli. L’ordine è il grado
massimo di quasi tutti i nodi dell’albero. Preso un nodo x di un albero radicato T
con radice r; un qualunque nodo y compreso nell’unico cammino da r a x è chiamato
antenato di x mentre x è detto discendente di y. La profondità di un nodo x, è la
lunghezza del cammino dalla radice al nodo in questione. La profondità massima di
un qualsiasi nodo dell’albero determina l’altezza dell’albero. Infine diciamo che un
albero è bilanciato se la profondità di ogni foglia coincide con l’altezza dell’albero
oppure a questa stessa altezza meno uno. Per concludere questo discorso introduttivo
sulle proprietà della struttura dati ad albero enunciamo il seguente teorema con il
suo corollario.
Teorema 2. Un albero completo di ordine d ed altezza h possiede
dh+1 −1
d−1
nodi. Corollario 1. L’altezza di un albero completo di ordine d e avente n nodi è H =
logd (nd − n + 1) − 1. 3
Metodi di rappresentazione classici
In questo capitolo viene offerta una descrizione ad alto livello delle principali
strutture dati utilizzate per rappresentare alberi. Ci sono vari metodi per rappresentare la struttura ad albero. Nella letteratura, tale struttura viene implementata
in due modi: con gli array e con le liste concatenate, ovvero oggetti (i nodi) che
puntano ad altri oggetti.
Per semplicità di esposizione ci soffermeremo a descrivere tutti i metodi noti che
ricorrono all’uso di uno ma anche più array e non quelli che ricorrono ai puntatori. Non
4
3 METODI DI RAPPRESENTAZIONE CLASSICI
descriveremo le liste concatenate perchè nella maggior parte dei casi la complessità
dell’esecuzione delle operazioni è la stessa, gli array sono più compatti e possiamo
avere accesso diretto ai dati mediante indici. Inoltre alcune tipologie di alberi come
ad esempio gli heap ricorrono proprio per definizione agli array.
Gli alberi che andremo a rappresentare saranno alberi k-ari, ovvero alberi dove
ogni nodo possiede al più k figli.
Preso un albero T, memorizziamo il contenuto dell’etichetta di ciascun nodo
all’interno di un array L ed i nodi vengono rinominati 0 ,1 ,..., n-1. In questo
modo modelliamo i nodi dell’albero con dei numeri naturali così da rendere più
semplice l’esecuzione delle operazioni sull’albero per qualsiasi tipo di dato contenuto
nell’etichette dei nodi.
3.1
Parent Representation (PR)
La Parent Representation(PR) è la forma di rappresentazione più semplice di un
albero. Il seguente metodo sfrutta la caratteristica della struttura dati ad albero che
evidenzia che ogni nodo ha un solo padre. La Parent Representation(PR) dell’albero
T è un array lineare A nel quale nella cella A[i] viene memorizzato il padre del nodo
i, ovvero A[i] = j se il nodo j è il padre del nodo i e A[i] = −1 se il nodo i è la root.
Con questa rappresentazione sarà semplice l’operazione di ricerca del padre di un
nodo ma risulterà più costosa eseguire l’operazione di calcolo dei figli e molte altre
operazioni. Inoltre in questo caso non viene specificato l’ordine dei figli di un nodo.
Nell’inserimento dei nodi è possibile imporre un ordine artificiale di inserimento dei
nodi nell’array. I nodi dell’albero possono essere letti, livello dopo livello, partendo da
sinistra a destra a partire dalla radice. Assegniamo alla root la prima cella dell’array
L (quella di posto 0), quindi avremo che A[0] = −1. È giusto notare che nel seguente
modo possiamo rappresentare qualsiasi tipo di albero non sono quello binario.
3.2
Adjacency Matrix Representation(AMR)
In teoria dei grafi, un albero è un tipo particolare di grafo, ovvero è un grafo
connesso ed aciclico. Un grafo può essere sempre rappresentato utilizzando una
matrice particolare detta matrice di adiacenza .
Definizione 3. La matrice di adiacenza è una matrice A di dimensione |V | × |V |
definita in questo modo :
aij =



1 se (i , j) ∈ E(G)
0 altrimenti .
5
3 METODI DI RAPPRESENTAZIONE CLASSICI
Se m è il numero di spigoli del grafo, avremmo 2m celle con il valore 1. La
trasposta di una matrice A = (aij ) è definita come la matrice AT = (aTij ), dove aTij =
aji . Poichè in un grafo non orientato (u, v) e (v, u) rappresentano lo stesso arco, la
matrice di adiacenza di un grafo non orientato è identica alla trasposta : A = AT .
In alcune applicazioni è conveniente memorizzare solo i dati che compaiono sopra
la diagonale principale (diagonale inclusa) della matrice di adiacenza, riducendo
quindi la memoria necessaria per memorizzare il grafo quasi della metà. L’albero è
un grafo quindi potremmo usare la matrice di adiacenza definita in questo modo per
rappresentarlo :



 1 se i è padre di j
aij = 1 se i è figlio di j



0 altrimenti
Quindi avremmo che ai,j = aj,i perchè se i è padre di j allora sicuramente j è figlio di
i. Come nel caso del grafo non orientato A = AT quindi per convenienza possiamo
memorizzare solo le celle al di sopra della diagonale e quindi definiamo la matrice di
adiacenza dell’albero nel seguente modo :


1 se i è padre di j
aij = 
0 altrimenti
In questa rappresentazione abbiamo bisogno di memorizzare dove si trova la root
altrimenti è difficile distinguerla dagli altri nodi. Possiamo rappresentare ogni albero
k-ario con questo metodo.
3.3
List of Children Representation(LCR)
Una tecnica per la rappresentazione degli alberi molto usata in informatica prevede
l’utilizzo di una famiglia di n liste L1 , ...., Ln di vertici denominate liste di adiacenza
del grafo G. La rappresentazione mediante liste di adiacenza avviene nel seguente
modo: ∀ vi ∈ V (G) memorizziamo nella lista Li tutti i vertici ad esso adiacenti.
Due vertici u e v si dicono adiacenti se esiste uno spigolo che li collega. Come nel
paragrafo precedente sapendo che l’albero T = (V, E) è un grafo possiamo sfruttare
questo metodo per rappresentarlo. Sia T un albero, le liste sono array A di lunghezza
n. Come per le altre forme questa può essere usata per ogni tipo di albero non solo
quello binario, nel caso di alberi k-ari avremo k array. In ciascuna cella A[i] sono
contenuti i figli del nodo i. Alla root assegniamo sempre la prima posizione e per
inserire gli altri nodi usiamo sempre un ordine convenzionale partendo dall’alto e
proseguendo verso il basso. Nel caso dell’albero binario avremo solo due array L[i]
e R[i] , nelle quali entrate memorizziamo rispettivamente il figlio sinistro e il figlio
destro del nodo i. Per questo motivo, nel caso nell’albero binario, oltre al metodo
6
3 METODI DI RAPPRESENTAZIONE CLASSICI
d’inserimento con gli input standard (nodo e padre del nodo) implementeremo anche
un altro metodo per inserire i nodi , per input avremo un ulteriore informazione
ovvero se il nodo in questione è un figlio sinistro o destro del padre. Chiamiamo
questa forma di rappresentazione List of Children Representation(LCR) .
3.4
Binary Tree Representation(BTR)
Binary Tree Representation(BTR) è una rappresentazione posizionale perchè la
posizione del nodo nell’array corrisponde alla sua posizione nell’albero. Assegniamo
la cella 0 alla root, il nodo nella cella 1 è il figlio sinistro della radice e quello nella
cella 2 è il figlio destro, e così via, proseguendo da sinistra a destra lungo ogni livello
dell’albero. Aggiungere un nodo in una posizione nell’albero vuol dire collocare
il nodo nella corrispettiva cella dell’array, se un nodo nell’albero non esiste nella
sua cella viene memorizzato il valore −1. Sia T un albero binario, i figli ed il
padre di un nodo possono essere trovati applicando semplici operazioni aritmetiche
agli indici di locazione del nodo nell’ array. Sia index l’indice del nodo, il figlio
sinistro si troverà nella cella 2index + 1 mentre il figlio destro invece nella cella
2index+2 ed infine il padre si trova nella posizione index−1
. Quando cancelliamo
2
un nodo dall’albero avremo uno spreco di memoria perchè nell’array si creeranno
dei buchi ovvero rimarranno vuote quelle celle precedentemente occupate dai nodi
eliminati. Questa rappresentazione non è solo usata per gli alberi binari ma potrà
essere sfruttata per rappresentare qualsiasi tipo di albero k-ario.
3.5
Left Child - Right Sibling Representation (LCRSR)
Dato un albero k-ario esiste un algoritmo per trasformarlo in albero binario,
questo procedimento non è reversibile senza l’ aggiunta di ulteriori informazioni. Se
non eseguiamo l’ultimo passo dell’algoritmo abbiamo la rappresentazione di qualsiasi
albero k-ario. Sia T un albero, prendiamo due array Left e Right di lunghezza pari
al numero n di nodi dell’albero. Memorizziamo all’interno della cella Left [i] il figlio
sinistro del nodo i, mentre nella cella Right[i] memorizziamo il fratello destro del
nodo i. Specifichiamo che con il termine figlio sinistro del nodo i, intendiamo il
primo nodo che abbiamo con padre il nodo i. L’ultimo passo dell’algoritmo consiste
nel prendere un nuovo albero T’ con gli stessi array Left e Right però cambierà il
significato del contenuto di questi array. Infatti Left[i] e Right[i] rappresentano il figlio
sinistro e destro del nodo i. Nel nostro caso non siamo interessati alla trasformazione
ma solo alla rappresentazione dell’albero quindi ci fermeremo al passo precedente,
ove in Left[i] abbiamo il figlio più sinistro del nodo i mentre in Right[i] il fratello
destro di i. Se abbiamo un albero non completo segnaliamo la mancanza di un nodo
7
4 NUOVO METODO DI RAPPRESENTAZIONE BASATA SU MATRICI
BINARIE
memorizzando -1 nell’entrata corrispettiva. Alla radice come in LCR assegniamo
sempre la prima cella dei due array quindi in Left[0] è contenuto il figlio più sinistro
della root e Right[0] = -1. Questa forma di rappresentazione si chiama Left Child Right Sibling Representation (LCRSR).
4
Nuovo metodo di rappresentazione basata su
matrici binarie
Presentiamo ora la nuova forma di rappresentazione, nella sua forma estesa e nella
sua forma contratta. Descriviamo queste due rappresentazioni della struttura dati
ad albero, la prima che ricorre all’uso di una matrice binaria, la seconda è sempre
una matrice ma costituita di array di dimensioni variabili.
4.1
Rappresentazione nella forma estesa
Dato un albero T con n nodi, i quali vengono memorizzati in un array L e li
denominiamo 0,1,. . . ,n-1 come per le classi note in letteratura. Rappresentiamo
l’albero attraverso una matrice binaria A di dimensione n × n. La matrice A viene
riempita seguendo il criterio che segue: A[i][j] = 1 se j è un antenato del nodo i
ed A[i][i] = 1, cioè ogni nodo è antenato di se stesso. Questa forma matriciale di
rappresentazione dell’albero ricalca un legame specifico tra i nodi, essere antenati,
per questo rende efficiente l’esecuzione di alcune operazioni. Come vedremo in avanti
questo metodo porta dei benefici in diverse operazioni, per il fatto che siamo in grado
di trovare in tempi costanti tutti gli antenati di ogni nodo dell’albero senza dover
iterare il metodo di ricerca del padre. Lo svantaggio è chiaramente rappresentato da
una maggiore occupazione di memoria rispetto alla memorizzazione del solo antenato
diretto (cioè il padre), ma come vedremo in seguito è possibile ridurre moltissimo
tale overhead di memoria con tecniche di compressione dei dati. Per ogni nodo
dell’albero la i-esima riga contiene gli antenati del nodo i. Tale metodo segue, come
nel caso di PR, un criterio d’inserimento dei nodi nell’albero. La matrice binaria
che otteniamo da tale rappresentazione è una matrice sparsa. Questa matrice si
distingue dalla matrice di adiacenza descritta nel paragrafo precedente, perchè in
questa sono riportati tutti gli antenati del nodo ed il nodo stesso, mentre nella
matrice di adiacenza riportiamo solamente il padre diretto di ogni nodo.
4.2
Rappresentazione nella forma compressa
Per non avere una matrice sparsa, possiamo definire una versione compressa
del metodo. C è un array n - dimensionale. Ogni cella è un array di dimensione
8
4 NUOVO METODO DI RAPPRESENTAZIONE BASATA SU MATRICI
BINARIE
variabile, all’interno di ogni cella C[i] memorizziamo il nodo i ed il valore di tutti i
suoi antenati. Utilizziamo, data la dimensione variabile di ogni cella dell’array C,
un altro array N. Ogni cella N [i] contiene il numero di elementi dell’array C[i], tale
valore è pari al numero di antenati del nodo i. In questo caso risulterà semplicissimo
ricercare gli antenati di un nodo dato.
Definiamo quanto detto in maniera rigorosa.
C[i]= {j : A[i][j] = 1} ed con N [i]=|{j : A[i][j] = 1}|.
Questa matrice vista per colonne ricorda le liste di adiacenza, ma a differenza di
questa vengono memorizzate in ogni riga gli antenati di ogni nodo ed il nodo stesso,
mentre all’interno delle liste di adiacenza in ogni colonna troviamo i figli di ogni
nodo.
4.3
Legame tra la nuova matrice binaria e la matrice di
adiacenza
Sia T un albero radicato, rappresentato nella memoria del calcolatore mediante
la matrice di adiacenza A definita nel seguente modo:


1 se i è padre di j
aij = 
0 altrimenti
Conosciuta h, l’altezza dell’albero, dalla matrice di adiacenza A possiamo ottenere la
matrice binaria della nuova forma di rappresentazione mediante i seguenti passi:
• Calcoliamo la trasposta della matrice di adiacenza AT = A0 .
• Sommiamo ad essa la matrice identità In quindi (A0 + In )
• Calcoliamo (A0 + In )h ed otteniamo la nuova forma di rappresentazione dell’albero mediante matrice binaria.
Osservazione 1. Il prodotto e la somma tra le matrici segue in questo caso le regole
dell’algebra booleana. Sia A ∈ Mm,n (K) e B ∈ Mn,p (K) il loro prodotto righe
per colonne è la matrice AB ∈ Mmp (K) il cui elemento di posto (i, k) è il prodotto
della i-esima riga di A per k-esima colonna di B.
In formule si ha
AB = (A(i) B (k) ) = ai1 b1k + · · · + ain bnk .
Il prodotto è il prodotto logico (AND) che restituisce 1 se tutti gli operandi valgono
1, mentre restituisce 0 in tutti gli altri casi.
Invece la somma è la somma logica (OR) che restituisce 1 se almeno uno degli
elementi è 1 altrimenti vale 0.
9
5 IMPLEMENTAZIONE DELLE PRINCIPALI OPERAZIONI TIPICHE DEGLI
ALBERI
4.4
Pro e Contro dell’uso della nuova di rappresentazione
degli alberi
Nell’analizzare questa forma di rappresentazione rispetto alle altre note in
letteratura evidenzieremo vantaggi ma anche svantaggi.
Rispetto agli altri metodi sarà semplice e veloce ricercare gli antenati di un
nodo rispetto alle lunghe iterazioni ai cui ricorrono le altre metodologie. Questa
riduzione del tempo di calcolo degli antenati influenzerà in positivo i tempi di
ricerca del cammino minimo di un nodo, di calcolo della profondità di un nodo e
calcolo dell’altezza dell’albero. Questa sua particolarità ci permetterà di usare questa
rappresentazione ad esempio nella gestione dell’organigramma di un’azienda basata
sui ruoli. Grazie al modo in cui vengono memorizzati i nodi rispetto alle altre forme
di rappresentazione saremo in grado di risalire in breve tempo al ruolo root di ogni
utente attraverso una semplice lettura.
Avremo però degli svantaggi per quanto riguarda la memoria occupata, dato che
essendo entrambe delle matrici occupano sicuramente più celle degli array ma anche
della stessa matrice di adiacenza (dato che come abbiamo detto memorizziamo tutti
gli antenati ed il nodo stesso, non solo il padre). Si possono sfruttare tecniche di
compressione degli insieme come ad esempio Concise: Compressed ’n’ Composable
Integer Set [11].Concise è un algoritmo di compressione di matrici di bit. La compressione avviene senza compromettere le prestazioni delle operazioni bit per bit.
Concise è in grado di rappresentare 32 interi in una cella di memoria solamente se gli
interi sono consecutivi (i, i + 1, i + 2, . . . , i + 31). [11] . Nel caso degli alberi quindi se
gli antenati fossero tutti interi consecutivi sarebbe possibile applicare la compressione
dei dati con Concise e quindi l’occupazione di memoria della matrice binaria nella
forma estesa diviene pari all’occupazione di memoria della matrice di adiacenza.
I vantaggi e gli svantaggi di questa nuova metodologia saranno esaminate nei
dettagli nei capitoli successi della tesi, attraverso l’analisi degli algoritmi, lo studio
della complessità computazionale e dell’occupazione di memoria
5
Implementazione delle principali operazioni tipiche degli alberi
Per ogni struttura dati proposta nella tesi verranno analizzate in dettaglio le
seguenti operazioni sugli alberi ed implementate in java:
• aumento dell’arietà, cioè aumento del numero massimo di figli per ogni nodo.
10
5 IMPLEMENTAZIONE DELLE PRINCIPALI OPERAZIONI TIPICHE DEGLI
ALBERI
• inserimento di un nodo in una data posizione, cioè inserimento all’interno della
struttura dati del nodo in una determinata posizione.
• ricerca del padre di un dato nodo, cioè ricerca del padre diretto di un nodo.
• ricerca dei figli diretti di un dato nodo, cioè ricerca dei figli diretti di un nodo.
• ricerca dei fratelli di un dato nodo, cioè ricerca di tutti i nodi che hanno lo
stesso padre del nodo dato.
• rimozione di un nodo in una data posizione, cioè eliminazione di un nodo dalla
sua posizione all’interno della struttura dati.
• spostamento di un nodo da una posizione ad un’altra, cioè cambiamento della
posizione del nodo all’interno della struttura dati.
• ricerca del cammino più breve tra due nodi dati e calcolo della sua lunghezza,
cioè ricerca del cammino di lunghezza minima che connette due nodi dell’albero.
• calcolo antenati di un dato nodo.
• calcolo discendenti di un dato nodo.
• calcolo profondità di dato nodo.
• calcolo altezza dell’albero.
• calcolo radice dell’albero.
• verificare se un dato nodo è una foglia.
• verificare se dati due nodi, uno è figlio diretto dell’altro.
• verificare se dati due nodi, uno è padre dell’altro.
• verificare se dati due nodi, uno è discendente dell’altro.
• verificare se dato un nodo, è padre diretto di almeno un nodo contenuto in un
insieme generico di nodi.
• verificare se dato un nodo, è padre diretto di tutti i nodi contenuti in un insieme
generico di nodi.
11
6
ANALISI MEMORIA E COMPLESSITÀ COMPUTAZIONALE
6
Analisi memoria e complessità computazionale
6.1
Vantaggi e Svantaggi in termini di complessità computazionale della nuova forma di rappresentazione:
Attraverso la nozione O grande, con cui si definisce l’insieme delle funzioni la
cui crescita asintotica non è maggiore, a meno di fattori moltiplicativi costanti, a
quella di una determinata funzione f (n). Descriviamo ora una scala per magnitudine
crescente, con c indichiamo una costante arbitraria .
Notazione
0(1)
0((log)
O(n)
O(n2 )
O(nc )
O(n!)
Nome
costante
logaritmica
lineare
quadratica
polinomiale
fattoriale
Tabella 1: Ordini di funzioni comuni
Riassumiamo nelle seguenti la complessità computazionali calcolate in precedendenza.
Memory Father
PR
n
O(1)
n
BTR
O(k )
O(1)
(n−1)n
AMR
O(n)
2
LCR
kn
O(kn)
LCRRSR kn
O(nk)
(n+1)n
NEW
O(n)
2
(n−1)n
NEWC
O(1)
2
Children
O(n)
O(1)
O(n)
O(1)
O(1)
O(n2 )
O(n)
Siblings
O(n)
O(1)
O(n2 )
O(kn)
O(1)
O(n2 )
O(n2 )
Tabella 2: Tabella riassuntiva 1
12
7 RISULTATI SPERIMENTALI
Insert
PR
O(1)
BTR
O(1)
AMR
O(1)
LCR
O(k)
LCRRSR O(k)
NEW
O(n)
NEWC
O(n)
Remove
O(n2 )
O(n)
O (n2 )
O(n2 )
O(n2 )
O (n2 )
O(n2 )
Shortest Path
O( h)
O(n)
O(n2 )
O(kn2 )
O(kn2 )
O(h)
O(h)
Move
O(1)
O(n)
O(1)
O(kn)
O(kn)
O(n3 )
O(n2 )
Tabella 3: Tabella riassuntiva 2
Con il termine h, indichiamo l’altezza dell’albero. Da questa tabelle possiamo
notare come la nuova forma di rappresentazione nella forma estesa (NEW) e compressa(NEWC) per quanto riguarda le operazione in tabella 2 non vanno peggio di
altri metodi. Infatti la forma compresse nel calcolo del padre di un nodo ha una
complessità bassa pari a quella di PR. Invece nelle altre operazioni non risultano
essere efficienti ma neanche hanno tempi più alti delle altre classi. Mentre dalla
tabella 3 NEW e NEWC vediamo che in alcune operazioni come ShortestPath vadano
meglio di altre. Infatti tale operazione che ha complessità O(h) poichè al più abbiamo
h antenati, ma nel caso peggiore h=n.
7
Risultati sperimentali
Per verificare i risultati teorici precedentemente esposti effettuiamo ora test
numerici per vedere l’andamento medio dei tempi di esecuzione di alcune operazioni.
Eseguiamo i test su un albero binario con nodi random ed un albero binario bilanciato.
• Remove: Eseguiamo anche in questo caso i test su entrambi i tipi di alberi.
Prendiamo in questo caso molti alberi con un numero sempre diverso di nodi
e calcoliamo il tempo medio di eliminazione da ogni albero dello stesso nodo.
Nel caso del metodo remove, come abbiamo visto dobbiamo sempre calcolarci i
discendenti e poi eliminare il nodo in questione con a seguito ogni discendente.
Siccome il calcolo dei discendenti nella nuova forma si riduce a semplice lettura
di una colonna ciò si ripercuote nell’esecuzione della remove poichè usando la
nuova forma di rappresentazione nella forma estesa si svolge con un tempo
medio più basso rispetto alle altre forme.
13
7 RISULTATI SPERIMENTALI
Figura 1: Tempo medio remove albero nodi random
Figura 2: Tempo medio remove alberi nodi random grafico 3D
14
7 RISULTATI SPERIMENTALI
Figura 3: Tempo medio remove alberi bilanciati
Figura 4: Tempo medio remove alberi bilanciati 3D
• ParentOf : Anche in questo caso conduciamo il test su alberi con nodi random
ed alberi bilanciati. Ogni test ricerchiamo su uno stesso albero il padre di ogni
nodo dell’albero a partire dalla radice fino alle foglie. Calcoliamo il tempo medio
per svolgere tale ricerca. In entrambe le tipologie di alberi possiamo notare
come PR e BTR risultano essere le forme note più convenienti per condurre
tale ricerca invece come la forma matriciale della nuova rappresentazione nella
versione contratta risulta essere migliore di AMR.
15
7 RISULTATI SPERIMENTALI
Figura 5: Tempo medio parentOf albero nodi random
Figura 6: Tempo medio parentOf alberi nodi random grafico 3D
16
7 RISULTATI SPERIMENTALI
Figura 7: Tempo medio parentOf alberi bilanciati
Figura 8: Tempo medio parentOf alberi bilanciati 3D
• allParentOf : In questo caso per testare i tempi d’esecuzione dell’algoritmo di
calcolo degli antenati di un nodo usiamo sempre uno stesso albero e cerchiamo
gli antenati di ogni nodo a partire dalla radice fino alle foglie. Calcoliamo
il tempo medio per condurre tale operazione. Possiamo vedere come nello
svolgimento di quest’operazione è conveniente usare un array sfruttando la forma
rappresentazione PR e BTR ma grazie alle due nuove forme di rappresentazione
diviene utile anche l’uso della matrice. Infatti entrambi le forme risultano essere
efficienti per ricercare gli antenati di un nodo.
17
7 RISULTATI SPERIMENTALI
Figura 9: Tempo medio AllParentOf albero nodi random
Figura 10: Tempo medio AllParentOf alberi nodi random grafico 3D
18
7 RISULTATI SPERIMENTALI
Figura 11: Tempo medio AllParentOfOf alberi bilanciati
Figura 12: Tempo medio AllParentOfOf alberi bilanciati 3D
• Height : In questo caso testo su più alberi e calcolo il tempo medio per il calcolo
dell’altezza di ogni albero. Il calcolo dell’altezza dell’albero viene influenzato
dal calcolo degli antenati e dalla ricerca della foglia. Dalla combinazione di
questi due fattori negli alberi con nodi random la nuova forma nella versione
estesa risulta essere efficiente.
19
7 RISULTATI SPERIMENTALI
Figura 13: Tempo medio Height alberi nodi random
Figura 14: Tempo medio Height alberi nodi random grafico 3D
20
8 ANALISI DELLE RAPPRESENTAZIONI DEGLI ALBERI BASATI SU
MATRICE
Figura 15: Tempo medio Height alberi bilanciati
Figura 16: Tempo medio Height alberi bilanciati 3D
8
Analisi delle rappresentazioni degli alberi basati
su matrice
Dopo aver descritto tutte le forme di rappresentazione degli alberi, tramite array
e matrici ed introdotte le nuove forme di rappresentazione matriciali degli alberi, in
questo capitolo studieremo ulteriore proprietà della matrice ed il legame che c’è tra
essa ed i grafi. Lo studio verrà effettuato sulla matrice di adiacenza, unica forma di
21
8 ANALISI DELLE RAPPRESENTAZIONI DEGLI ALBERI BASATI SU
MATRICE
rappresentazione matriciale presente nella letteratura. Possiamo usare lo spettro di
una matrice(autovalori di una matrice) per avere informazioni su un determinato
tipo di grafo. In questo modo creiamo una relazione tra gli autovalori di una matrice
e le proprietà di un grafo. Lo studio della relazione tra questi due oggetti si chiama
Teoria Spettrale dei grafi[10].
8.1
La matrice di adiacenza di un grafo
Come abbiamo già detto nella presentazione delle varie metodologie di presentazione dell’albero abbiamo che :
Definizione 4. La matrice di adiacenza è una matrice A di dimensione |V | × |V |
definita in questo modo :


1 se (i , j)∈ E(G)
aij = 
0 altrimenti .
Definiamo lo spettro del grafo G, assumendo spec(G) = spec(A) ed estendiamo a G
la terminologia usata per la matrice A (autovalori,autovettori, polinomio caratteristico
ecc...).Vediamo ora come dallo studio spettrale della matrice siamo in grado di cogliere
le caratteristiche di un grafo. Nel caso di alberi e grafi non orientati la matrice è
simmetrica e reale e tutti gli autovalori sono reali e mg (λi ) = ma (λi ) per ogni
autovalore di A.
Proposizione 1. Sia G un grafo con n vertici, sia A la sua matrice di adiacenza e
sia pG (λ):= pA (λ) il polinomio caratteristico di G. Se il grafo G ha t ≥ 1 componenti
connesse G1 . . . Gt allora pG = pG1 . . . pGt . Pertanto lo spettro di G è l’unione degli
spettri delle sue componenti, e la molteplicità di ciascun autovalore di G è la somma
delle sue molteplicità come autovalore delle componenti Gi
8.2
La matrice di adiacenza e i cammini tra i vertici
Proposizione 2. Sia G=(V, E) un grafo e sia A la sua matrice di adiacenza. Allora
per ogni x,y ∈ V e per ogni s ≥ 1 : (As )xy = numero di cammini tra x e y di lunghezza
s.
Osservazione 2. Sia G = (V, E) un grafo ed A la sua matrice di adiacenza. Dalla
proposizione precedente segue che per ogni x ∈ V : (A2 )xx = dG (x). e quindi la traccia
P
P
di A2 è tr(A2 ) := x∈V (A)2xx = x∈V dG (x) = 2|E|.
Proposizione 3. Sia M ∈ C n×n . La traccia ed il determinate di M sono rispettivaP
P
mente la somma e il prodotto dei suoi autovalori : tr(M) := mij = λi ∈Spec(M ) λi
Q
det(M ) := λi ∈Spec(M ) λi
22
9 CONCLUSIONI
Lemma 1. Siano M ∈ C
,λsn con (s= 1,2,3,. . . )
n×n
e Spec(M) = λ1 , . . . , λn . Allora Spec(M s ) = λs1 ,. . .
Proposizione 4. Sia G = (V, E) un grafo con n vertici, sia A la matrice di adiacenza
e sia Spec(G) = {λ1 ,. . . ,λn } i suoi autovalori. Allora :
1.
P
2.
P 2
P
λi = x∈V (A)2xx =
P
λi =
2=
3.
P
x∈V (A)xx =
x∈V
P 3
λ
tr(A) = 0;
tr(A2 ) = numero delle passeggiate chiuse di G lunghezza
dG (x) = 2|E|
= x∈V (A)3xx = tr(A3 ) = numero delle passeggiate chiuse di G lunghezza
3 = 6 volte il numero dei triangoli del grafo
i
P
4. Conclusione : tr(As )= numero delle passeggiate chiuse di G lunghezza s =
P s
λi = numero delle passeggiate chiuse di G di lunghezza s
8.3
La matrice binaria della nuova forma
La nuova matrice viene definita nel seguente modo :


1 se j è antenato i oppure i=j
Aij = 
0 altrimenti
Osservazione 3. Da tale definizione deriva che la traccia della nostra matrice è
pari sempre ad n ove con n intendiamo il numero di nodi dell’albero.
Rispetto alla matrice di adiacenza per come è definita dipendendo dagli antenati
di un nodo, è conveniente principalmente per la rappresentazione di un albero. Per
rappresentare un grafo qualsiasi con questa metodologia dovremmo prima trasformarlo
in albero. Calcolando gli autovalori di tale matrice (attraverso l’ analisi numerica
e anche con il calcolo delle radici del polinomio caratteristico) possiamo dire che
P
P
in questo caso λi = x∈V (A)xx = tr(A) = n =|V |. Attraverso lo studio degli
autovalori della matrice binaria nella nuova forma si potrebbero trovare particolari
caratteristiche di un albero.
9
Conclusioni
L’ albero è una struttura dati non lineare, che segue un modello gerarchico. La
sua struttura non sequenziale permette di stabilire dei rapporti di parentela tra i
nodi, per cui è possibile definire un nodo padre, un nodo figlio ed un nodo fratello
per questo viene impiegato in molti campi dell’informatica. Attraverso la struttura
23
9 CONCLUSIONI
dati ad albero siamo in grado di modellare aspetti della vita reale, come ad esempio
l’organigramma di un’azienda, la tassonomia degli organismi animali e vegetali, le
gerarchie militari e la genealogia di una persona.
La letteratura è molto ricca per quanto riguarda la descrizione di queste strutture
dati che rappresentano l’albero nella memoria del calcolatore, tuttavia è molto difficile
trovare delle analisi comparative che mostrino l’efficienza di ogni forma rispetto alle
altre.
Nel corso della tesi sono state presentate le metodologie che utilizzano uno o più
array. Tutte le forme di rappresentazione note in letteratura sono state presentate
nel secondo capitolo della tesi, nel quale abbiamo visto che ogni metodologia ricalca
un legame di parentela specifico che si sviluppa tra i nodi dell’albero.
Nel terzo capitolo è stata presentata la nuova metodologia di rappresentazione
nella forma estesa e compressa. Questo nuovo metodo rispecchia il legame tra due
nodi di essere antenati. Sono entrambe due matrici, nella forma estesa è binaria
e quadrata mentre in quella compressa è una matrice dove le righe sono array di
dimensioni variabili. In ogni riga sono contenuti gli antenati del nodo associato a
tale riga ed il nodo stesso.
Questa forma usa le matrici e quindi nel sesto capitolo della tesi abbiamo evidenziato l’importanza dell’uso della matrice nella rappresentazione di grafi grazie
al legame che nasce tra le proprietà algebriche della matrice e la teoria dei grafi.
Infatti attraverso lo studio dello spettro della matrice di adiacenza siamo in grado di
conoscere diverse caratteristiche di un grafo, uno studio spettrale è stato condotto
anche sulla matrice della nuova rappresentazione nella forma estesa.
Siamo passati alla comparazione delle varie forme di rappresentazione, abbiamo
descritto ed implementato in Java gli algoritmi di esecuzione nelle principiali operazioni, abbiamo calcolato l’occupazione di memoria all’interno del calcolatore ed
infine la complessità computazionale di ogni operazione per ogni forma.
Nel quinto capitolo sono stati condotti test numerici da quali è possibile dedurre
come la nuova forma di rappresentazione nella forma estesa e compressa ha portato dei
miglioramenti sull’esecuzione di alcune operazioni e dove ogni struttura dati risulta
essere valida nell’esecuzione di un operazione. La nuova forma risulta essere efficiente
nel calcolo degli antenati, nel calcolo dell’altezza dell’albero, nel calcolo del cammino
minimo tra due nodi , nella semplice ricerca del padre del nodo e nella rimozione di
un nodo. Tali miglioramenti specialmente apportati in queste operazione rendono
utilizzabile l’albero mediante questa nuova forma di rappresentazione nella gestione
dell’organigramma di un’azienda, di siti web o anche della rete di comunicazione tra
gli enti di una stessa azienda.
Come sviluppi futuri si potrebbero analizzare con maggior dettaglio i vantaggi
24
RIFERIMENTI BIBLIOGRAFICI
che un metodo di compressione dei dati apporta nell’uso di questa forma.
Riferimenti bibliografici
[1] Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Introduzione agli
algoritmi, seconda edizione, McGraw - Hill,1999.
[2] John R. Hubbard, Schaum’s Outline of Data Structures with Java, McGraw Hill, 2000
[3] Robert Lafore, Data Structures e Algorithms in Java, Sams ,1998.
[4] Alfred V.Aho,John E. Hopcroft, and Jeffrey Ullman. Data Structures e Algorithms .Addison-Wesley Longman Publishing Co. , Inc.,Boston , MA, USA, 1st
edition, 1983.
[5] J.A. Bondy and U.S.R. Murty.Graph Theory. Springer Publishing Company,
Incorporated, 1st edition, 2008
[6] P.S. Deshpande and O.G. Kakde. C e Data Structures (Eletrical and Computer
Engineering Series). Charles River Media, Inc, Rockland, MA,USA,2004.
[7] Alan Gibbons, Algorithmic graph theory, Cambridge University Press, 19948
[8] Richard J. Trudeau, Introduction to Graph Theory, Dover Publiactions Ins.,
New York,1993.
[9] L.W. Beineke, R.J. Wilson, Topics in algebraic graph theory. Edited by L.W.
Beineke and R.J. Wilson. Cambridge University Press, 2004.
[10] N. Biggs ,Algebraic graph theory. Second edition. Cambridge University
Press,1993.
[11] Alessandro Colantonio and Roberto Di Pietro, CONCISE: COmpressed ’N’
Composable Integer SEt, Information Processing Letters, Elsevier, 2010, 110 ,16,
644–650.
25