Implementazione in C di alcuni algoritmi e strutture dati visti a lezione: ▻
Algoritmi ... Per poter fare l'orale bisogna preparare un progetto in C: ▻ tre
settimane a ...
Laboratorio di Algoritmi e Strutture Dati
Marco Tarini e-mail:
[email protected]
Argomenti del corso Calcolo del tempo di computazione di un algoritmo: I
Esercizi di analisi formale: sommatorie, ricorrenze;
I
Misurazione sperimentale dei tempi di calcolo;
Implementazione in C di alcuni algoritmi e strutture dati visti a lezione: I
Algoritmi di Ricerca e Ordinamento;
I
Strutture: liste concatenate, alberi, BST, RB-alberi, Grafi;
I
Algoritmi ricorsivi e iterativi;
I
Programmazione dinamica.
Variazioni di implementazioni usate frequentemente. Lezioni frontali e (secondo disponibilit`a delle aule) in laboratorio.
Modalit`a d’esame
L’esame `e in comune con il Prof. Massazza (15 CFU totali). Per poter fare l’orale bisogna preparare un progetto in C: I
tre settimane a disposizione;
I
si consegna indicativamente 7gg prima dell’orale (la data varia a seconda dell’appello);
I
si discute nello stesso giorno dell’orale.
Tutte le informazioni sul sito del corso.
Materiale didattico Materiale del corso (sul sito, a breve): I
file delle presentazioni;
I
codice usato a lezione;
I
calendario delle lezioni;
I
avvisi di modifiche all’orario;
I
avvisi per gli appelli;
I
materiale aggiuntivo;
I
links utili;
Per informazioni, contattatemi per posta elettronica. e-mail:
[email protected]
Testi di riferimento
Robert Sedgewick. Algoritmi in C. Addison-Wesley, Pearson Italia, 2002. Brian W. Kernighan, Dennis M. Ritchie. Linguaggio C (seconda edizione), Nuova edizione italiana, Pearson, Italia, 2004. Al Kelley, Ira Pohl. C Didattica e Programmazione (A book on C, 4th edition). Pearson, Italia, 2004.
Compilatore di riferimento
Useremo il compilatore gcc (GNU Compiler Collection). Scaricatelo da I
per UNIX: http://gcc.gnu.org/
I
per Windows: http://www.cs.colorado.edu/~main/cs1300/
Oppure un IDE qualunque I
es, DevCpp
Calcolo delle prestazioni degli algoritmi Un algoritmo `e un insieme di istruzioni finalizzate a risolvere un problema. I
Ci possono essere pi` u algoritmi che risolvono lo stesso problema.
I
(ma un algoritmo risolve un solo problema).
Calcolo delle prestazioni degli algoritmi Un algoritmo `e un insieme di istruzioni finalizzate a risolvere un problema. I
Ci possono essere pi` u algoritmi che risolvono lo stesso problema.
I
(ma un algoritmo risolve un solo problema).
Per valutare quale sia migliore si analizzano quante risorse impegano per resituire una soluzione: I
tempo (di calcolo)
I
spazio (di memoria)
I
banda (di rete)
I
eccetera
Calcolo delle prestazioni degli algoritmi Un algoritmo `e un insieme di istruzioni finalizzate a risolvere un problema. I
Ci possono essere pi` u algoritmi che risolvono lo stesso problema.
I
(ma un algoritmo risolve un solo problema).
Per valutare quale sia migliore si analizzano quante risorse impegano per resituire una soluzione: I
tempo (di calcolo) ← soprattutto questo
I
spazio (di memoria)
I
banda (di rete)
I
eccetera
Calcolo delle prestazioni degli algoritmi (cont.) Il tempo di esecuzione dipende da molti fattori. Lo stesso algoritmo impiega: (1) ;
I
tempo diverso su input diversi
I
tempi diversi sullo stesso input, quando `e eseguito su computer diversi (2) .
Calcolo delle prestazioni degli algoritmi (cont.) Il tempo di esecuzione dipende da molti fattori. Lo stesso algoritmo impiega: (1) ;
I
tempo diverso su input diversi
I
tempi diversi sullo stesso input, quando `e eseguito su computer diversi (2) .
(2)
L’analisi di complessit`a degli algoritmi cerca di stabilire il tempo di calcolo indipendentemente dalla macchina sulla quale `e implementato il programma.
Calcolo delle prestazioni degli algoritmi (cont.) Il tempo di esecuzione dipende da molti fattori. Lo stesso algoritmo impiega: (1) ;
I
tempo diverso su input diversi
I
tempi diversi sullo stesso input, quando `e eseguito su computer diversi (2) .
(1)
Chiediamo che il tempo di esecuzione sia stimato in relazione alla grandezza dell’input. (2)
L’analisi di complessit`a degli algoritmi cerca di stabilire il tempo di calcolo indipendentemente dalla macchina sulla quale `e implementato il programma.
Esempio 1
Supponiamo che la stampa di un numero costi 1 secondo e consideriamo un programma che legge un numero n e: Caso A: stampa i numeri da 0 a n − 1. Caso B: stampa tutte le coppie di numeri da 0 a n − 1. Caso C: stampa tutte le potenze di 2 pi` u piccole di n.
Esempio 1 (cont.) Tabella di esecuzione
Al variare di n indichiamo quanti secondi spende ogni algoritmo per la stampa: n 1 2 3 4 5 10 20 50 100
A
B
C
Esempio 1 (cont.) Tabella di esecuzione
Al variare di n indichiamo quanti secondi spende ogni algoritmo per la stampa: n 1 2 3 4 5 10 20 50 100
A 1 2 3 4 5 10 20 50 100
B
C
Esempio 1 (cont.) Tabella di esecuzione
Al variare di n indichiamo quanti secondi spende ogni algoritmo per la stampa: n 1 2 3 4 5 10 20 50 100
A 1 2 3 4 5 10 20 50 100
B 1 4 9 16 25 100 400 2500 10000
C
Esempio 1 (cont.) Tabella di esecuzione
Al variare di n indichiamo quanti secondi spende ogni algoritmo per la stampa: n 1 2 3 4 5 10 20 50 100
A 1 2 3 4 5 10 20 50 100
B 1 4 9 16 25 100 400 2500 10000
C 1 2 2 3 3 4 5 6 7
Esempio 1 (cont.) Tabella di esecuzione
Al variare di n indichiamo quanti secondi spende ogni algoritmo per la stampa: n 1 2 3 4 5 10 20 50 100 1000
A 1 2 3 4 5 10 20 50 100
B 1 4 9 16 25 100 400 2500 10000
C 1 2 2 3 3 4 5 6 7
Esempio 1 (cont.) Tabella di esecuzione
Al variare di n indichiamo quanti secondi spende ogni algoritmo per la stampa: n 1 2 3 4 5 10 20 50 100 1000
A 1 2 3 4 5 10 20 50 100 1.000
B 1 4 9 16 25 100 400 2500 10000 1.000.000
C 1 2 2 3 3 4 5 6 7 10
Esempio 1 (cont.) Tabella di esecuzione
Al variare di n indichiamo quanti secondi spende ogni algoritmo per la stampa: n 1 2 3 4 5 10 20 50 100 1000 106
A 1 2 3 4 5 10 20 50 100 1.000
B 1 4 9 16 25 100 400 2500 10000 1.000.000
C 1 2 2 3 3 4 5 6 7 10
Esempio 1 (cont.) Tabella di esecuzione
Al variare di n indichiamo quanti secondi spende ogni algoritmo per la stampa: n 1 2 3 4 5 10 20 50 100 1000 106
A 1 2 3 4 5 10 20 50 100 1.000 106
B 1 4 9 16 25 100 400 2500 10000 1.000.000 1012
C 1 2 2 3 3 4 5 6 7 10 20
Esempio 1 (cont.) Le differenze tra il costo dei diversi algoritmi sono tanto pi` u grandi quanto ` e pi` u grande l’input. Caso A se l’input raddoppia di grandezza, il costo raddoppia. Caso B se l’input raddoppia di grandezza, il costo quadruplica. 25 Caso C se l’input raddoppia di grandezza, il costo aumenta di 1 sec. 20
15
10
5
0 1
2
3 n
4
5
Velocit`a di crescita delle funzioni
La complessit`a di un algoritmo deve quindi essere funzione della dimensione dell’input. Per valori piccoli degli input, algoritmi con tempi di esecuzione diversi possono impiegare in realt`a tempi molto simili. Ma quando gli input hanno dimensione maggiore, allora la differenza diventa rilevante.
Pseudo-codice
I
Caso A: for (i=0;i= 0 ) && ( a[j] > v ) ) { a[j+1] = a[j]; j--; } a[j+1] = v; } } I
Caso migliore: Θ(n);
I
Caso peggiore: Θ(n2 );
I
Caso medio: Θ(n2 );
Esempio.
La funzione f : n ∈ N 7→ 2n `e O(n). Infatti per n > 1 si ha 2n ≤ 3n.
Esercizi: I
5n2 + n = O(n2 ) ?
I
log2 (8n) = O(n) ?
I
n2 = O(n) ?
Alcune propriet`a
O e Ω sono relazioni opposte. I
se g = O(f ) allora f = Ω(g )
Alcune propriet`a
O e Ω sono relazioni opposte. I
se g = O(f ) allora f = Ω(g )
Per ogni funz f g h da naturali a naturali I
f = Θ(f ) (riflessiva)
I
se f = Θ(g ) allora g = Θ(f ) (simmetrica)
I
se f = Θ(g ) e g = Θ(h) allora f = Θ(h) (transitiva)
Alcune propriet`a
O e Ω sono relazioni opposte. I
se g = O(f ) allora f = Ω(g )
Θ `e una relazione di equivalenza! Per ogni funz f g h da naturali a naturali I
f = Θ(f ) (riflessiva)
I
se f = Θ(g ) allora g = Θ(f ) (simmetrica)
I
se f = Θ(g ) e g = Θ(h) allora f = Θ(h) (transitiva)
In quanto tale, Θ divide le funzioni in classi di equivalenza.
Funzioni di riferimento Si usano alcune funzioni di riferimento per la notazione asintotica. I
f = Θ(1) : f ha andamento costante
I
f = Θ(log (N)) : f ha andamento logaritmico
I
f = Θ(N) : f ha andamento lineare
I
f = Θ(Nlog (N)) : f ha andamento pseudo-lineare
I
f = Θ(N 2 ) : f ha andamento quadratico
I
f = Θ(N 3 ) : f ha andamento cubico ...
I
f = Θ(N K ) : f ha andamento polinomiale (K costante)
I
f = Θ(K N ) : f ha andamento esponenziale (K costante)
Sono classi di equivalenza per Θ! (una per ogni K ).
A parole
Per N abbastanza grandi, e a meno di una costante moltiplicativia... f = O(g ) significa “f vale meno di g ” f = Ω(g ) significa “f vale pi` u di g ” f = Θ(g ) significa “f e g si comportano allo stesso modo”
f = O(N 2 ) significa “f ha andamento al pi` u quadratico”. 2 f = Ω(N ) significa “f ha andamento almeno quadratico”. f = Θ(N 2 ) significa “f ha andamento quadratico”.
Esercizio 2: Alberi Quanti nodi ha un albero k-ario completo di altezza h?
Esercizio 2: Alberi Quanti nodi ha un albero k-ario completo di altezza h?
I
il livello 0 contiene 1 nodo (la radice);
I
il livello 1 contiene k nodi;
I
il livello 2 contiene k 2 nodi;
I
il livello i contiene k i nodi;
Esercizio 2: Alberi Quanti nodi ha un albero k-ario completo di altezza h?
I
il livello 0 contiene 1 nodo (la radice);
I
il livello 1 contiene k nodi;
I
il livello 2 contiene k 2 nodi;
I
il livello i contiene k i nodi; 2
1 + k + k + ··· + k
(h−1)
=
h−1 X i=0
ki =
kh − 1 k −1
Esercizio 2: Alberi (cont.) Quanti cammini diversi partono dalla radice?
E quant’`e la lunghezza totale di questi cammini?
Esercizio 2: Alberi (cont.) Quanti cammini diversi partono dalla radice? I
Esiste uno ed un solo cammino dalla radice ad ogni nodo: kh − 1 −1 k −1
E quant’`e la lunghezza totale di questi cammini?
Esercizio 2: Alberi (cont.) Quanti cammini diversi partono dalla radice? I
Esiste uno ed un solo cammino dalla radice ad ogni nodo: kh − 1 −1 k −1
E quant’`e la lunghezza totale di questi cammini? I
(per i nodi a livello 1) ho k cammini di lunghezza 1;
I
(per i nodi a livello 2) ho k 2 cammini di lunghezza 2;
I
(per i nodi a livello i) ho k i cammini di lunghezza i;
Esercizio 2: Alberi (cont.) Quanti cammini diversi partono dalla radice? I
Esiste uno ed un solo cammino dalla radice ad ogni nodo: kh − 1 −1 k −1
E quant’`e la lunghezza totale di questi cammini? I
(per i nodi a livello 1) ho k cammini di lunghezza 1;
I
(per i nodi a livello 2) ho k 2 cammini di lunghezza 2;
I
(per i nodi a livello i) ho k i cammini di lunghezza i;
k + 2k 2 + · · · + (h − 1)k h−1 =
h−1 X i=0
ik i = k
(h − 1)k h − hk h−1 + 1 (k − 1)2
Tipico problemino
Problema della ricerca su vettore ordinato. “Dato un vettore ordinato a di valori e un valore v , capire se v `e presente in a e, se se lo `e, a quale posizione.” Vediamo due possibili algoritmi per questo problema. Algoritmo 1: Ricerca lineare (o esaustiva) Algoritmo 2: Ricerca binaria
Ricerca lineare Implementazione: int linearSearch( int a[], int length, int v ) { int i; for (i=0; i= l ) { m = ( l + r ) / 2; if ( v == a[m] ) return m; if ( v < a[m] ) r = m - 1; else l = m + 1; } return -1; } Complessit`a? Ogni test esclude met`a dell’array: T (n) ≤ T (bn/2c) + 1 (considero solo i confronti). T (n) ≤ log2 n + 1 E il caso medio?