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 k1
(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 k1. 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}