Programmazione dinamica - Fibonacci, memoizzazione e schema del paradigma
In questa pagina 6
Il divide and conquerParadigma in cui un'istanza si divide in sottoistanze più piccole, si risolvono ricorsivamente e si ricombinano le soluzioni (vedi Divide and conquer - proprietà di sottostruttura e albero delle chiamateUn algoritmo divide and conquer nasce da una proprietà di sottostruttura (funzioni Divide e Conquer, base n0) con sottoistanze di taglia strettamente minore; l'albero delle chiamate ne descrive l'esecuzione, e il D&C non ha memoria (ripete le sottoistanze uguali). Esempio completo: MAX di un array, con correttezza per induzione, ricorrenza e conteggio esatto n-1 tramite il teorema sugli alberi binari pieni.Divide and conquer - proprietà di sottostruttura e albero delle chiamate →) è smemorato: ogni nodo dell'albero delle chiamate viene espanso per intero, anche se un nodo con la stessa sottoistanza è già stato risolto altrove. Se le sottoistanze si ripetono, il costo può diventare esponenziale. La programmazione dinamica (DP, dynamic programming) mantiene una tabella con le soluzioni già calcolate e le riusa.
Un esempio semplice: i numeri di Fibonacci
Definizione. Si pone e per . I primi valori sono
(Qui , come nelle dispense del corso: il di altri testi, con , è il di questa nota.) La definizione suggerisce subito un algoritmo divide and conquer:
RFIB(n)
if n = 0 or n = 1 then return 1
return RFIB(n-1) + RFIB(n-2)Costo del ricorsivo
Si contano le addizioni tra interi. Il numero soddisfa
Valore esatto: . Per induzione su (vedi Principio di induzionePer dimostrare una proprietà per ogni n basta il passo base e il passo induttivo da n a n+1.Principio di induzione →). Base: e . Passo: per , supponendo la tesi per e ,
Il numero di chiamate (nodi dell'albero) è : ogni addizione corrisponde a un nodo interno, e un albero binario pieno con nodi interni ha foglie, in tutto nodi.
Minorazione esponenziale (srotolamento). Senza conoscere , è non decrescente (), quindi . Srotolando:
Si arriva ai casi base quando , cioè per (se è pari , se è dispari vale ). Lì , quindi
Il valore esatto cresce come con (formula di Binet: con ).
Dove si spreca lavoro
Albero delle chiamate per (a destra di ogni nodo, il valore restituito):
F(5)=8
├─ F(4)=5
│ ├─ F(3)=3
│ │ ├─ F(2)=2
│ │ │ ├─ F(1)=1
│ │ │ └─ F(0)=1
│ │ └─ F(1)=1
│ └─ F(2)=2
│ ├─ F(1)=1
│ └─ F(0)=1
└─ F(3)=3
├─ F(2)=2
│ ├─ F(1)=1
│ └─ F(0)=1
└─ F(1)=1Le chiamate sono (le addizioni sono ). Il sottoalbero di è espanso due volte, quello di tre volte; per argomento, le chiamate sono: una, una, due, tre, cinque, tre. Il grado di replicazione cresce esponenzialmente con .
Il bottom-up: ITFIB
Basta riempire una tabella dai casi base verso l'alto.
ITFIB(n)
if n = 0 or n = 1 then return 1
F[0] <- 1
F[1] <- 1
for i <- 2 to n do
F[i] <- F[i-1] + F[i-2]
return F[n]- Correttezza per induzione su : i casi base sono corretti; se e sono corretti, lo è anche .
- Costo: il ciclo esegue iterazioni (da a ), una addizione ciascuna: . Per : addizioni contro del ricorsivo; per : contro .
Tabella per :
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|---|
| 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 |
Grafico interattivo: Chiamate di RFIB(n), 2F_n − 1 ≈ 2·φ^(n+1)/√5 − 1, contro le 2n − 1 della versione con memoizzazione: per n = 5 sono 15 contro 9, per n = 15 sono 1973 contro 29
Osservazione (taglia dell'istanza). Anche è inefficiente rispetto alla vera taglia dell'istanza. La taglia di non è , ma il numero di cifre per scriverlo in notazione posizionale, (vedi Problemi computazionali - istanze, soluzioni, correttezza e complessitàUn problema computazionale è una relazione totale (non necessariamente univoca) tra istanze e soluzioni; un algoritmo è una funzione dalle istanze alle soluzioni e risolve il problema se la sua uscita è sempre in relazione con l'ingresso. Correttezza = coerenza con la specifica; complessità = costo in funzione della taglia, per default al caso peggiore, con le istruzioni a costo unitario scelte in modo significativo.Problemi computazionali - istanze, soluzioni, correttezza e complessità →). Quindi ITFIB è esponenziale nella taglia () e RFIB addirittura doppiamente esponenziale. Esistono algoritmi con operazioni aritmetiche, cioè lineari nella taglia.
Un algoritmo logaritmico lavora direttamente sugli interi. Vale
(con per far tornare il caso ; per esempio la quarta potenza è , con , , ). Elevando la matrice a potenza con le quadrature successive (Esponenziazione veloce per quadrature successivepotenza x^n con un operatore associativo con elemento neutro, costo = numero di applicazioni dell'operatore; STUPID_POWER costa n-1, la sottostruttura x^n = (x^floor(n/2))^2 (per n dispari per x) dà POWER con T(n) = floor(log2 n) + (numero di bit a 1 di n) - 1, Theta(log n) (caso peggiore 2 floor(log2 n), n = 2^t - 1); versioni iterative POWER2 e POWER3 dalla rappresentazione binaria di n; esempio con la matrice di Fibonacci; il modello a costo unitario vale solo se l'operatore non fa crescere gli operandi.Esponenziazione veloce per quadrature successive →) bastano prodotti di matrici . Queste identità sono state controllate con Python per .
Considerazioni sulla programmazione dinamica
La DP conserva l'idea del divide and conquer (una proprietà di sottostruttura: la soluzione di un'istanza si ottiene da quelle di istanze più piccole), ma cambia il modo di calcolare. Il processo è puramente bottom-up: si parte dalle istanze più piccole e, ricombinando le soluzioni salvate, si arriva alle più grandi. La fase top-down del D&C (scendere dalla radice alle foglie per decidere quali sottoistanze servono) sparisce.
- Vantaggio: si ottiene un algoritmo iterativo, in pratica più veloce (niente costi di ricorsione).
- Svantaggio 1: senza la ricorsione che guida, si risolvono tutte le sottoistanze di taglia inferiore riconducibili alla sottostruttura, anche quelle che servono poco o niente per l'istanza data.
- Svantaggio 2: l'ordine bottom-up va costruito a mano, e in alcuni casi non è ovvio (lo si vede nella Matrix Chain Multiplication).
Condizioni per applicare la DP:
- il problema ha una proprietà di sottostruttura;
- le sottoistanze generate compaiono più volte (overlapping subproblems): è questo che giustifica la tabella;
- lo spazio delle sottoistanze potenzialmente utilizzabili è abbastanza ristretto (polinomiale), perché vanno calcolate e salvate tutte.
Memoizzazione
La memoizzazioneDal latino memo, promemoria: da non confondere con memorizzazione è un approccio ibrido: si mantiene la ricorsione della sottostruttura (e quindi la fase top-down del D&C), ma con una tabella globale a tutte le chiamate in cui salvare le istanze già risolte. L'algoritmo ha due procedure.
INIT_X, non ricorsiva: risolve i casi base, inizializza la tabella (casi base più un valore di default, la sentinella, per le istanze ancora da risolvere) e chiamaREC_Xsull'istanza principale.REC_X, ricorsiva: controlla se la tabella contiene già la soluzione; se sì la restituisce, altrimenti calcola con Divide, Recurse e Conquer, salva in tabella prima di restituire.
Per Fibonacci:
INIT_FIB(n)
if n <= 1 then return 1
F[0] <- 1
F[1] <- 1
for i <- 2 to n do F[i] <- 0 { 0 = non ancora calcolato }
return REC_FIB(n)
REC_FIB(i)
if F[i] = 0 then F[i] <- REC_FIB(i-1) + REC_FIB(i-2)
return F[i]Lo è un buon valore di default perché nessun vale (sono tutti ). La scelta della sentinella è parte della correttezza: deve essere un valore che la ricorrenza non può mai produrre (per le lunghezze di una sottosequenza si userà , per un costo minimo ). La correttezza discende dalla sottostruttura e da questa scelta.
Albero delle chiamate per (in ordine di esecuzione; = sono le chiamate che trovano il valore in tabella o sono casi base):
REC(5)
├─ REC(4)
│ ├─ REC(3)
│ │ ├─ REC(2)
│ │ │ ├─ REC(1) base
│ │ │ └─ REC(0) base F[2] = 2
│ │ └─ REC(1) base F[3] = 3
│ └─ REC(2) letto in tabella F[4] = 5
└─ REC(3) letto in tabella F[5] = 8Analisi della complessità
Non si può usare la ricorrenza per valutare il costo: una ricorrenza descrive l'esecuzione puramente funzionale del D&C, in cui ogni nodo viene espanso. Si analizza l'albero. Per Fibonacci l'albero di contiene tutte le istanze di taglia minore di , e ogni istanza non di base compare come nodo interno una sola volta (tutte le altre occorrenze sono letture della tabella). Quindi:
- nodi interni, con lavoro unitario (una addizione);
- l'albero è binario e pieno, quindi foglie a lavoro nullo;
- addizioni e chiamate in tutto.
Per : addizioni, chiamate (nell'albero sopra si contano nodi). Il conteggio è lo stesso di ITFIB; in pratica ITFIB è più veloce perché non ha i costi nascosti della ricorsione.
Sintesi per confronto:
| D&C puro | bottom-up | memoizzato | |
|---|---|---|---|
| tabella | no | sì | sì |
| fase top-down | sì | no | sì |
| sottoistanze risolte | tutte le occorrenze | tutte quelle dello spazio | solo quelle raggiunte dall'istanza principale |
| ordine di calcolo | automatico | da progettare | automatico |
| Fibonacci, addizioni |
La memoizzazione è poco usata, perché il bottom-up è spesso ugualmente efficiente e senza i costi della ricorsione. Conviene quando la risoluzione top-down evita di calcolare molte sottoistanze (un esempio è la LCS di stringhe simili: Longest Common Subsequence - ricorrenza, tabella e ricostruzionela LCS di X (m caratteri) e Y (n caratteri) è la sottosequenza comune più lunga. Sottoistanze = coppie di prefissi (Theta(mn)). Ricorrenza: l(i,j) = 0 se i = 0 o j = 0; 1 + l(i-1,j-1) se x_i = y_j; max{l(i-1,j), l(i,j-1)} altrimenti. Bottom-up con tabelle l e b (freccia): mn confronti; ricostruzione in O(m+n). Il D&C sulla stessa ricorrenza costa C(m+n,m) - 1 nodi interni nel caso peggiore (esponenziale). Memoizzazione: stessa complessità nel caso peggiore, ma molto meno lavoro su stringhe simili. Spazio ridotto a Theta(min{m,n}) se serve solo la lunghezza.Longest Common Subsequence - ricorrenza, tabella e ricostruzione →) o quando l'ordine bottom-up è difficile da scrivere.
In Python, la memoizzazione è una riga:
from functools import cache
@cache
def fib(n):
return 1 if n <= 1 else fib(n - 1) + fib(n - 2)
assert [fib(i) for i in range(8)] == [1, 1, 2, 3, 5, 8, 13, 21]Problemi di ottimizzazione e sottostruttura ottima
Un problema computazionale è una relazione tra istanze e soluzioni; per un'istanza le soluzioni ammissibili sono . Un problema di ottimizzazione ha una funzione di costo e chiede la migliore soluzione ammissibile (minimo o massimo di su ), oppure di provare che . Una soluzione è ottima se .
Definizione (sottostruttura ottima). Un problema di ottimizzazione ha la proprietà di sottostruttura ottima se una soluzione ottima di un'istanza si ottiene da soluzioni ottime di istanze più piccole.
Si dimostra quasi sempre con la tecnica del cut-and-paste: si suppone per assurdo che una sottosoluzione contenuta nella soluzione ottima non sia ottima, la si sostituisce con una sottosoluzione ottima della stessa sottoistanza e si ottiene una soluzione complessiva strettamente migliore, in contraddizione con l'ottimalità. La prova della LCS e quella di Matrix Chain usano proprio questo argomento.
Avere un problema di ottimizzazione non implica che serva la DP: anche il D&C (o il greedy, vedi Paradigma greedy - scelta golosa, sottostruttura e selezione delle attivitàIl paradigma greedy compie subito una scelta localmente migliore (scelta greedy), pulisce l'istanza (clean-up) e risolve l'unica sottoistanza residua, con ricorsione di coda eliminabile; il codice è facile, la correttezza no e richiede due proprietà, la scelta greedy (esiste un ottimo che la contiene) e la sottostruttura ottima (cut-and-paste), da cui la correttezza segue per induzione. Caso di studio: la selezione delle attività, con l'algoritmo del primo che finisce, correttezza completa, complessità Theta(n) dopo l'ordinamento e controesempi per le scelte greedy sbagliate.Paradigma greedy - scelta golosa, sottostruttura e selezione delle attività →) può risolverlo. La scelta dipende dalla presenza di sottoistanze ripetute (che rendono il D&C inefficiente) e dalla taglia dello spazio dei sottoproblemi (che devono stare in tabella).
Lo schema del paradigma
Dato un problema di ottimizzazione, un algoritmo di DP si sviluppa in cinque passi.
Schema DP.
- Sottostruttura ottima. Una relazione tra la soluzione ottima di un'istanza di taglia e le soluzioni ottime di istanze di taglia strettamente minore, più la caratterizzazione dei casi base (taglia ). Si dimostra (di solito con cut-and-paste). La presenza di sottoistanze ripetute giustifica la DP.
- Costo. Una ricorrenza sui costi . 2'. Informazione addizionale. La minima informazione necessaria a ricostruire da .
- Calcolo dei costi, bottom-up (ordine di riempimento coerente con le dipendenze della ricorrenza) oppure top-down memoizzato. 3'. Calcolo dell'informazione addizionale, insieme ai costi.
I passi e si saltano se interessa solo il costo dell'ottimo e non la soluzione: succede quando il costo è esso stesso una metrica (per esempio la similarità tra due sequenze di DNA, misurata dal numero minimo di cancellazioni e inserzioni; sapere quali operazioni siano contano di meno).
Se serve la soluzione, non si salvano le sottosoluzioni intere (costerebbe troppo spazio e tempo), ma solo l'informazione incrementale: per esempio una freccia, o l'indice di spezzamento. La soluzione ottima si ricostruisce poi in una fase di postprocessing, componendo queste informazioni.
I problemi svolti nelle note seguenti applicano tutti lo stesso schema:
Domande d'esame
- (Quesito a risposta unica.) Quante addizioni esegue
RFIB(6)? QuanteITFIB(6)? Traccia: , con , quindi addizioni (e chiamate); ITFIB ne fa . - (Quesito aperto.) Quali condizioni deve soddisfare un problema perché la programmazione dinamica sia conveniente rispetto al divide and conquer? Traccia: sottostruttura ottima; sottoistanze che si ripetono (altrimenti la tabella non serve, il D&C basta); spazio dei sottoproblemi polinomiale, perché si calcolano e si salvano tutti.
- (Quesito aperto.) Perché la complessità di un algoritmo memoizzato non si valuta con la ricorrenza del D&C? Qual è il costo di
REC_FIB(n)? Traccia: la ricorrenza conta ogni nodo dell'albero, ma nell'albero memoizzato molti nodi sono letture della tabella (lavoro nullo). Si contano i nodi interni: , quindi .
Esercizi d'esame svolti su ricorrenze a due indici calcolate con la tabella:
- calcolo iterativo e spazio lineare: Esercizio - ricorrenza c(i,j) con bordi dati da due array, calcolo iterativo e spazio lineare (compito del 18 giugno 2026, es. 2);
- calcolo memoizzato e scelta della sentinella: Esercizio - ricorrenza t(i,j) con calcolo memoizzato e conteggio esatto (compito del 26 giugno 2025, es. 2), Esercizio - ricorrenza c(i,j) con valore di default e conteggio delle operazioni (simulazione d'esame 2023-24, problema 2) e la nota Memoizzazione di ricorrenze triangolari - conteggio esatto e diagonali costantimemoizzazione di M(i,j) = M(i+1,j-1) M(i+1,j) M(i,j-1) sulle coppie 1 <= i <= j <= n, con M(i,i) = 1 e M(i,i+1) = 2. Il codice standard (INIT_M e REC_M, valore di default 0) esegue esattamente (n-1)(n-2) moltiplicazioni, uno nodo interno per ogni coppia con j >= i+2. Ma tutti gli elementi della stessa diagonale hanno lo stesso valore V(l) = V(l-1)^2 V(l-2): con questa osservazione bastano 2(n-2) moltiplicazioni. Lezione: prima di scrivere la tabella conviene cercare regolarità nella ricorrenza.Memoizzazione di ricorrenze triangolari - conteggio esatto e diagonali costanti →;
- ottimizzazione su suffissi, con due ricorrenze intrecciate: Esercizio - costo alternato minimo e massimo di una sottosequenza (simulazione d'esame 2025-26, es. 2).
Versione ripasso
- Fibonacci. , .
RFIBricorsivo: , esatto (induzione); chiamate ( per , addizioni ). Minorazione per srotolamento: , con : . Valore esatto , . Il sottoalbero di è espanso volte, quello di volte. - ITFIB (tabella ,
for i <- 2 to n): addizioni, correttezza per induzione su . Tabella: . - Taglia dell'istanza: (Problemi computazionali - istanze, soluzioni, correttezza e complessitàUn problema computazionale è una relazione totale (non necessariamente univoca) tra istanze e soluzioni; un algoritmo è una funzione dalle istanze alle soluzioni e risolve il problema se la sua uscita è sempre in relazione con l'ingresso. Correttezza = coerenza con la specifica; complessità = costo in funzione della taglia, per default al caso peggiore, con le istruzioni a costo unitario scelte in modo significativo.Problemi computazionali - istanze, soluzioni, correttezza e complessità →), quindi ITFIB è esponenziale nella taglia. Con e le quadrature successive (Esponenziazione veloce per quadrature successivepotenza x^n con un operatore associativo con elemento neutro, costo = numero di applicazioni dell'operatore; STUPID_POWER costa n-1, la sottostruttura x^n = (x^floor(n/2))^2 (per n dispari per x) dà POWER con T(n) = floor(log2 n) + (numero di bit a 1 di n) - 1, Theta(log n) (caso peggiore 2 floor(log2 n), n = 2^t - 1); versioni iterative POWER2 e POWER3 dalla rappresentazione binaria di n; esempio con la matrice di Fibonacci; il modello a costo unitario vale solo se l'operatore non fa crescere gli operandi.Esponenziazione veloce per quadrature successive →) bastano operazioni.
- Condizioni per la DP: sottostruttura; sottoistanze ripetute (overlapping); spazio dei sottoproblemi ristretto. È bottom-up: sparisce la fase top-down (vantaggio: algoritmo iterativo; svantaggi: si risolve anche l'inutile, l'ordine va progettato a mano).
- Memoizzazione = ricorsione + tabella globale.
INIT(casi base, tabella, sentinella, chiamaREC) eREC(se in tabella restituisce, altrimenti calcola, salva, restituisce). La sentinella non deve mai essere un valore possibile ( per Fibonacci, per lunghezze, per minimi). Costo: si conta l'albero, non la ricorrenza; per Fibonacci nodi interni, foglie, chiamate ( per ). Utile quando il top-down evita molte sottoistanze o l'ordine è difficile; altrimenti meglio il bottom-up. - Ottimizzazione. soluzioni ammissibili, costo , ottima se . Sottostruttura ottima: l'ottimo contiene ottimi di sottoistanze; si prova con il cut-and-paste (si sostituisce una sottosoluzione non ottima e si ottiene una contraddizione). La DP non è obbligata: anche D&C o greedy (Paradigma greedy - scelta golosa, sottostruttura e selezione delle attivitàIl paradigma greedy compie subito una scelta localmente migliore (scelta greedy), pulisce l'istanza (clean-up) e risolve l'unica sottoistanza residua, con ricorsione di coda eliminabile; il codice è facile, la correttezza no e richiede due proprietà, la scelta greedy (esiste un ottimo che la contiene) e la sottostruttura ottima (cut-and-paste), da cui la correttezza segue per induzione. Caso di studio: la selezione delle attività, con l'algoritmo del primo che finisce, correttezza completa, complessità Theta(n) dopo l'ordinamento e controesempi per le scelte greedy sbagliate.Paradigma greedy - scelta golosa, sottostruttura e selezione delle attività →).
- Schema DP: (1) sottostruttura ottima e casi base, (2) ricorrenza sui costi, (2') informazione addizionale minima (freccia, indice), (3) calcolo bottom-up o memoizzato, (3') calcolo dell'informazione insieme ai costi, poi ricostruzione in postprocessing. Se serve solo il costo si saltano e .
- Codice.
ITFIB(n): F[0] <- F[1] <- 1; for i <- 2 to n do F[i] <- F[i-1] + F[i-2]; return F[n]
INIT_FIB(n): F[0] <- F[1] <- 1; for i <- 2 to n do F[i] <- 0; return REC_FIB(n)
REC_FIB(i): if F[i] = 0 then F[i] <- REC_FIB(i-1) + REC_FIB(i-2); return F[i]- Esempio memoizzato: le chiamate, in ordine, sono (le ultime tre sono letture di tabella o casi base): chiamate, addizioni, contro chiamate e addizioni del ricorsivo. Per il ricorsivo fa addizioni, il bottom-up .
- Confronto per Fibonacci: D&C puro addizioni (espande tutte le occorrenze); bottom-up (tutte le sottoistanze, ordine da progettare); memoizzato (solo le sottoistanze raggiunte, ordine automatico).
- Come si applica lo schema ai problemi delle note seguenti: LCS, coppie di prefissi, celle, scelte, freccia (Longest Common Subsequence - ricorrenza, tabella e ricostruzionela LCS di X (m caratteri) e Y (n caratteri) è la sottosequenza comune più lunga. Sottoistanze = coppie di prefissi (Theta(mn)). Ricorrenza: l(i,j) = 0 se i = 0 o j = 0; 1 + l(i-1,j-1) se x_i = y_j; max{l(i-1,j), l(i,j-1)} altrimenti. Bottom-up con tabelle l e b (freccia): mn confronti; ricostruzione in O(m+n). Il D&C sulla stessa ricorrenza costa C(m+n,m) - 1 nodi interni nel caso peggiore (esponenziale). Memoizzazione: stessa complessità nel caso peggiore, ma molto meno lavoro su stringhe simili. Spazio ridotto a Theta(min{m,n}) se serve solo la lunghezza.Longest Common Subsequence - ricorrenza, tabella e ricostruzione →); Matrix Chain, sottocatene, celle, fino a scelte, indice , diagonali crescenti (Matrix Chain Multiplication - parentesizzazione ottimadato il vettore p delle dimensioni di una catena di n matrici compatibili (A_i è p_(i-1) x p_i), si cerca la parentesizzazione che minimizza il numero di prodotti tra scalari. Le parentesizzazioni sono numeri di Catalan (esponenziali). Sottostruttura: i due sottoalberi dell'ultimo prodotto sono ottimi (cut-and-paste). Ricorrenza c(i,j) = min su k di c(i,k) + c(k+1,j) + p_(i-1) p_k p_j, c(i,i) = 0. D&C: 3^(n-1) - 1 operazioni. Bottom-up per diagonali crescenti con tabelle m e s: (n^3 - n)/3 prodotti, Theta(n^3). La memoizzazione ha lo stesso costo e non risparmia mai.Matrix Chain Multiplication - parentesizzazione ottima →); allineamento e palindroma, come LCS e per diagonali (Allineamento di stringhe e sottosequenza palindroma più lungadue problemi di DP su stringhe con la stessa struttura della LCS. Allineamento ottimo di due stringhe di DNA con matrice di score S e penalità di gap g: C(i,j) = max{C(i-1,j-1) + s(x_i,y_j), C(i-1,j) + g, C(i,j-1) + g}, C(i,0) = i g, C(0,j) = j g; Theta(mn) tempo, spazio Theta(min{m,n}) con un solo vettore se serve solo il costo. Sottosequenza palindroma più lunga (LPS) sullo spazio delle sottostringhe X_(i..j): p(i,j) = 2 + p(i+1,j-1) se x_i = x_j, altrimenti max{p(i+1,j), p(i,j-1)}; scansione per diagonali, n(n-1)/2 confronti.Allineamento di stringhe e sottosequenza palindroma più lunga →).
- Errori tipici: usare la ricorrenza del D&C per analizzare il memoizzato; scegliere una sentinella che può essere un valore vero; salvare le sottosoluzioni intere invece di una freccia; dimenticare che in queste dispense; dire che ITFIB è "lineare" senza distinguere dalla taglia.
Esercizi su questo argomento
- Esercizio - costo alternato minimo e massimo di una sottosequenza (simulazione d'esame 2025-26, es. 2)
- Esercizio - funzione g(n) definita per ricorrenza, calcolo memoizzato e formula chiusa (compito del 19 giugno 2024, es. 2)
- Esercizio - ricorrenza c(i,j) con bordi dati da due array, calcolo iterativo e spazio lineare (compito del 18 giugno 2026, es. 2)
- Esercizio - ricorrenza c(i,j) con valore di default e conteggio delle operazioni (simulazione d'esame 2023-24, problema 2)
- Esercizio - ricorrenza t(i,j) con calcolo memoizzato e conteggio esatto (compito del 26 giugno 2025, es. 2)
Teoria collegata
- Allineamento di stringhe e sottosequenza palindroma più lunga
- Divide and conquer - proprietà di sottostruttura e albero delle chiamate
- Esercitazione greedy - tracce su CD, scheduling shortest-first e (2,1)-boxing
- Esponenziazione veloce per quadrature successive
- Longest Common Subsequence - ricorrenza, tabella e ricostruzione
- Matrix Chain Multiplication - parentesizzazione ottima
- Memoizzazione di ricorrenze triangolari - conteggio esatto e diagonali costanti
- Paradigma greedy - scelta golosa, sottostruttura e selezione delle attività