Laboratorio di Algoritmi e Strutture Dati

17 downloads 278 Views 371KB Size Report
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?