Algoritmi greedy • Gli algoritmi che risolvono problemi di ottimizzazione devono in genere operare una sequenza di scelte per arrivare alla soluzione • Gli algoritmi greedy sono algoritmi basati sull’idea di fare sempre scelte che sembrano ottime al momento della scelta (cioè localmente). • IMPORTANTE: Non sempre è garanta la correttezza, ma sono spesso molto semplici ed efficienti. Problema della selezione di attività Problema: Siano date N attività in competizione tra loro per l’utilizzo di una certa risorsa. Trovare l’allocazione ottimale della risorsa, cioè il sottoinsieme di cardinalità massima delle attività che la possano condividere senza creare conflitti • S={1,2,…,N} un insieme di attività • Ad ogni attività i S sono associati • si = tempo di inizio (attivazione) • fi = tempo di fine (conclusione) • tali che si fi Problema della selezione di attività Definizione: Due attività i e j si dicono compatibili se gli intervalli [si , fi] e [sj , fj] sono disgiunti, cioè non si sovrappongono. In altre parole se vale fj si oppure fi sj Problema (riformulato): Dato l’insieme di attività S={1,2,…,N}, trovare il massimo sottoinsieme di attività tra loro compatibili. Problema della selezione di attività Problema (riformulato): Dato l’insieme di attività S={1,2,…,N}, trovare il massimo sottoinsieme di attività tra loro compatibili. ❶ Partiamo con un insieme A di attività inizialmente vuoto ❷ Inseriamo in A l’attività j col minimo tempo di terminazione ❸ Fra le attività rimanenti compatibili con j selezionare l’attività i col minore tempo di terminazione aggiungere l’attività selezionata i ad A ❹ Se esistono altre attività compatibili con i, torniamo al passo ❸, altrimenti terminiamo Problema della selezione di attività ❶ Assumiamo le attività siano ordinate in modo crescente rispetto al tempo di fine. f1 f2 … fN ❷ Siano I=[s1,…, sN] e F=[f1,…, fN] i vettori contenenti i tempi di inizio (non ordinati) e di fine (ordinati) ❸ Greedy-Activity-Selection(I,F) A = {1}; j = 1 for i = 2 to length[I] do if I[i] F[j] then A = A ∪ {i} j = i ❹ Al termine l’inseme A contiene la soluzione greedy al problema. Tempo: (n), (n log n) se dobbiamo prima ordinare le attività 6:5-9 7:6-10 i s i fi 1 1 4 1:1-4 2:3-5 2 3 5 3:0-6 4:5-7 3 0 6 Criterio: attività più lunga 8 8 11 0 10 8 6 12 Criterio: attività che finisce prima 9 8 12 10 12 14 3 attività 9 3 6 5 9 7 6 10 10:12-14 5:3-8 4 5 7 5 3 8 8:8-11 9:8-12 1 1 4 attività 10 8 4 4 5 14 7 8 11 12 14 6:5-9 7:6-10 i s i fi 1 1 4 1:1-4 2:3-5 2 3 5 3:0-6 4:5-7 3 0 6 8:8-11 9:8-12 10:12-14 5:3-8 4 5 7 5 3 8 1 6 5 9 2 7 6 10 3 8 8 11 4 9 8 12 5 10 12 14 6 1 2 3 4 5 6 7 8 9 10 11 12 13 14 6:5-9 7:6-10 i s i fi 1 1 4 1:1-4 2:3-5 2 3 5 3:0-6 4:5-7 3 0 6 8:8-11 9:8-12 10:12-14 5:3-8 4 5 7 5 3 8 1 6 5 9 4 7 6 10 7 8 8 11 8 9 8 12 9 10 12 14 1 2 3 4 5 6 7 8 10 9 10 11 12 13 14 6:5-9 7:6-10 i s i fi 1 1 4 1:1-4 2:3-5 2 3 5 3:0-6 4:5-7 3 0 6 8:8-11 9:8-12 10:12-14 5:3-8 4 5 7 1 5 3 8 4 6 5 9 7 6 10 8 8 8 11 10 9 8 12 10 12 14 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Selezione di attività: correttezza Teorema: L’algoritmo Greedy-Activity-Selection produce soluzioni di dimensione massima per il problema della selezione delle attività. Dimostrazione: Per assunzione dell’algoritmo, le N attività sono ordinate per tempo di fine, quindi la prima attività è quella che termina per prima. Dimostriamo innanzitutto che esiste una soluzione ottima che inizia con la prima attività (proprietà della scelta greedy) – Sia A S sia una qualsiasi soluzione ottima. Ordiniamola per tempi di fine crescenti. Supponiamo che A[i1] = S[k] e k1 (cioè la prima attività di A non è quella con tempo di terminazione minore in S) – Quindi vale f1 < fk. Selezione di attività: correttezza Teorema: L’algoritmo Greedy-Activity-Selection produce soluzioni di dimensione massima per il problema della selezione delle attività. Dimostrazione: – A S sia una qualsiasi soluzione ottima (ordinata per tempi di fine) con A[i1] = S[k] e k1. Quindi vale f1 < fk. – Possiamo quindi costruire un’altra soluzione B = A - {k} + {1}; B è una soluzione perché vale f1 < fk e quindi nessun vincolo di compatibilità è violato. B è anche ottima perché ha lo stesso numero di attività della soluzione ottima A. – B è quindi una sol. ottima che inizia con la scelta greedy. Selezione di attività: correttezza Teorema: L’algoritmo Greedy-Activity-Selection produce soluzioni di dimensione massima per il problema della selezione delle attività. Dimostrazione: Quindi esiste una soluzione ottima che inizia con la prima attività (passo base proprietà della scelta greedy). – Dopo la scelta greedy, il problema si riduce al sottoproblema di trovare la massima sequenza di attività compatibili con la prima. – Il sottoproblema è quindi di cercare di una sequenza massima di attività tra S’ = {i ∈ S : si f1} (if nel codice) – Se A è una soluzione ottima, una soluzione ottima del sottoproblema S’ è allora A’ = A - {1}. Dimostriamolo! Selezione di attività: correttezza Teorema: L’algoritmo Greedy-Activity-Selection produce soluzioni di dimensione massima per il problema della selezione delle attività. Se A è una soluzione ottima, la soluzione ottima del sottoproblema S’ è A’ = A – {1}. Se A’ non fosse ottima, esisterebbe un B’ di cardinalità maggiore di A’ per lo stesso sottoproblema S’ = {i ∈ S : si f1}. Ma aggiungendo l’attività 1 a B’, otterremo una soluzione B per S migliore di A (contraddizione). Selezione di attività: correttezza Teorema: L’algoritmo Greedy-Activity-Selection produce soluzioni di dimensione massima per il problema della selezione delle attività. Abbiamo provato che esiste una soluzione ottima che inizia con la prima attività e Dopo la prima scelta greedy, rimaniamo col sottoproblema (analogo) di cercare di una sequenza massima di attività in un insieme S’ = {i ∈ S : si f1}. Per induzione si può allora dimostrare facilmente che data una qualsiasi soluzione ottima, essa contiene al suo interno le soluzioni ottime dei suoi sottoproblemi (proprietà della sottostruttura ottima). Quindi la sequenza di scelte greedy determina una soluzione ottima (correttezza dell'algoritmo). Algoritmi Greedy : problema del cambio di denaro Università degli Studi di Milano • Input Marco Frasca • Un numero intero positivo n • Output • Il più piccolo numero intero di banconote per cambiare n dollari usando pezzi da 20, 10, 5, 1 dollaro. • Esempi Criterio di scelta greedy • n = 58, 7 banconote: 20+20+10+5+1+1+1 • n = 18, 5 banconote: 10+5+1+1+1 • Algoritmo • Dispensa una banconota alla volta • Ad ogni passo, utilizza la banconota più grande che non superi la cifra rimanente. Punti per garantire l’ottimalità Università degli Studi di Milano ● Proprietà della scelta greedy Marco Frasca • Una soluzione globalmente ottima può esser ottenuta effettuando, in sequenza, delle scelte localmente ottime (greedy). • Proprietà della sottostruttura ottima • Una soluzione ottima al problema contiene le soluzioni ottime dei sottoproblemi Problema del cambio di denaro Università degli Studi di Milano Marco Frasca Teorema: Il problema del cambio di denaro precedente soddisfa sia la proprietà della sottostruttura ottima che la proprietà della scelta greedy. Dimostrazione (cenni): Se b1,…,bk è una soluzione ottima al problema di cambiare n dollari, b2,…,bk è una soluzione ottima per il problema di cambiare n – b1v1 dollari (la banconota b1 vale v1 dollari). Se non lo fosse …. La seconda parte (scelta greedy) si basa sul fatto che non è possibile che in una soluzione ottima non compaia la scelta greedy. Problema del cambio di denaro Università degli Studi di Milano Marco Frasca Dimostrazione (cenni): Assumiamo b1,…,bk sia una soluzione ottima, che la banconota h sia la più grande non superiore all’importo n e che h non compaia nella soluzione. ● ● Analizzando i casi possibili, si nota che se h non supera n, esisterà sempre nella soluzione un insieme di almeno due biglietti di taglia inferiore ad n la cui somma sia proprio h (tutti i tagli sono infatti divisibili per qualsiasi dei tagli minori). Quindi sostituendo nella soluzione il biglietto di taglia maggiore si otterrebbe una soluzione migliore di b1,…,bk, il che è una contraddizione. Esercizio Scrivere una programma MATLAB che legge dal file processi.txt una lista di processi individuati da due valori: tempo di inzio e tempo di fine. Es. 0 5 1 4 5 15 2 7 ● I processi hanno bisogno della CPU per poter essere eseguiti, e la CPU può essere assegnata ad un solo processo per volta. Il programma deve restituire la lista dei processi (es. vettore di indici) a cui può essere assegnata la CPU, massimizzando il numero di processi eseguiti. Programmazione Dinamica (PD, con 'la' non 'il' !!) Università degli Studi di Milano Marco Frasca Altra tecnica per risolvere problemi di ottimizzazione, più generale degli algoritmi greedy La programmazione dinamica risolve un problema di ottimizzazione componendo le soluzioni dei sottoproblemi Caratteristiche della programmazione dinamica Università degli Studi di Milano Marco Frasca • Risolve i problemi in modo bottom-up: si parte da problemi piccoli e se ne compongono le soluzioni per trovare soluzioni di problemi di dimensioni più grandi. • Si applica nei casi in cui un problema ha la proprietà della sottostruttura ottima. Differenza con i metodi divide et impera Università degli Studi di Milano Marco Frasca • I metodi divide et impera (esempio: ordinamento con mergesort) procedono top-down: la scomposizione produce problemi che vengono risolti in modo indipendente. • Nei problemi a cui si applica la PD, la scomposizione produce sottoproblemi che non sono indipendenti: la soluzione di alcuni sottoproblemi richiede di risolvere i medesimi sottoproblemi. PD versus divide et impera (segue) Università degli Studi di Milano Marco Frasca • I metodi divide et impera si applicano quando la scomposizione in sottoproblemi produce problemi tutti distinti. • La PD si applica quando la scomposizione in sottoproblemi produce piu’ volte gli stessi sottoproblemi. • Per evitare di risolvere piu’ volte gli stessi sottoproblemi, si memorizzano le soluzioni dei sottoproblemi in una tabella. PD versus metodi greedy Università degli Studi di Milano Marco Frasca • Programmazione dinamica e metodi greedy si applicano entrambi a problemi di ottimizzazione in cui vale la proprieta’ della sottostruttura ottima. • Nei metodi greedy, le scelte ad ogni passo dipendono da un criterio esterno (appetibilità), ogni scelta determina un sottoproblema • Nella PD le scelte dipendono dalla soluzione dei sottoproblemi PD versus metodi greedy (segue) Università degli Studi di Milano Marco Frasca • I metodi greedy agiscono in modo top-down: riducono progressivamente un problema a sottoproblemi di dimensioni decrescenti. • I metodi basati sulla PD procedono bottom-up risolvendo per primi i problemi più piccoli. • I metodi greedy sono molto piu’ efficienti di quelli basati sulla programmazione dinamica che devono provare tutte le alternative per fare una scelta ottima. • La PD ha un’applicabilità maggiore rispetto ai metodi greedy. Sviluppo di un algoritmo di programmazione dinamica Università degli Studi di Milano Marco Frasca 1. Caratterizzazione della struttura di una soluzione ottima 2. Definizione ricorsiva del valore di una soluzione ottima. 3. Calcolo iterativo del valore di una soluzione ottima mediante una strategia bottom-up 4. Costruzione di una soluzione ottima a partire dal valore calcolato Esempio: Problema dello zaino 0/1 Università degli Studi di Milano Marco Frasca La programmazione dinamica permette di risolvere il problema dello zaino 0/1, che non ammette una soluzione con i metodi greedy - o1, o2, ...,on oggetti ● - w1, w2, ..., wn pesi - v1, v2, ...,vn valori - Capacità zaino W. Obiettivo: massimizzare il valore trasportato rispettando la capacità. Caratterizzazione della struttura di una soluzione ottima Università degli Studi di Milano Marco Frasca Definiamo: M[i, j] = il massimo valore trasportabile con un peso j e potendo selezionare gli oggetti 1, …, i Caratterizzazione della struttura di una soluzione ottima Università degli Studi di Milano Marco Frasca Considera una soluzione ottima in cui si hanno a disposizione gli articoli 1,…, i e un carico j • se i non è incluso nella soluzione ottima M[i, j] = M[i-1, j] • se i è incluso nella soluzione ottima M[i, j] = M[i-1, j - wi] + vi, e deve valere j wi Condizioni limite – M[i, 0] = 0 – M[0, j] = 0 per ogni i per ogni j Università degli Studi di Milano Marco Frasca Posso calcolare in modo iterativo (bottom-up) i valori di M[i, j] a partire da M[0, 0] per valori crescenti di i e j. ● Se ho n articoli e un peso W il valore di una selezione ottima sarà dato alla fine da M[n, W] ● Definizione ricorsiva del valore di una soluzione Università degli Studi di Milano Marco Frasca M[0, j] = 0 M[i, 0] = 0 M[i, j] = M[i-1, j] se j < wi M[i, j] = max {M[i-1, j], vi + M[i-1, j - wi ]} se j wi Calcolo iterativo del valore di una soluzione ottima DynamicKnapsack01(W, w, v) Università degli Studi di Milano Marco Frasca for j = 0 to W % inizializzazione M [0,j] 0 for i = 0 to n % inizializzazione M [i,0] 0 for i = 1 to n for j = 1 to W if (j wi) then M[i, j] max {M[i-1, j], vi + M[i - 1, j - wi]} else M[i, j] M [i-1, j]; Costruzione di una soluzione ottima a partire dal valore calcolato Università degli Studi di Milano Marco Frasca L'algoritmo ritorna la matrice M contenente i valori delle soluzioni ottime per vari sottoproblemi. ● Partendo dalla cella M[n, W], risalire nella colonna M[:, W] per capire come costruire la soluzione. ● Esempio: ● O1 v1 = 10, w1 = 5 0 1 2 3 4 5 6 0 8 0 9 0 10 0 0 O2 7 3 O0 0 0 0 0 0 O3 8 4 O1 0 0 0 0 0 10 10 10 10 10 10 O4 5 2 O2 0 0 0 7 7 10 10 10 17 17 17 O3 0 0 0 7 8 10 10 15 17 18 18 O4 0 0 5 7 8 12 13 15 17 20 22 W = 10 0 7 Costruzione di una soluzione ottima a partire dal valore calcolato Università degli Studi di Milano Marco Frasca Se M[n,W] = M[n-1,W], l'oggetto n non è nella soluzione e passiamo alla cella M[n-1,W] ● Se invece c'è, cioè M[n,W] ≠ M[n-1,W], passiamo alla cella M[n-1, W-wn] ● ● Iteriamo.... O1 v1 = 10, w1 = 5 0 1 2 3 4 5 6 0 8 0 9 0 10 0 0 O2 7 3 O0 0 0 0 0 0 O3 8 4 O1 0 0 0 0 0 10 10 10 10 10 10 O4 5 2 O2 0 0 0 7 7 10 10 10 17 17 17 O3 0 0 0 7 8 10 10 15 17 18 18 O4 0 0 5 7 8 12 13 15 17 20 22 W = 10 0 7 Complessità dell’algoritmo La complessità dell’algoritmo di programmazione dinamica per lo zaino 0/1 è O(nW) dove n è il numero degli articoli e W è il peso dello zaino. Notare che W può essere arbitrariamente grande rispetto a n (ad esempio può essere W = 2n) Esercizio Risolvere in maniera efficiente la variante del problema dello zaino 0/1, detta zaino frazionario, in cui degli oggetti si puó prendere anche una porzione x ∈ [0,1], dove 0 indica “niente”, 1 indica “tutto” ● NOTA: nello zaino 0/1 la porzione x ∈ {0,1}