Memorization:
* Approccio: Top-down (ricorsivo con cache). Inizia con il problema originale e lo suddivide in modo ricorsivo in piccoli sottoproblemi. Poiché ogni sottoproblema viene risolto, il suo risultato viene memorizzato (memorizzato nella cache) in una struttura di dati (di solito un dizionario o una tabella). Se si incontra nuovamente lo stesso sottoproblema, il risultato memorizzato nella cache viene recuperato invece di essere ricompenso.
* Implementazione: In genere implementato utilizzando funzioni ricorsive e una cache. La cache viene generalmente implicitamente gestita attraverso chiamate ricorsive.
* Idoneità al problema: Ospitati per i problemi in cui non tutti i sottoproblemi devono necessariamente essere risolti. Calcola e memorizza solo i risultati di sottoproblemi che sono effettivamente necessari per risolvere il problema principale.
* Ordine di esecuzione: L'ordine di risolvere i sottoproblemi è determinato dalla ricorsione, seguendo naturalmente la struttura del problema.
* Impatto di efficienza:
* * BUOST per le prestazioni:* Migliora significativamente le prestazioni quando l'algoritmo incontra gli stessi sottoproblemi più volte durante la ricorsione. Riduce la complessità del tempo esponenziale al tempo polinomiale in molti casi.
* * Overhead:* in sospese un po 'di sovraccarico a causa di ricerche di chiamata e cache. Questo sovraccarico a volte può essere significativo per problemi più piccoli in cui il costo di calcolo è già basso.
* * Complessità spaziale:* Usa lo spazio per la cache, che memorizza i risultati dei sottoproblemi risolti. Lo spazio richiesto dipende dal numero di sottoproblemi unici che sono effettivamente risolti.
* Leggibilità: Può essere più intuitivo e più facile da implementare rispetto alla programmazione dinamica, specialmente quando la struttura ricorsiva del problema è chiara.
Programmazione dinamica:
* Approccio: Bottom-Up (iterativo con tabulazione). Risolve tutti i possibili sottoproblemi in un ordine specifico, in genere a partire dai più piccoli sottoproblemi e costruendo fino a quelli più grandi. I risultati di tutti i sottoproblemi sono memorizzati in una tabella (spesso un array multidimensionale).
* Implementazione: In genere implementato utilizzando loop iterativi e una tabella. L'ordine di calcolo è esplicitamente controllato dai loop.
* Idoneità al problema: Meglio adatti ai problemi in cui tutti i sottoproblemi devono essere risolti per arrivare alla soluzione finale.
* Ordine di esecuzione: L'ordine in cui vengono risolti i sottoproblemi è esplicitamente definito dall'algoritmo, generalmente basato sulle dipendenze tra i sottoproblemi.
* Impatto di efficienza:
* * BUOST per le prestazioni:* Migliora significativamente le prestazioni evitando calcoli ridondanti. Riduce la complessità del tempo esponenziale al tempo polinomiale in molti casi. Poiché utilizza i loop iterativi, viene generalmente evitato il sovraccarico delle chiamate di funzione, rendendolo potenzialmente più veloce della memorizzazione.
* * Overhead:* usa lo spazio per la tabella, che memorizza i risultati di * tutti * possibili sottoproblemi, anche quelli che potrebbero non essere usati direttamente per risolvere il problema principale.
* * Complessità spaziale:* Usa lo spazio per la tabella, che memorizza i risultati di tutti i possibili sottoproblemi. Questo può essere superiore alla memorizzazione se non sono necessari alcuni sottoproblemi.
* Leggibilità: Può essere meno intuitivo della memorizzazione, specialmente per problemi complessi. Richiede un'attenta pianificazione per determinare l'ordine corretto di risolvere i sottoproblemi.
Differenze chiave riassunte:
| Caratteristica | Memorizzazione | Programmazione dinamica |
| ---------------- | ------------------------------------------------ | --------------------------------------------------- |
| Approccio | Top-down (ricorsivo) | Bottom-up (iterativo) |
| Implementazione | Funzioni ricorsive + cache | Loop iterativi + Tabella |
| Idoneità del problema | Potrebbe essere necessario risolvere tutti i sottoproblemi | Tutti i sottoproblemi devono essere risolti |
| Ordine di esecuzione | Determinato da ricorsione | Esplicitamente definito (spesso iterativo) |
| Utilizzo dello spazio | Memorizza i risultati di * Subrobles * risolti * | Memorizza i risultati di * tutti i possibili * sottoproblemi |
| Overhead | Funzione Chiamata sovraccarica, ricerche nella cache | Meno sovraccarico (nessuna funzione di funzione) |
| Leggibilità | Spesso più intuitivo | Può essere meno intuitivo |
Impatto sull'efficienza (prestazioni):
* Complessità temporale: Sia la memorizzazione che la programmazione dinamica possono ridurre drasticamente la complessità temporale degli algoritmi che coinvolgono sottovalutazioni sovrapposte, spesso da esponenziale (ad esempio, O (2^n)) a polinomiale (ad esempio, O (n^2), O (n)).
* Complessità dello spazio: Entrambe le tecniche aumentano la complessità dello spazio. La memoizzazione memorizza i risultati di sottoproblemi risolti, mentre la programmazione dinamica memorizza i risultati di tutti i possibili sottoproblemi. La scelta tra i due può dipendere dai vincoli di memoria e dal problema specifico.
* Performance pratiche:
* In molti casi, la programmazione dinamica può essere leggermente più veloce a causa del sovraccarico inferiore dei loop iterativi rispetto alle chiamate di funzione ricorsive.
* Tuttavia, se è necessario risolvere solo una piccola frazione dei possibili sottoproblemi, la memorizzazione può essere più efficiente in termini di tempo e spazio perché calcola e memorizza solo i risultati necessari.
Quando usare ciò:
* Usa Memorization:
* Quando il problema ha una formulazione ricorsiva naturale.
* Quando non tutti i sottoproblemi devono essere risolti.
* Quando la leggibilità e la facilità di implementazione sono prioritarie.
* Usa la programmazione dinamica:
* Quando tutti i sottoproblemi devono essere risolti per ottenere la soluzione finale.
* Quando le prestazioni sono critiche e le spese generali di chiamata di funzione dovrebbero essere ridotte al minimo.
* Quando un approccio dal basso verso l'alto è più naturale per il problema.
Esempio (sequenza Fibonacci):
Memorization (Python):
`` `Python
def fib_memo (n, memo ={}):
Se N in memo:
Return Memo [n]
Se n <=1:
restituire n
Memo [n] =fib_memo (n - 1, memo) + fib_memo (n - 2, memo)
Return Memo [n]
Stampa (FIB_MEMO (10)) # Output:55
`` `
Programmazione dinamica (Python):
`` `Python
def fib_dp (n):
fib_table =[0] * (n + 1)
Se n> =0:
fib_table [0] =0
Se n> =1:
fib_table [1] =1
per i nell'intervallo (2, n + 1):
fib_table [i] =fib_table [i - 1] + fib_table [i - 2]
return fib_table [n]
print (fib_dp (10)) # output:55
`` `
In questo esempio, sia la memoizzazione che la programmazione dinamica trasformano la soluzione ricorsiva ingenua (che ha complessità temporale esponenziale) in un algoritmo con complessità temporale O (N). La versione di programmazione dinamica, tuttavia, evita il sovraccarico della chiamata di funzione e sarà generalmente leggermente più veloce in pratica.
software © www.354353.com