Laboratorio di Algoritmi
II Semestre 2005/2006
Laboratorio di Algoritmi e Strutture Dati
II Semestre 2005/2006
Marco Antoniotti
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
0
Laboratorio di Algoritmi
• Corso intermedio
• “Proseguimento” del corso di Algoritmi
• Attenzione centrata su
– Strutture dati ed algoritmi
– Loro effettiva implementazione
– Soluzione di problemi
Argomento
Strutture dati ed algoritmi
Tipo di dati
Liste, code, “heaps”, union-find
Ordinamenti
Quicksort, mergesort, heapsort, radix-sort
Dizionari
Hash-tables, Alberi (red-black, splay, B-trees)
Stringhe
Huffman, Automi (espressioni regolari)
Grafi
Depth First Search, Dijkstra, Prim, Kruskal
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
1
1
Laboratorio di Algoritmi
II Semestre 2005/2006
Algoritmi e strutture dati:
dove li troviamo
•
•
•
•
•
•
•
•
•
Internet: Google, Akamai, packet routing
Biologia: Il progetto del Genoma, ricostruzione di proteine
Hardware: Progettazione di circuiti, HW/SW Co-design
Sicurezza: Protezione della riservatezza dei dati personali,
commercio elettronico
Grafica: Il Signore degli Anelli, Doom, Halo, Simcity
Multimedia: Ipod e MP3
Finanza, economia e banche: Base dati e transazioni varie;
gestione ordini su mercati azionari, previsioni economiche
Fisica: Simulazione di vari problemi quali N-corpi, astrofisica,
meccanica quantistica
Trasporti: Controllo del traffico, delle emissioni e logistica
…
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
2
Referenti del corso
• Lezioni ed esercitazioni
Marco Antoniotti, Professore Associato
Orario di ricevimento: Mercoledì 11:00-13:00
• Laboratorio
Yuri Pirola, Esercitatore
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
3
2
Laboratorio di Algoritmi
II Semestre 2005/2006
Orari
• Lezioni ed esercitazioni
Laboratorio di Algoritmi Primavera 2006
Lezione
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Data
7 March
9 March
14 March
16 March
21 March
23 March
2006
2006
2006
2006
2006
2006
Argomento
union-find
Introduzione al C/C++
Introduzione al C/C++
Richiami di Analisi di Algoritmi
Pile e Code (Stacks and Queues)
Ordinamenti semplici
Capitoli Libro
1
n/a
n/a
2
3e4
6
30 March 2006 Ordinamenti efficienti ed applicazioni
4 April 2006 Code con priorità (Priority Queues)
6 April 2006 Liste ed iteratori (Lists e Iterators)
20 April
27 April
2 May
4 May
9 May
11 May
16 May
18 May
23 May
25 May
30 May
1 June
2006
2006
2006
2006
2006
2006
2006
2006
2006
2006
2006
2006
Hashing ed applicazioni
Esame intermedio
Alberi binari (Binary Trees)
Alberi bilanciati (Balanced Trees)
Ordinamenti senza confronto (Counting e Radix sort)
Tries
Ricerca di stringhe e automi deterministici (DFA)
Compressione di dati
Grafi bidirezionali
"Minimum Spanning Trees"
Grafi diretti e Ricerca in profondità (DFS)
Cammini minimi
7e8
9
n/a
14
12
12
n/a
15
n/a
n/a
17 e 18
20
19
21
6 June 2006 Possibile recupero
8 June 2006 Possibile recupero
27 June 2006 Esame
12 July 2006 Esame
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
4
Orari
• Laboratorio - Yuri Pirola
Laboratorio^2 di Algoritmi Primavera 2006
Lab 721 9:30-12:30
Lab
Data
1
4 April 2006
2
11 April 2006
3
2 May 2006
4
9 May 2006
5
16 May 2006
6
23 May 2006
7
30 May 2006
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
5
3
Laboratorio di Algoritmi
II Semestre 2005/2006
Materiali del corso
•
•
•
•
•
Slides ed appunti
Esercizi
Compiti
Progetti
Libri
–
–
–
–
–
–
Algorithms in C/C++ Part 1-4, Sedgewick
Algorithms in C/C++ Part 5, Sedgewick
Introduction to Algorithms, Cormen, Leiserson, Rivest
The C Programming Language, Kernigham, Ritchie
C++ Primer, Lippman, Lajoie, Moo
The C++ Programming Language, Stroustroup
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
6
Valutazione
•
•
•
•
Compiti di programmazione: 20%
Esercizi vari: 10%
Esame intermedio: 20%
Progetto Finale e Discussione (Esame): 50%
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
7
4
Laboratorio di Algoritmi
II Semestre 2005/2006
UNION-FIND
• Un algoritmo fondamentale
• Capitolo 1, Sedgewick
• Un problema esemplare:
Connettività in una rete (grafo)
– Nodi su una griglia
– Si aggiungono un tot di
connessioni tra nodi
– Alla fine di questa procedura
esiste un cammino tra il nodo A
ed il nodo B?
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
8
Connettività su una griglia
• Quali sono gli ingredienti per questo algoritmo?
• Supponiamo di inserire nel programma una sequenza di
connessioni nella forma
p
q
• Ogni volta che inseriamo una “connessione” il programma
controlla se la connessione tra p e q è nuova. Se si, p e q
vengono stampati.
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
9
5
Laboratorio di Algoritmi
II Semestre 2005/2006
Connettività su una griglia
• Esempio
Input
Output
3 4
3 4
4 9
4 9
8 0
8 0
2 3
2 3
5 6
5 6
2 9
Evidenza
2-3-4-9
5 9
5 9
5 6
5-6
0 2
2-3-4-8-0
6 1
6 1
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
10
Interpretazione del problema come
UNION-FIND
• Quali sono le operazioni fondamentali che dobbiamo
rappresentare?
–
–
–
–
Oggetti
Insiemi disgiunti di oggetti
FIND: due oggetti sono nello stesso insieme?
UNION: rimpiazzare due insiemi contenenti due oggetti con
la loro unione
• Obbiettivo
– Progettare una struttura dati e delle operazioni “efficienti” per
risolvere il problema di UNION e FIND
– Note:
• Il numero M di operazioni (UNION e/o FIND) da eseguire può
essere molto grande
• Il numero N degli oggetti da considerare può essere enorme
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
11
6
Laboratorio di Algoritmi
II Semestre 2005/2006
Interpretazione del problema come
UNION-FIND
•
Quali sono gli oggetti che dobbiamo rappresentare?
– Punti sulla griglia (rappresentati da numeri interi)
2 3 4 5 9 7
– Sottinsiemi disgiunti di oggetti (sottinsiemi di punti connessi)
2-3-4 5 9-7
•
Quali sono le operazioni che dobbiamo implementare?
– FIND: gli oggetti 2 e 4 sono nello stesso insieme?
Ovvero, i punti 2 e 4 sono connessi?
2-3-4 5 9-7
– UNION: dati due insiemi, costruiamo la loro unione.
Ovvero, aggiungiamo una connessione tra due punti sulla griglia (ad
esempio 3 e 9)
2-3-4-9-7 5
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
12
Oggetti
• Diverse applicazioni (programmi) manipolano oggetti diversi
–
–
–
–
–
Alias di variabili in un compilatore
Pixels in una foto digitale
Computers in una rete
Pagine web
Componenti in un circuito logico
• Per convenzione (dettata dai linguaggi più diffusi) è comodo
indicare questi oggetti con gli interi da 0 a N-1
– I dettagli degli oggetti non sono rilevanti per l’algoritmo
UNION-FIND
– Usare dei numeri interi facilita molte operazioni
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
13
7
Laboratorio di Algoritmi
II Semestre 2005/2006
QUICK-FIND
• Una soluzione semplice, ma non particolarmente efficiente è la
seguente
• Struttura dati: un array set_id_of[] di dimensione N.
• Interpretazione: due oggetti p e q appartengono allo stesso
insieme, ovvero, due punti p e q sono connessi, se e solo se
hanno lo stesso ‘set_id’.
i
0
1
2
3
4
5
6
7
8
9
set_id_of[i]
0
1
9
9
9
6
6
7
8
9
5 e 6 sono connessi
2,3,4 e 9 sono connessi
• FIND: controlla se p e q hanno lo stesso ‘set_id’.
Siccome set_id_of[3] != set_id_of[6], ne consegue
che 3 e 6 non sono connessi
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
14
QUICK-FIND
• UNION: per unire due componenti che contengono p e q si
cambiano tutti valori di set_id_of[p] a set_id_of[q].
i
0
1
2
3
4
5
6
7
8
9
set_id_of[i]
0
1
6
6
6
6
6
7
8
6
3 e 6 vengono connessi,
i loro insiemi uniti,
2,3,4,5,6 e 9 sono connessi
Parecchi valori possono cambiare
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
15
8
Laboratorio di Algoritmi
II Semestre 2005/2006
QUICK-FIND
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
16
QUICK-FIND: implementazione in C++
#include <iostream.h>
static const int N = 10;
int set_id_of[N];
void init_quick_find() {
for (int i = 0; i < N; i++) set_id_of[i] = i; // Ogni oggetto è in un
// insieme a se’ stante
}
bool find(int p, int q) {
return set_id_of[p] == set_id_of[q]; // 1 operazione!
}
void unite(int p, int q) {
int pid = set_id_of[p];
for (int i = 0; i < N; i++)
if (set_id_of[i] == pid) set_id_of[i] = set_id_of[q]; // N operazioni!
}
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
17
9
Laboratorio di Algoritmi
II Semestre 2005/2006
Dimensione del problema e tempo di
computazione
•
Qualche anno fa (inizio secolo), la potenza di calcolo a nostra disposizione
aveva le seguenti caratteristiche
–
–
–
•
Consideriamo il seguente, enorme (!) problema per
QUICK-FIND
–
–
–
•
109 operazioni al secondo
109 “parole” di memoria
Quindi, ogni parola può essere “visitata” in 1 secondo (valore valido dal 1950)
1010 archi che connettono 109 nodi
QUICK-FIND deve eseguire 1020 operazioni (10 operazioni per query)
Il computo finirà tra 3000 anni!
Paradossalmente, gli algoritmi quadratici peggiorano con il migliorare
dell’hardware.
–
–
–
I nuovi computer possono essere 10 volte più veloci
Ma hanno anche 10 volte più memoria, ergo il problema può essere 10 volte più
grande
Con algoritmi quadratici, ci vuole 10 volte il tempo
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
18
Cerchiamo di migliorare
• QUICK-UNION
– Rallentiamo l’operazione FIND
– Sveltiamo l’operazione UNION
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
19
10
Laboratorio di Algoritmi
II Semestre 2005/2006
QUICK-UNION
•
•
Struttura dati: un array set_id_of[] di dimensione N.
Interpretazione: due oggetti p e q appartengono allo stesso
insieme, ovvero, due punti p e q sono connessi, se e solo
se hanno lo stesso ‘set_id’.
Però, dato un oggetto x, il suo ‘set_id’ viene calcolato
come
set_id_of[set_id_of[…set_id_of[x]…]]
ovvero interpretiamo set_id_of[x] come un “genitore”,
ed iteriamo finchè il valore non cambia. Il valore finale
viene detto radice. Ovvero il ‘set_id’ di un oggetto è la sua
radice.
i
0
1
2
3
4
5
6
7
8
9
set_id_of[i]
0
1
9
4
9
6
6
7
8
9
p = 3, q = 5
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
20
QUICK-UNION
•
FIND: controlla se due oggetti hanno la stessa radice
•
UNION: assegna il set_id della radice di p al set_id della radice di q.
Ovvero
set_id_of[find_root(q)] = find_root(p)
i
0
1
2
3
4
5
6
7
8
9
set_id_of[i]
0
1
9
4
9
6
9
7
8
9
Solo un cambio di valore
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
21
11
Laboratorio di Algoritmi
II Semestre 2005/2006
QUICK-UNION
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
22
QUICK-UNION: implementazione in
C++
int find_root(int x) {
while (set_id_of[x] != x) x = set_id_of[x];
return x;
}
bool find(int p, int q) {
return find_root(p) == find_root(q);
}
void unite(int p, int q) {
int rp = find_root(p);
int rq = find_root(q);
if (rp != rq) set_id_of[rq] = rp;
}
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
23
12
Laboratorio di Algoritmi
II Semestre 2005/2006
Riassunto
• QUICK-FIND ha il seguente problema
– UNION costa troppo
– Gli alberi risultanti sono piatti, ma è difficile costruirli
• QUICK-UNION ha il problema simmetrico
– FIND può costare troppo
– Gli alberi risultanti possono diventare molto profondi
Soluzione
UNION
FIND
QUICK-FIND
N
1
QUICK-UNION
1*
N
* Unione di due radici
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
24
QUICK-UNION PESATA
•
Modifichiamo QUICK-UNION in modo da evitare alberi troppo profondi
– Si mantiene informazione sulle dimensioni di un insieme
– UNION bilancia l’albero creato al momento dell’unione di due insiemi,
collegando il sotto-albero più piccolo a quello più grande
•
Esempio: unione di 5 e 3
– QUICK-UNION: 9 viene collegato a 6
– QUICK-UNION Pesata: 6 viene collegato a 9
dimensione
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
25
13
Laboratorio di Algoritmi
II Semestre 2005/2006
QUICK-UNION Pesata
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
26
QUICK-UNION Pesata:
implementazione in C++
•
•
•
•
Molto simile all’implementazione di QUICK-UNION
Si mantiene anche un array size[x] che contiene le dimensioni di
ogni (sotto)insieme
FIND rimane uguale a QUICK-UNION
UNION deve scegliere come costruire l’albero “unione” di due insiemi e
deve mantenere le dimensioni dei vari (sotto)insiemi in size[x]
if (size[p] < size[q]) {
set_id_of[p] = q;
size[q] += size[p];
} else {
set_id_of[q] = p;
size[p] += size[q];
}
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
27
14
Laboratorio di Algoritmi
II Semestre 2005/2006
QUICK-UNION Pesata: analisi
• Analisi
– FIND: richiede un tempo direttamente proporzionale alla “profondità”
degli alberi di p e q
– UNION: date due radici, richiede tempo costante
– Nota: la profondità massima raggiungibile è 1 + lg(N) [prova
richiesta]
–
Soluzione
UNION
FIND
QUICK-FIND
N
1
QUICK-UNION
1*
N
QUICK-UNION
Pesata
lg(N)
lg(N)
• È possibile però migliorare ulteriormente.
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
28
Compressione di percorso
• Compressione di percorso (“Path Compression”):
non appena si è computata la radice di x, la si assegna come
‘set_id’ di ogni nodo visitato
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
29
15
Laboratorio di Algoritmi
II Semestre 2005/2006
QUICK-UNION Pesata con
Compressione di Percorso
• Compressione di percorso
– Implementazione standard: si aggiunge un secondo ciclo a
find_root(x) per modificare la radice di ogni nodo visitato
– Variante più semplice: ci si assicura che ogni nodo visitato “punti”
al suo “nonno” (“grandparent”)
int find_root(int x) {
while (x != set_id_of[x]) {
set_id_of[x] = set_id_of[set_id_of[x]];
// Una sola
// linea di
// codice in più
x = set_id_of[x];
}
return x;
}
• In pratica questa variante funziona benissimo.
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
30
QUICK-UNION Pesata con
Compressione di Percorso
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
31
16
Laboratorio di Algoritmi
II Semestre 2005/2006
QUICK-UNION pesata con
compressione di percorso
• Teorema: Una sequenza di M operazioni UNION e FIND su N
elementi richiede O(N + M lg*(N)) tempo
– La dimostrazione è molto complicata
– L’algoritmo però rimane semplice
• Nota: lg*(N) è la funzione inversa della funzione di Ackermann; il
suo valore è pressochè costante nel nostro universo
lg*(2) = 1
lg*(4) = 2
lg*(16) = 3
lg*(265536) = 5
lg*(216) = 4
• Sebbene QUICK-UNION pesato con compressione di percorso
(WQUPC) non sia teoricamente lineare, lo è in pratica
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
32
Discussione
• Tornando all’esempio precedente
–
–
–
–
1010 connessioni tra 109 oggetti (enorme problema pratico)
WQUPC riduce il tempo necessario da 3000 anni ad 1 minuto
Un supercomputer non aiuterebbe più di tanto
Un buon algoritmo rende il problema trattabile
• Morale della favola: con un buon algoritmo il vostro telefono
cellulare batte un supercomputer
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
33
17
Laboratorio di Algoritmi
II Semestre 2005/2006
Applicazioni
•
•
•
•
•
•
•
•
•
Connettività su grafi
Hex
Percolazione
Tipizzazione Hendley-Milner
Visione
“Least Common Ancestor”
Algoritmo di Kruskal per il “Minimum Spannin Tree”
Equivalenza di due automi a stati finiti
“Schedulazione” di task
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
34
Hex
•
Il gioco Hex (Piet Hein 1942, John Nash 1948, Parker Brothers 1962)
– Due giocatori scelgono alternativamente delle caselle esagonali
– Il nero deve congiungere le caselle dall’alto a sinistra al basso a destra
– Il bianco deve congiungere le le caselle dall’alto a destra al basso a sinistra
•
Obiettivo: controllare quando uno dei due giocatori ha vinto
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
35
18
Laboratorio di Algoritmi
II Semestre 2005/2006
Conclusione
• Algoritmi semplici possono essere molto utili
• È utile iniziare a risolvere un problema usando tecniche grezze
e non particolarmente efficienti
– Non le si usino per problem grandi!
– Non si possono usare per problemi grandi
• Si faccia sempre molta attenzione al caso peggiore
• Si individui l’insieme di astrazioni fondamentali
• Si generalizzi la soluzione a vari domini di applicazione
II Semestre 2005/2006
Laboratorio Algoritmi - Marco Antoniotti
36
19