Sommario della lezione
Ulteriori esempi di applicazione della Programmazione
Dinamica
Esempio di applicazione n. 1: Cambio di monete
Esempio di applicazione n. 2: Selezione di attività
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 1/36
Programmazione Dinamica: idee di base
1. Esprimi la soluzione al problema in questione in termini
di soluzioni a sottoproblemi di dimensione minore
2.
Calcola la soluzione ai distinti sottoproblemi una
volta soltanto, memorizza tali soluzioni in una tabella, in
modo tale che esse possano essere usate nel seguito, se
occorre.
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 2/36
Programmazione Dinamica
Lo sviluppo di algoritmi basati sulla Programmazione Dinamica
prevede generalmente due passi separati:
Formulare il problema in termini ricorsivi: ovvero scrivere
una espressione per la soluzione all’intero problema che sia
una combinazione di soluzioni a sottoproblemi di taglia minore
Calcolare la soluzione globale al problema in modo
ricorsivo, facendo precedere ciascuna chiamata ricorsiva con
un controllo per verificare se la soluzione al relativo
sottoproblema é stata giá calcolata.
Gli algoritmi di Programmazione Dinamica hanno bisogno di
memorizzare le soluzioni ai sottoproblemi intermedi. Spesso (ma
non sempre) ciò viene effettuato memorizzandole in tabelle.
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 3/36
Programmazione Dinamica
Il secondo passo di prima puó essere sostituito in algoritmi iterativi
con il seguente:
Calcolare le soluzioni ai sottoproblemi in maniera
“bottom-up”: scrivere un algoritmo che parta con i casi base
della ricorrenza e proceda via via considerando (e risolvendo)
problemi di taglia sempre maggiore, considerandoli nell’ordine
corretto
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 4/36
Esempio 1: Problema del cambio delle monete
Input: Un valore monetario V , ed un vettore di valori di monete
v[1 . . . n], con v[1] > v[2] > . . . > v[n] = 1
Output: Il minimo numero di monete il cui valore totale sia
esattamente pari a V . (Assumiamo di avere a disposizione un
numero illimitato di monete di valore v[i], per ogni i)
In altri termini, detto ai ≥ 0 il numero di monete di valore v[i] che
usiamo, vogliamo minimizzare il numero totale di monete usate,
pari a
a1 + a2 + . . . + an
sotto la condizione che
n
X
ai v[i] = V
i=1
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 5/36
Esempio
Sia V = 26 (Euro), ed il vettore dei valori delle monete dato da
v[1] = 10, v[2] = 5, v[3] = 2, v[4] = 1
Possibili soluzioni per esprimere 26 Euro con le monete a
disposizione:
1. a1 = a2 = a3 = 0, a4 = 26 ⇒ il numero totale di monete usato
P4
è a1 + a2 + a3 + a4 = 26, di valore totale i=1 ai v[i] = 26
2. a1 = 2, a2 = 0 a3 = 3, a4 = 0 ⇒ il numero totale di monete
usato è a1 + a2 + a3 + a4 = 5 di valore totale
P4
i=1 ai v[i] = 2 · 10 + 3 · 2 = 26
3. a1 = 2 a2 = 1 a3 = 0, a4 = 1
⇒ il numero totale di monete
usato è a1 + a2 + a3 + a4 = 4 di valore totale
P4
i=1 ai v[i] = 2 · 10 + 1 · 5 + 1 · 1 = 26
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 6/36
Passo 1 di PD: Formulare il problema ricorsivamente:
Ovvero: scrivere una formula per la soluzione all’intero problema
che sia una combinazione di soluzioni a sottoproblemi di taglia
minore
Domanda: Quali sono i sottoproblemi del problema di partenza
(che chiede di esprimere il valore V usando il minor numero di
monete, ognuna delle quali di un possibile valore
v[1] > v[2] > . . . > v[n] = 1)?
Risposta: Sono tutti i sottoproblemi che si ottengono qualora si
voglia esprimere un qualsiasi valore j ≤ V usando il minor numero
di monete, ognuna delle quali di un possibile valore
v[i] > v[i + 1] > . . . > v[n] = 1, i ≥ 1
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 7/36
Esempio
Sia C(i, j) il minimo numero di monete necessario per esprimere la
somma j ≤ V , usando monete di valore v[i] > v[i + 1] > . . . > v[n], (noi
siamo interessati a C(1, V ))
Consideriamo l’esempio con V = 12, e monete di valore v[1] = 10,
v[2] = 6, v[3] = 1. L’indice di riga i specifica che sono disponibili le monete
di valore v[i], v[i + 1], . . . , v[n]. L’indice di colonna j specifica il valore
monetario totale che si deve esprimere.
j
i
0
1
2
3
4
5
6
7
8
9
10
11
12
1
0
1
2
3
4
5
1
2
3
4
1
2
2
2
0
1
2
3
4
5
1
2
3
4
5
6
2
3
0
1
2
3
4
5
6
7
8
9
10
11
12
Ad esempio, C(2, 8) = 3, dovendo necessariamente usare una moneta
di valore 6 e due monete di valore 1.
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 8/36
Risoluzione dei sottoproblemi C(i, j)
C(i, j) = minimo numero di monete per esprimere la somma j ≤ V ,
usando solo monete di valore v[i] > v[i + 1] > . . . > v[n].
Per risolvere il sottoproblema C(i, j), possiamo o usare oppure non
usare la moneta di valore v[i].
Se decidiamo di non usare la moneta di valore v[i], ne segue
che per ottenere il valore j occorre risolvere il sottoproblema
C(i + 1, j).
Essendo questo un sottoproblema di taglia ancora inferiore a quello
relativo a C(i, j) (in quanto meno monete vengono usate) possiamo
supporre ricorsivamente che esso sia stato risolto, e quindi in
questo caso C(i, j) = C(i + 1, j)
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 9/36
Risoluzione dei sottoproblemi C(i, j)
Se decidiamo invece di usare la moneta di valore v[i], allora ne
segue che per completare la soluzione e ottenere il valore j,
occorre ottenere preliminarmente il valore j − v[i], usando monete
di valore v[i], v[i + 1], . . . , v[n].
Se usassimo un qualche numero k monete per ottenere il valore
j − v[i], la nostra soluzione per ottenere il valore j userebbe k + 1
monete (poiché abbiamo giá usato una moneta di valore v[i]).
Per minimizzare 1 + k occorre scegliere k il piú piccolo possibile,
ovvero occorre usare il minimo numero di monete per risolvere il
sottoproblema di ottenere il valore j − v[i] usando monete di valore
v[i], v[i + 1], . . . , v[n].
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 10/36
quindi....
Occorre determinare minimo numero di monete per risolvere il
sottoproblema di ottenere il valore j − v[i] usando monete di valore
v[i], v[i + 1], . . . , v[n]
Anche questo é un sottoproblema del problema relativo a C(i, j), in
quanto il valore da esprimere, (cioé j − v[i]) é minore del valore j.
Possiamo quindi supporre ricorsivamente che esso sia stato risolto,
ed in questo caso (cioé che una moneta di valore v[i] venga scelta)
varrebbe C(i, j) = 1 + C(i, j − v[i])
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 11/36
Cosa vale quindi?
Se decidiamo di non usare la moneta di valore v[i], vale che
C(i, j) = C(i + 1, j)
Se decidiamo di usare la moneta di valore v[i], vale che
C(i, j) = 1 + C(i, j − v[i])
Quindi, in generale, C(i, j) sará pari alla migliore di queste due
alternative, ovvero:
C(i, j) =

C(i + 1, j)
se v[i] > j,
min{C(i + 1, j), 1 + C(i, j − v[i])}
se v[i] ≤ j
con i casi base della ricorrenza pari a:
C(n, j) = j,
∀j = 0, . . . , V
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 12/36
Algoritmo ricorsivo per il calcolo di C(1, V )
C(i, j) =

C(i + 1, j)
se v[i] > j,
min{C(i + 1, j), 1 + C(i, j − v[i])} se v[i] ≤ j
con i casi base:
C(n, j) = j, ∀j = 0, . . . , V
R EC _C AMBIO M ONETE(v[i...n], j) % fá uso di una tabella T (i, j)
if i = n then return (j)
else if T (i, j) non é definito
T (i, j) ← min(R EC _C AMBIO M ONETE(v[i + 1...n], j),
1 + R EC _C AMBIO M ONETE(v[i...n], j − v[i]))
return (T (i, j))
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 13/36
Algoritmo iterativo per il calcolo di C(1, V )
C(i, j) =

C(i + 1, j)
se v[i] > j,
min{C(i + 1, j), 1 + C(i, j − v[i])} se v[i] ≤ j
con i casi base:
C(n, j) = j, ∀j = 0, . . . , V
C AMBIO M ONETE(v[1...n], V )
for j ← 0 to V do
C(n, j) ← j
for i ← n − 1 downto 1 do
for j ← 0 to V do
if (v[i] > j OR C(i + 1, j) < 1 + C(i, j − v[i])) do
C(i, j) ← C(i + 1, j)
else C(i, j) ← C(i, j − v[i]) + 1
return (C(1, V ))
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 14/36
Analisi di C AMBIO M ONETE(v[1...n], V )
C AMBIO M ONETE(v[1...n], V )
1. for j ← 0 to V do
2.
C(n, j) ← j
3. for i ← n − 1 downto 1 do
4.
for j ← 0 to V do
5.
if (v[i] > j OR C(i + 1, j) < 1 + C(i, j − v[i])) do
6
C(i, j) ← C(i + 1, j)
7.
else C(i, j) ← C(i, j − v[i]) + 1
8. return (C(1, V ))
Analisi: Il for delle linee 1. e 2. prende tempo Θ(V ). Le istruzione nelle
linee 5., 6., e 7. prendono tempo Θ(1). Il for delle linee 4., 5., 6., e 7.
prende tempo Θ(V ). Il for delle linee da 3. a 7. prende tempo Θ(nV ).
Pertanto, l’algoritmo C AMBIO M ONETE(v[1...n], V ) prende in totale
tempo Θ(nV )
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 15/36
Esempio
Sull’ esempio con V = 12, e monete di valore v[1] = 10, v[2] = 6, v[3] = 1
l’algoritmo costruirebbe la matrice seguente, e produrrebbe in output il
valore C(1, 12) = 2
j
i
0
1
2
3
4
5
6
7
8
9
10
11
12
1
0
1
2
3
4
5
1
2
3
4
1
2
2
2
0
1
2
3
4
5
1
2
3
4
5
6
2
3
0 1 2 3 4 5 6 7 8 9 10 11 12
La matrice verrebbe costruita riga per riga, dal basso in alto, e da sinistra
a destra, secondo la regola:
j
j − v[i]
= min(
,
)
i
i+1
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 16/36
Esercizi
L’algoritmo di Programmazione Dinamica per il problema del
cambio delle monete usa un array di dimensione n × (V + 1). É
possibile modificare l’algoritmo in modo tale che esso usi solo
O(n) locazioni di memoria? Giustificare la risposta.
L’algoritmo di Programmazione Dinamica per il problema del
cambio delle monete restituisce in output il minimo numero di
monete per esprimere il valore V , usando solo monete di dati
valori v[i], i = 1, . . . n. Progettare un algoritmo che restituisca
una tabella U (i, j), con 1 ≤ i ≤ n, e 0 ≤ j ≤ V , tale che
U (i, j) = True ⇐⇒ la moneta di valore v[i] viene usata per
esprimere il valore j con il minor numero di monete, sotto la
condizione che solo monete di valore v[i], . . . , v[n] vengono
impiegate.
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 17/36
Esercizi
L’algoritmo di Programmazione Dinamica per il problema del
cambio delle monete restituisce in output il minimo numero di
monete per esprimere il valore V , usando solo monete di dati
valori v[i], i = 1, . . . n. Progettare un algoritmo che restituisca in
output un insieme di cardinalitá minima di monete per
esprimere il valore V , usando solo monete di dati valori
v[i], i = 1, . . . n. Si usi la tabella U (i, j) calcolata nell’esercizio
precedente.
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 18/36
Problemi di Ottimizzazione
Il problema del Cambio di Monete é il primo esempio di Problema di
Ottmizzazione che abbiamo visto.
Informalmente, un Problema di Ottimizzazione é caratterizzato
dal fatto che ad ogni possibile istanza di input (ad es., il valore V ed
i valori v[i] delle monete nel problema precedente), é possibile
associare piú soluzioni (ad es., i diversi modi di esprimere il valore
V con le monete di valore v[i]).
A ciascuna soluzione di una istanza di input, é associato un
costo (ad es., il numero di monete per esprimere V ).
Ció che noi cerchiamo é una soluzione di minimo costo (o di
massimo costo, se esso rappresenta un “guadagno” per noi)
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 19/36
Esempio 2: Selezione di Attività
Input del problema: Supponiamo di avere un insieme
A = {A1 , A2 , . . . , An } di attivitá , dove ciascuna attivitá Ai ha un tempo di
inizio si , un tempo di fine fi , con si < fi (in altre parole, l’attivitá Ai deve
essere svolta nell’intervallo temporale [si , fi ]), ed un certo valore vi .
Le attivitá in A devono essere eseguite da un server, sotto la condizione
che Ai ed Aj possono essere entrambe eseguite se e solo se
[si , fi ] ∩ [sj , fj ] = ∅ (in tal caso, diremo che l’attivitá Ai ed Aj sono
compatibili). In altri termini, possono essere eseguite dal server solo
attivitá il cui svolgimento temporale non si sovrappone (si pensi, ad
esempio, alle attivitá come dei job che un sistema operativo deve far
eseguire da una CPU)
Output del problema: Calcolare un sottoinsieme di S ⊆ A di attivitá a
due a due compatibili, di valore totale massimo.
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 20/36
Esempio 2: Selezione di Attività
1. Attività i inizia al tempo si e termina al tempo fi
2. Attività compatibili se il loro intervallo di svolgimento non si sovrappone
3. Obiettivo: Sottoinsieme di attività compatibili di valore totale massimo
v=4
v=2
v=3
v=5
v=3
v=5
v=5
0
1
2
3
4
5
6
7
8
v=7
9
10
11
12 tempo
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 21/36
La prima e l’ultima attività sono compatibili, di valore totale 11
1. Attività i inizia al tempo si e termina al tempo fi
2. Attività compatibili se il loro intervallo di svolgimento non si sovrappone
3. Obiettivo: Sottoinsieme di attività compatibili di valore totale massimo
v=4
v=2
v=3
v=5
v=3
v=5
v=5
0
1
2
3
4
5
6
7
8
v=7
9
10
11
12 tempo
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 22/36
La seconda, terza e l’ultima attività sono compatibili, di valore totale 12
1. Attività i inizia al tempo si e termina al tempo fi
2. Attività compatibili se il loro intervallo di svolgimento non si sovrappone
3. Obiettivo: Sottoinsieme di attività compatibili di valore totale massimo
v=4
v=2
v=3
v=5
v=3
v=5
v=5
0
1
2
3
4
5
6
7
8
v=7
9
10
11
12 tempo
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 23/36
Rinomina le attività in ordine di terminazione: f1 ≤ f2 ≤ . . . ≤ fn
Definizione. Per ogni attività j, p(j) = più grande indice i < j tale che
attività i è compatibile con attività j (p(i) = 0 se non cè)
Es. p(8) = 5, p(7) = 3, p(2) = 0,
1
2
3
4
5
6
7
8
0
1
2
3
4
5
6
7
8
9
10
11
12 tempo
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 24/36
Sia O una soluzione ottima al problema in questione
Sicuramente, o vale che n (l’ultima attività) ∈ O, oppure vale che
n∈
/O
Se n ∈ O allora tutte le attività p(n) + 1, p(n) + 2, . . . , n − 1
intersecano n, quindi esse non possono essere in O
Se n ∈ O allora O − {n} è una soluzione ottima per le attivitá
{1, 2, . . . , p(n)} (che non intersecano n) (Perchè? Perchè se
O − {n} non lo fosse, allora si potrebbe trovare una soluzione
migliore che unita all’attività n sarebbe globalmente migliore di O
stessa!)
Se invece n ∈
/ O, allora O è una soluzione ottima per l’insieme
delle attività {1, 2, . . . , n − 1}
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 25/36
Passo 1 di PD: formulare il problema ricorsivamente
Ovvero: scrivere una formula per la soluzione all’intero problema
che sia una combinazione di soluzioni a sottoproblemi di taglia
minore
∀1 ≤ j ≤ n, sia Oj una soluzione ottima per il sottoproblema
costituito dalle attività {1, . . . , j}, e sia OPT(j) il valore di Oj (noi
cerchiamo OPT(n)).
Da quanto detto prima, o vale che j ∈ Oj (ed in tal caso Oj NON
può contenere le attività p(j) + 1, . . . , j − 1) inoltre Oj − {j} è una
soluzione ottima (ovvero di valore OPT(j)) per le attivitá
{1, 2, . . . , p(j)} ⇒ OPT(j) = vj + OPT(p(j))
oppure vale che j ∈
/ Oj (ed in tal caso OPT(j) = OPT(j − 1))
⇒ OPT(j) = max{vj + OPT(p(j)), OPT(j − 1)}
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 26/36
In sintesi
OPT(j) =

0
se j = 0,
max{vj + OPT(p(j)), OPT(j − 1)} se j > 1
Il che ci suggerisce il seguente algoritmo
Input: n, s1 , . . . , sn , f1 , . . . , fn , v1 , . . . , vn
Ordina le attività in modo che f1 ≤ . . . ≤ fn
Calcola p(1), . . . , p(n)
Calcola-OPT(j)
if (j = 0)
return(0)
else
return(max{vj + Calcola-OPT(p(j)), Calcola-OPT(j − 1)})
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 27/36
Purtroppo l’algoritmo Calcola-OPT(n) ha complessità esponenziale
1
OPT(5)
2
OPT(4)
3
OPT(3)
OPT(3)
OPT(2)
OPT(2)
OPT(1)
4
5
OPT(2)
p(1)=0, p(j)=j-2
OPT(1)
OPT(1) OPT(0) OPT(1) OPT(0)
OPT(1)
OPT(0)
# chiamate
cresce
(esponen# chiamate su parametro j =(# chiamate su parametro j-1)
zialmente)
+(# chiamate su parametro j-2)
come
i
⇑
OPT(j)=max{vj +OPT(p(j)), OPT(j-1)}= max{vj +OPT(j-2), OPT(j-1)} numeri di
Fibonacci
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 28/36
Perché l’algoritmo di D&I Calcola-OPT(n) é esponenziale?
Perché risolve lo stesso sottoproblema piú volte! Infatti, il numero
di sottoproblemi distinti che Calcola-OPT(n) deve in realtá
considerare é solo O(n) (tutte le distinte chiamate
Calcola-OPT(j)), mentre
Esempio: Albero delle chiamate ricorsive di Calcola-OPT(5)
1
OPT(5)
2
OPT(4)
3
OPT(3)
OPT(3)
OPT(2)
OPT(2)
OPT(1)
4
5
OPT(2)
p(1)=0, p(j)=j-2
OPT(1)
OPT(1) OPT(0) OPT(1) OPT(0)
OPT(1)
OPT(0)
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 29/36
É il momento di usare la Programmazione Dinamica
Ricordiamo che esistono due approcci per trasformare un inefficiente
algoritmo di Divide et Impera in un efficiente algoritmo.
La prima, basata sulla tecnica della Memoization, che aggiunge
all’algoritmo una tabella in cui vengano memorizzate le soluzioni ai
sottoproblemi giá risolti. Viene altresí addottata l’addizionale accortezza
che prima di ogni chiamata ricorsiva dell’algoritmo su di un particolare
sottoproblema, debba essere effettuato un controllo sulla tabella per
verificare se la soluzione a quel sottoproblema é stata giá calcolata in
precedenza.
La seconda tecnica risolve semplicemente tutti i sottoproblemi del
problema di partenza, in maniera iterativa ed in modo “bottom-up”, ovvero
risolvendo prima i sottoproblemi di taglia piccola e poi via via quelli di
taglia maggiore fino a risolvere l’intero problema di partenza
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 30/36
Quale approccio é migliore?
Dipende... Entrambi hanno i loro meriti.
L’approccio basato sulla memorizzazione preserva la struttura
ricorsiva tipica degli algoritmi basati su Divide et Impera (che sono
in generale semplici ed eleganti). Per contro, vi é un’aggiunta di
lavoro, tipo gestione stack, etc., che in certe situazioni puó
diventare significativo.
L’approccio iterativo “bottom-up” é in generale efficiente.
Tuttavia, gli algoritmi basati su questo approccio tendono a
calcolare la soluzione a tutti i sottoproblemi del problema originale,
anche quelli che potrebbero non concorrere alla soluzione ottima
del problema di partenza. Ció non accade per gli algoritmi basati
sul primo approccio, che risolvono solo i sottoproblemi
“strettamente necessari”.
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 31/36
Programmazione Dinamica: primo approccio
Memoization: Memorizza le soluzioni di ciascun sottoproblema,
leggili all’occorrenza.
Input: n, s1 , . . . , sn , f1 , . . . , fn , v1 , . . . , vn
Ordina le attività in modo che f1 ≤ . . . ≤ fn
Calcola p(1), . . . , p(n)
for j = 1 to n
M [j] ← vuoto
M [1] ← 0
M_Calcola_OPT(j)
if(M [j] è vuota)
M [j] ← max{vj + M_Calcola_OPT(p(j)), M_Calcola_OPT(j−1)}
return M [j]
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 32/36
Confrontiamo le ricorsioni nei due algoritmi
Calcola-OPT(j)
if (j = 0)
return(0)
else
return( max{vj + Calcola-OPT(p(j)), Calcola-OPT(j − 1)})
for j = 1 to n
M [j] ← vuoto
M [1] ← 0
M_Calcola_OPT(j)
if (M [j] è vuota)
M [j] ← max{vj + M_Calcola_OPT(p(j)), M_Calcola_OPT(j−1)}
return M [j]
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 33/36
E qual è la complessità dell’algoritmo che usa la memoization?
Input: n, s1 , . . . , sn , f1 , . . . , fn , v1 , . . . , vn
1.Ordina le attività in modo che f1 ≤ . . . ≤ fn
2.Calcola p(1), . . . , p(n)
3.for j = 1 to n
M [j] ← vuoto
4.M [1] ← 0
5. M_Calcola_OPT(j)
if(M [j] è vuota)
M [j] ← max{vj + M_Calcola_OPT(p(j)), M_Calcola_OPT(j − 1)}
return M [j]
L’istruzione 1. richiede tempo (n log n) per ordinare. L’istruzione 2. richiede tempo O(n)
(una volta aver ordinato). Il for in 3. richiede tempo O(n). L’istruzione 4. richiede tempo
O(1).
M_Calcola_OPT(n) effettua chiamate al suo interno a M_Calcola_OPT(j), j < n. Ogni
chiamata richiede tempo O(1) e o ritorna un valore M [j] già calcolato, oppure calcola un
nuovo valore M [j] facendo due chiamate a valori già calcolati. Il numero totale di chiamate
sarà quindi al più pari a 2n ⇒ il tempo impiegato da M_Calcola_OPT(n) è O(n).
Sommando tutto otteniamo che l’algoritmo ha complessità O(n log n).
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 34/36
E se vogliamo trovare la soluzione ottima? (e non solo il suo valore)
Chiama M_Calcola_OPT(n)
Chiama Trova_Soluzione(n)
Trova_Soluzione(j)
if (j = 0)
output nulla
else if (vj + M [p(j)] > M [j − 1])
print j
Trova_Soluzione(p(j))
else
Trova_Soluzione(j)
Il numero di chiamate ricorsive è ≤ n ⇒ la complessità dell’algoritmo è
O(n)
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 35/36
PD: seconda versione (iterative e non ricorsiva)
Calcoliamo i valori M [j] nell’ordine M [0], M [1], . . . , M [n].
Input: n, s1 , . . . , sn , f1 , . . . , fn , v1 , . . . , vn
Ordina le attività in modo che f1 ≤ . . . ≤ fn
Calcola p(1), . . . , p(n)
Iterative_Compute_Opt
M [0] ← 0
for j = 1 to n
M [j] ← max{vj + M [p(j)], M [j − 1]}
Complessità = O(n)
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 36/36