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