Somiglianze di programmazione dinamica e divisione e conquista
Per come la vedo per ora posso dire che la programmazione dinamica è un'estensione del paradigma di divisione e conquista .
Non li tratterei come qualcosa di completamente diverso. Perché entrambi funzionano abbattendo ricorsivamente un problema in due o più sotto-problemi dello stesso tipo o correlati, fino a quando questi non diventano abbastanza semplici da essere risolti direttamente. Le soluzioni ai problemi secondari vengono quindi combinate per fornire una soluzione al problema originale.
Quindi perché abbiamo ancora nomi di paradigmi diversi allora e perché ho chiamato la programmazione dinamica un'estensione. È perché l'approccio di programmazione dinamica può essere applicato al problema solo se il problema ha determinate restrizioni o prerequisiti . E dopo che la programmazione dinamica estende l'approccio alla divisione e alla conquista con la memoizzazione o la tabulazione tecnica della .
Andiamo passo dopo passo ...
Prerequisiti / restrizioni di programmazione dinamica
Come abbiamo appena scoperto, ci sono due attributi chiave che il problema di divisione e conquista deve avere affinché la programmazione dinamica sia applicabile:
Sottostruttura ottimale - la soluzione ottimale può essere costruita da soluzioni ottimali dei suoi sottoproblemi
Sotto-problemi sovrapposti : il problema può essere suddiviso in sottoproblemi che vengono riutilizzati più volte o un algoritmo ricorsivo per il problema risolve ripetutamente lo stesso sottoproblema anziché generare sempre nuovi sottoproblemi
Una volta soddisfatte queste due condizioni, possiamo dire che questo problema di divisione e conquista può essere risolto utilizzando un approccio di programmazione dinamica.
Estensione di programmazione dinamica per Divide and Conquer
L'approccio di programmazione dinamica estende l'approccio alla divisione e alla conquista con due tecniche ( memoizzazione e tabulazione ) che hanno entrambe lo scopo di archiviare e riutilizzare soluzioni con problemi secondari che possono migliorare drasticamente le prestazioni. Ad esempio, l'implementazione ricorsiva ingenua della funzione di Fibonacci ha una complessità temporale in O(2^n)
cui la soluzione DP fa lo stesso con solo O(n)
tempo.
La memorizzazione (riempimento cache top-down) si riferisce alla tecnica di memorizzazione nella cache e riutilizzo dei risultati precedentemente calcolati. La fib
funzione memorizzata sarebbe quindi simile a questa:
memFib(n) {
if (mem[n] is undefined)
if (n < 2) result = n
else result = memFib(n-2) + memFib(n-1)
mem[n] = result
return mem[n]
}
La tabulazione (riempimento della cache dal basso verso l'alto) è simile ma si concentra sul riempimento delle voci della cache. Il calcolo dei valori nella cache viene eseguito più facilmente in modo iterativo. La versione di tabulazione fib
sarebbe simile a questa:
tabFib(n) {
mem[0] = 0
mem[1] = 1
for i = 2...n
mem[i] = mem[i-2] + mem[i-1]
return mem[n]
}
Puoi leggere di più sulla comparazione della memoizzazione e della tabulazione qui .
L'idea principale che dovresti capire qui è che, poiché il nostro problema di divisione e conquista ha sovrapposizioni di sotto-problemi, diventa possibile la memorizzazione nella cache delle soluzioni di sotto-problemi e quindi la memoizzazione / tabulazione salgono sulla scena.
Quindi qual è la differenza tra DP e DC dopo tutto
Dato che ora conosciamo i prerequisiti DP e le sue metodologie, siamo pronti a mettere tutto ciò che è stato menzionato sopra in una sola immagine.
Se vuoi vedere esempi di codice puoi dare un'occhiata a una spiegazione più dettagliata qui dove troverai due esempi di algoritmo: Ricerca binaria e Distanza minima di modifica (Levenshtein Distance) che illustrano la differenza tra DP e DC.