Algoritmi e Strutture Dati Capitolo 10 Tecniche algoritmiche Algoritmi e strutture dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Sommario • Una tecnica algoritmica può essere vista come un meta-algoritmo che permette di risolvere un’ampia classe di problemi, i quali, per loro natura, si prestano ad essere risolti appunto in modo analogo • In particolare, abbiamo già visto dettagliatamente la tecnica divide et impera • Oggi vedremo la Programmazione Dinamica e il Metodo Goloso, che troveranno ampia applicazione nei problemi su grafi 2 Copyright © 2004 - The McGraw - Hill Companies, srl Algoritmi e strutture dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Richiamo: la tecnica divide et impera Tecnica top-down: 1. Dividi l’istanza del problema in due o più sottoistanze (sottoproblemi) 2. Risolvi ricorsivamente il problema sulle sottoistanze 3. Ricombina la soluzione dei sottoproblemi allo scopo di ottenere la soluzione globale (si noti quindi che la soluzione del sottoproblema non può essere usata direttamente per produrre la soluzione del problema, ma deve essere combinata opportunamente con altre sottosoluzioni) Esempi: mergesort, quicksort 3 Copyright © 2004 - The McGraw - Hill Companies, srl Algoritmi e strutture dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Programmazione dinamica Tecnica bottom-up: 1. Identifica dei sottoproblemi del problema originario, procedendo logicamente dai problemi più piccoli verso quelli più grandi 2. Utilizza una tabella per memorizzare le soluzioni dei sottoproblemi incontrati: quando si incontra lo stesso sottoproblema, sarà sufficiente esaminare la tabella 3. Si usa quando i sottoproblemi non sono indipendenti, ovvero quando vale il principo di sottostruttura ottima della soluzione: una sottosoluzione può essere usata direttamente per costruire la soluzione del problema! 4 Copyright © 2004 - The McGraw - Hill Companies, srl Algoritmi e strutture dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Un esempio banale: Fibonacci3 algoritmo fibonacci3(intero n) intero sia Fib un array di n interi Fib[1] Fib[2] 1 for i = 3 to n do Fib[i] Fib[i-1] + Fib[i-2] return Fib[n] 5 Copyright © 2004 - The McGraw - Hill Companies, srl Algoritmi e strutture dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Distanza tra stringhe Siano X e Y due stringhe di lunghezza m ed n: Vogliamo calcolare la distanza tra X e Y, ovvero il minimo numero delle seguenti operazioni elementari da eseguire su X che permetta di trasformare X in Y 6 Copyright © 2004 - The McGraw - Hill Companies, srl Algoritmi e strutture dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Esempio • Parto dalla stringa X=“RISOTTO”, e voglio trasformarla in Y=“PRESTO” • La soluzione banale consiste nel fare 7 cancellazioni e 6 inserimenti, cioè 13 operazioni • Un’altra soluzione semplice: sostituisco uno dopo l’altro i caratteri di RISOTTO, ove necessario, e cancello i caratteri in eccesso; nel nostro esempio, saranno 5 sostituzione e 1 cancellazione (cioè, 6 operazioni). • Posso fare meglio? Vediamo una sequenza di operazioni controintuitiva: Abbiamo fatto solo 4 operazioni! Posso fare meglio?? 7 Copyright © 2004 - The McGraw - Hill Companies, srl Algoritmi e strutture dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Approccio • Denotiamo con d(X,Y) la distanza tra X e Y • Definiamo Xi il prefisso di X fino all’i-esimo carattere, per 0≤ i ≤m (X0 denota la stringa vuota): • Risolveremo il problema di calcolare d(X,Y) calcolando d(Xi,Yj) per ogni i, j tali che 0 ≤i ≤m e 0 ≤j ≤n Manterremo le informazioni in una tabella D di dimensione (m+1) (n+1) • 8 Copyright © 2004 - The McGraw - Hill Companies, srl Algoritmi e strutture dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Inizializzazione della tabella • Alcuni sottoproblemi sono molto semplici: d(X0,Yj)=j per trasformare la stringa vuota X0 nella stringa Yj, basta inserire uno ad uno gli j caratteri di Yj • d(Xi,Y0)=i per trasformare la stringa Xi nella stringa vuota Y0, basta rimuovere uno ad uno gli i caratteri di Xi • Queste soluzioni sono memorizzate rispettivamente nella prima riga e nella prima colonna della tabella D • 9 Copyright © 2004 - The McGraw - Hill Companies, srl Algoritmi e strutture dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Esempio La tabella D inizializzata dall’algoritmo 10 Copyright © 2004 - The McGraw - Hill Companies, srl Algoritmi e strutture dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Avanzamento nella tabella (1/3) • Se xi=yj, il minimo costo per trasformare Xi in Yj è uguale al minimo costo per trasformare Xi-1 in Yj-1 (infatti, non dovrò fare alcuna operazione su xi, visto che esso coincide con yj) D [ i,j] = D [ i-1, j-1 ] • Se xiyj, allora per trasformare Xi in Yj devo fare una qualche operazione sull’i-esima posizione di Xi; distinguiamo quindi in base all’ultima operazione che dovremmo usare per trasformare Xi in Yj in una sequenza ottima di operazioni 11 Copyright © 2004 - The McGraw - Hill Companies, srl Algoritmi e strutture dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Avanzamento nella tabella (2/3) (in realtà questa operazione viene fatta sulla posizione (i+1)-esima di Xi) il minimo costo per trasformare Xi in Yj è uguale al minimo costo per trasformare Xi in Yj-1 più 1 per inserire il carattere yj D [i,j] = 1 + D [i, j-1] il minimo costo per trasformare Xi in Yj è uguale al minimo costo per trasformare Xi-1 in Yj più 1 per la cancellazione del carattere xi D [i,j] = 1 + D [i-1, j] 12 Copyright © 2004 - The McGraw - Hill Companies, srl Algoritmi e strutture dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Avanzamento nella tabella (3/3) il minimo costo per trasformare Xi in Yj è uguale al minimo costo per trasformare Xi-1 in Yj-1 più 1 per sostituire il carattere xi con yj D [i,j] = 1 + D [i-1, j-1] In conclusione: 13 Copyright © 2004 - The McGraw - Hill Companies, srl Algoritmi e strutture dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Pseudocodice Tempo di esecuzione ed occupazione di memoria: Θ(m n) NOTA: Se tengo traccia della casella della tabella che utilizzo per decidere il valore di D[i,j], ho un metodo costruttivo per ricostruire la sequenza di operazioni di editing ottima. 14 Copyright © 2004 - The McGraw - Hill Companies, srl Algoritmi e strutture dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Esempio La tabella D costruita dall’algoritmo. In grassetto è indicata la sequenza di operazioni che permette di ottenere la distanza tra le stringhe (verificate che coincide con quella data prima!) mantenimento 15 sostituzione inserimento cancellazione Copyright © 2004 - The McGraw - Hill Companies, srl Algoritmi e strutture dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano La tecnica golosa (o greedy) • La tecnica golosa (greedy) si applica principalmente a problemi di ottimizzazione in cui, dato un certo numero di elementi come input, bisogna scegliere un sottoinsieme di essi che ottimizzi una funzione obiettivo rispettando un certo numero di vincoli. • Richiede che l’algoritmo esegua il processo di costruzione della soluzione in fasi successive, in cui ad ogni fase viene aggiunto un elemento alla soluzione. • Anche in questo caso la tecnica può funzionare solo se il problema gode della proprietà di sottostruttura ottima (sebbene questa sia una condizione necessaria, ma non sufficiente). 16 Copyright © 2004 - The McGraw - Hill Companies, srl Algoritmi e strutture dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Tecnica golosa (2) • Un algoritmo greedy ordina dapprima gli oggetti in base ad un criterio di appetibilità (da cui il termine goloso), e poi ripete le seguenti operazioni: 1. Ad ogni fase i, la soluzione viene accresciuta selezionando l’ i-esima componente, la quale, tra tutte quelle ammissibili, risulta la migliore in questo momento rispetto al criterio di appetibilità 2. Una volta fatta la scelta per la i-esima componente, si aggiornano (eventualmente) le appetibilità degli elementi rimanenti, e si passa a considerare le scelte successive, senza più tornare sulla decisione presa. • Questa strategia euristica non garantisce sempre la determinazione di una soluzione ottima, come detto prima e come vedremo con un esempio. 17 Copyright © 2004 - The McGraw - Hill Companies, srl Algoritmi e strutture dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Paradigma di tecnica golosa 18 Copyright © 2004 - The McGraw - Hill Companies, srl Algoritmi e strutture dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Un problema di sequenziamento • Un server (e.g., una CPU, o un impiegato dell’ufficio postale) deve servire n clienti, stabilendo un ordine sapendo che il servizio richiesto dal cliente i richiede ti secondi • Chiamiamo T(i) il tempo di attesa del cliente i (pari al suo tempo di processamento sommato ai tempi di processamento dei clienti che lo hanno preceduto) e sia il tempo di attesa totale di tutti i clienti • Vogliamo stabilire un ordine che minimizzi il tempo di attesa medio: Devo minimizzare il tempo di attesa totale! 19 Copyright © 2004 - The McGraw - Hill Companies, srl Algoritmi e strutture dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Esempio Dei sei possibili ordinamenti, il migliore è evidenziato in arancione 20 Copyright © 2004 - The McGraw - Hill Companies, srl Algoritmi e strutture dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Un algoritmo goloso ottimo • Il seguente algoritmo genera l’ordine di servizio in maniera incrementale secondo una strategia greedy: – Il primo cliente ad essere servito è quello con tempo di processamento minimo. – Al generico passo l’algoritmo serve la richiesta più corta tra quelle rimanenti. • La sequenza generata è ottima: infatti, è facile osservare che il tempo di attesa totale può essere riscritto come segue: T=n·t1+(n-1)·t2+(n-2)·t3+…+1·tn e quindi è minimo quando t1≤t2 ≤ … ≤ tn Tempo di esecuzione: Θ(n log n) (ordinamento ottimo) • 21 Copyright © 2004 - The McGraw - Hill Companies, srl Algoritmi e strutture dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Un algoritmo goloso non ottimo • Gli algoritmi greedy non sempre funzionano! • Esempio: Input: lista di interi che rappresentano il valore di monete disponibili e un intero che rappresenta un resto. Output: una collezione di cardinalità minima di monete la cui somma sia uguale al resto. Appetibilità: Taglio della moneta Ma supponendo di dover formare un resto di 9, avendo a disposizione monete da 1, 4 e 6. l’algoritmo darebbe 6 + 1 + 1 + 1 mentre la soluzione ottima è 4 + 4 + 1! Domanda: Nella pezzatura dell’Euro, le monete sono da 1,2,5,10,20,50 centesimi, e da 1,2 Euro. La tecnica greedy funziona in questo caso? Se sì, perché? 22 Copyright © 2004 - The McGraw - Hill Companies, srl Algoritmi e strutture dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Applicazioni future su grafi – Programmazione dinamica: cammini minimi tra tutte le coppie di nodi (algoritmo di Floyd e Warshall) – Tecnica greedy: minimo albero ricoprente (algoritmi di Prim, Kruskal, Boruvka) e cammini minimi a sorgente singola (algoritmo di Dijkstra) 23 Copyright © 2004 - The McGraw - Hill Companies, srl