Paola Vocca Lezione 4: Programmazione dinamica 1 Caratteristiche Programmazione dinamica: paradigma basato sullo stesso principio utilizzato per il divide et impera o il problema viene decomposto in sotto-problemi o a soluzione del problema è derivata da quelle di tali sottoproblemi Entrambi i paradigmi vengono applicati a partire da una definizione ricorsiva che correla la soluzione di un problema a quella di un insieme di sotto-problemi. 2 1
Differenze tra programmazione dinamica e divide et impera La programmazione dinamica è nata principalmente per risolvere problemi di ottimizzazione, il divide et impera è più mirato alla risoluzione di problemi non necessariamente di ottimizzazione nel divide et impera un qualunque sotto-problema viene considerato una sola volta, nella programmazione dinamica uno stesso sotto-problema può rientrare come componente nella definizione di più problemi 3 Differenze tra programmazione dinamica e divide et impera 4 2
Proprietà di decomposizione Risoluzione mediante programmazione dinamica di un problema caratterizzata dalle due proprietà della decomposizione: Ottimalità dei sotto-problemi: o La soluzione ottima di un problema deriva dalle soluzioni ottime dei sotto-problemi in cui esso è stato decomposto (come nel divide et impera). Sovrapposizione dei sotto-problemi: o Uno stesso sotto-problema può venire usato nella soluzione di due (o più) sotto-problemi diversi. 5 Definizione di un algoritmo di programmazione dinamica La definizione di un algoritmo di programmazione dinamica è basata su: 1. caratterizzazione della struttura generale di cui sono istanze sia il problema in questione che tutti i sotto-problemi 2. identificazione dei sotto-problemi elementari e individuazione della relativa modalità di determinazione della soluzione 3. definizione di una regola ricorsiva per la soluzione in termini di composizione delle soluzioni di un insieme di sotto-problemi 4. derivazione di un ordinamento di tutti i sotto-problemi, per il calcolo efficiente e la memorizzazione delle loro soluzioni in una tabella. (memorizzazione dei valori intermedi memoization- in una tabella di programmazione dinamica) Numero di passi dell'algoritmo limitato superiormente dal prodotto tra il numero di sotto-problemi e il costo di ricombinazione delle loro soluzioni ottime 6 3
Esempio: Numeri di Fibonacci Esempio: successione di Fibonacci (0, 1, 1, 2, 3, 5, 8, 13,...) o 0 = 0 o 1 = 1 o = 1 + 2 2 Ricorsione Fib( n ) { // n >= 0 if (n <= 1) RETURN n; else RETURN Fib(n-1) + Fib(n-2); } 7 Ricorsione inefficiente: analisi + =* + + = + + = ( + +)+= + ++= + ++ +=. + + > = = φ! ",. Fib(n) richiede O(F n+3 ) = O(φ n+3 ) passi, dove φ = (+ ")/,'()... è il rapporto aureo 8 4
Fibonacci con programmazione dinamica Fib( n ) { // n >= 0 F[0] = 0; F[1] = 1; for (k = 2; k <= n; k = k + 1) F[k] = F[k-1] + F[k-2]; RETURN F[n]; } [alvie] L array F è la tabella di programmazione dinamica e richiede O(n) tempo per essere riempita F n può essere anche calcolato in tempo O(log n), basandosi sulla forma chiusa di Binet. 9 Problema del resto Dati: un insieme - = {,..,/ } di tipi di monete, di valore 1,,1 / un numero illimitato di monete di tipo 2 per 0 2 3 1 un intero 4 si vuole determinare un vettore,, /, dove è il numero di monete dell'2 esimo tipo, in modo da ottenere il resto 4 con il minor numero possibile di monete. Vogliamo quindi minimizzare / con il vincolo 5 / 5 1 =4 10 5
Soluzione ricorsiva 6(4): minimo numero di monete necessario per ottenere il resto 4 o 4= è il caso base. Non occorre restituire alcuna moneta, per cui 6 = o 4>: è il caso ricorsivo. Se si utilizza una moneta di tipo i per il resto 4, allora il problema diventa creare il resto di 4 1 col minor numero possibile di monete Definizione di 6 4 conseguente: CD 4= 6 4 =: + + min 6(4 1 CD 1 >6 ED6 FG / ) >:? @ AB +HI6/DI 11 Algoritmo per il problema del resto Basato sul riempimenti di una tabella resto di dimensione 4+. Problema più generale: o resto[c] indica il minimo numero di monete necessarie per ottenere il resto c o Se c=r, abbiamo la soluzione o riempimento della tabella da sinistra verso destra Caso base: resto[c]=0 Caso ricorsivo, calcola resto[c] prendendo il minimo tra tutti i valori resto[k 1 ]+1 12 6
Algoritmo per il problema del resto 13 Algoritmo per il problema del resto: esempio 14 7
Algoritmo per il problema del resto: esempio 15 Algoritmo goloso per il problema del resto Più semplice: si effettuano iterativamente le operazioni seguenti, fino ad ottenere 4. o si sceglie la moneta di valore più alto a disposizione o se ne usa la massima quantità possibile Caratteristica tipica degli algoritmi golosi (greedy): le scelte si basano principalmente sulla configurazione attuale, senza cercare di valutarne le conseguenze sulle configurazioni successive. Può essere interpretato come un caso speciale di programmazione dinamica. 16 8
Coin Changing: Greedy Algorithm 17 Coin Changing: Greedy Algorithm 18 9
Algoritmo goloso per il problema del resto 19 Non ottimalitàdell'algoritmo goloso per il problema del resto Esempio: M=9,3=3,P Q =6, P S =4 P U =1. Algoritmo goloso: una moneta da 6e tre monete da 1, in totale 4 Soluzione ottima: due monete da 4 e una 1, in totale 3 20 10
Greedy Algorithm: Special cases 21 22 11
Sequenza ottima di moltiplicazioni Vogliamo usare la programmazione dinamica per calcolare il prodotto di n matrici V 2 di taglia W 2 X W2 + 1 Hp. semplificativa: il costo di moltiplicazione di due matrici di taglia Ze Z [ è pari a Z [ Esempio: W 0 =100,W 1 =20,W 2 =1000,W 3 =2,W 4 =50 23 Sequenza ottima di moltiplicazioni Il costo varia a seconda di come associamo il prodotto utilizzando la moltiplicazione tra due matrici: in generale, qual è il costo minimo? input: sequenza di interi positivi W 0,W 1,,W (dove W 2 W > S è la taglia della matrice V 2) output: minimo numero di operazioni per calcolare (detto costo minimo per V ) con l ipotesi che la moltiplicazione di due matrici di taglia Z e Z [ richieda Z [ operazioni. 24 12
Approccio di forza bruta Esiste un numero esponenziale (in ) di modi per calcolare il costo minimo per V usando l associatività con le parentesi: inefficiente! [Tale numero è quello di Catalan C n-1 = n.ro di alberi binari distinti con n-1 nodi interni, aventi ciascuno due figli] Metodo più efficiente: per 0 2 ^ 1, sia -(,_) = costo minimo per ` ` + L `_ (la cui taglia risultante è W 2 W a S ) Il costo minimo per V è quindi dato da b(0, 1) 25 Calcolo ricorsivo di M(i,j) Caso base: -(,) = per 0 2 1 (per ottenere V 2 non dobbiamo moltiplicare) Caso induttivo: per ogni 2 < ^, vale ` ` L `_ = (` ` L `6) (`6 `6 L `_) dim.: W 2 W a S W 2 W d S Wf S W a S costo: c c 6 c _ b(2,^) b(2,) + b(+1,^) + W > W d S W a S 26 13
Calcolo ricorsivo di M(i,j) b(2,^ b$2, b$1,^ W > YW d S YW a S 27 Ricorsione costosa (come con Fibonacci) Numero esponenziale di chiamate ricorsive, ciascuna invocata molte volte con gli stessi parametri d ingresso 28 14
Tabella di programmazione dinamica Riempita per diagonali a partire dalla principale min b 0,0 b 1,2 W QYW S YW i b 0,1 b 2,2 W Q YW U YW i tabella: ghz[2[2][^] = b(2, ^ 29 Programmazione dinamica è iterativa Sostituiscono le chiamate ricorsive Serve a ricostruire la soluzione ottima 30 15
Analisi di complessità La tabella costi ha taglia e il calcolo di ciascuno dei suoi elementi richiede j() tempo (ciclo for più interno) CostoMinimoIterativo richiede k( ) tempo (laddove la versione ricorsiva è esponenziale in ) Lo spazio occupato è k( ) celle Esercizio: utilizzando la tabella indice, ricavare la sequenza ottima di moltiplicazioni per il calcolo di ` 31 Sotto-sequenza comune più lunga Definizione del problema: due stringhe di caratteri l, m, di lunghezze 3, stringa n sotto-sequenza comune più lunga (longest common subsequence, LCS) se non ne esistono sottosequenze di lunghezza maggiore che siano comuni ad l e m opq(+,): lunghezza delle sotto-sequenze comuni più lunghe di l e m numero esponenziale in 3 e di possibili sotto-sequenze comuni di l e m non è efficiente generarle tutte per poi scegliere quella di lunghezza massima 32 16
Sotto-sequenza comune più lunga Definizione: o(,_): lunghezza della sotto-sequenza più lunga comune alle due sequenze formate dai primi2 elementi di l e dai primi ^ elementi dim. o,_ = opq `,,r,_ o s(3, ) fornisce la soluzione opq +, al problema o s(2,0) = s(0,^) = 0 per ogni 2,^ 33 Sotto-sequenza comune più lunga Definizione: o,_ =: o,_ + max{o,_,o,_ } CD = F _= CD,_> D + =[_ ] CD,_> D + [_ ] se una delle due sequenza è vuota, allora la sotto-sequenza comune più lunga è vuota 34 17
Sotto-sequenza comune più lunga Definizione: o,_ : o,_ max.o,_,o,_ 0 CD F _ CD,_, D + J_L CD,_, D + vj_l se l ultimo carattere c in ciascuna sequenza è uguale, esiste una sotto-sequenza comune più lunga che lo contiene ed è ottenibile da quella in cui l ultimo carattere è rimosso. c c 35 Sotto-sequenza comune più lunga Definizione: o,_ : o,_ max.o,_,o,_ 0 CD F _ CD,_, D + J_L CD,_, D + vj_l se invece l ultimo carattere differisce, la sotto-sequenza comune più lunga è quella ottenuta rimuovendo l ultimo carattere in una sola delle due sequenze 36 18
37 Sotto-sequenza comune più lunga Definizione: o,_ =: o,_ + max{o,_,o,_ } CD = F _= CD,_> D + =[_ ] CD,_> D + [_ ] Tabella lunghezza di dimensione (/ + ) ( + ): l'algoritmo pone HwGxDyy+[][_] = s(2, ^) j(3) elementi da determinare, corrispondenti a k(/) sottoproblemi L'elemento HwGxDyy+[][_] è calcolabile in tempo costante, dati tutti gli elementi HwGxDyy+[6][C] con C < _ o con C = _,6 < L'algoritmo richiede tempo k(/) 38 19
Partizione di insiemi di interi Insieme di interi positivi `={+,+,,+ } di somma totale (pari) + = C. Si vuole determinare se esiste `z= +,+,,+ { ` { tale che + =C. Risolubile generando esplicitamente tutti i sottoinsiemi di `. sottoinsiemi: tempo esponenziale in 39 Programmazione dinamica Definiamo per 0 2 e 0 ^ Z ~(2,^) = [ sse esiste {l 0,l 1,..,l >S } t.c. = j Nota: ~(, Z) = [ sse la partizione è possibile [convenzione: {+,+,,+ } = ] 40 20
Programmazione dinamica per partizione Regola ricorsiva se l insieme è vuoto allora la somma ^=0 è l unica possibile 41 Programmazione dinamica per partizione Regola ricorsiva la somma pari ^ era già possibile con un sottoinsieme di.+,+,..,+ 0 che non include l >S 42 21
Programmazione dinamica per partizione Regola ricorsiva la somma pari ^ èpossibile con un sottoinsieme che include l > : eliminando l >S deve esistere un sottoinsime la cui somma è pari a jl >S (ovviamente se jl >S ) 43 Partizione di un insieme di interi: tabella di programmazione dinamica Tabella di programmazione dinamica di taglia 1 Z+1 parti[i][j] = ~(2,^) Ordine riempimento per righe 44 22
Partizione di insiemi di interi: esempi 45 Programmazione dinamica e partizione di insiemi di interi 46 23
Analisi di complessità per partizione Tempo k(c) Spazio k(c) k(c) usando solo le ultime due righe compilate iterativamente Nota: in generale è possibile ottenere delle prestazioni simili a quelle della programmazione dinamica usando ricorsione + memoization [memoization = prender nota] Convenzione: Φ denota un valore indefinito 47 Ricorsione + memoization 48 24
Pseudo-polinomialità e programmazione dinamica Quando gli interi coinvolti nel problema diventano molto grandi dobbiamo usare un differente modello di computazione Esempio: complessità dell algoritmo di partizione è k(c) ma la dimensione dell input è circa + celle, per memorizzare la sequenza C, +,+,,+ : algoritmo pseudo-polinomiale Al crescere di C rispetto ad n, non possiamo più pensare di memorizzare C in una singola cella, ma piuttosto in { = k(logc ) celle (la sua rappresentazione binaria): la dimensione dell input diventa di circa + { celle di memoria La complessità k( C) = k( { ) diventa quindi esponenziale. In tal caso va confrontata con quella k( ) che genera tutti i sottoinsiemi. In altre parole, k(c) è una complessità polinomiale soltanto se C = k( ) per una costante >. 49 25