Una distinzione caso sulla programmazione dinamica: esempio necessario!


19

Ho lavorato sulla programmazione dinamica per qualche tempo. Il modo canonico per valutare una ricorsione di programmazione dinamica consiste nel creare una tabella di tutti i valori necessari e compilarla riga per riga. Vedi ad esempio Cormen, Leiserson et al: "Introduzione agli algoritmi" per un'introduzione.

Mi concentro sullo schema di calcolo basato su tabella in due dimensioni (riempimento riga per riga) e indago la struttura delle dipendenze delle celle, ovvero quali celle devono essere fatte prima che un'altra possa essere calcolata. Indichiamo con l'insieme di indici di celle da cui dipende la cella . Si noti che deve essere privo di cicli.i ΓΓ(i)iΓ

Estraggo dalla funzione effettiva che viene calcolata e mi concentro sulla sua struttura ricorsiva. Formalmente, mi si consideri un recurrrence essere programmazione dinamica se ha la formad

d(i)=f(i,Γ~d(i))

con , e qualche funzione (calcolabile) che non utilizza diverso tramite .˜ Γ d ( i ) = { ( j , d ( j ) ) jΓ d ( i ) } f d ˜ Γ di[0m]×[0n]Γ~d(i)={(j,d(j))jΓd(i)}fdΓ~d

Quando si limita la granularità di alle aree ruvide (a sinistra, in alto a sinistra, in alto, in alto a destra, ... della cella corrente) si osserva che ci sono essenzialmente tre casi (fino a simmetrie e rotazione) di validi ricorsioni di programmazione dinamica che informano su come compilare la tabella:Γd

Tre casi di dipendenze dinamiche di celle di programmazione

Le aree rosse indicano (sovrastimazioni di) . I casi uno e due ammettono sottoinsiemi, il caso tre è il caso peggiore (fino alla trasformazione dell'indice). Si noti che non è strettamente necessario che tutte le aree rosse siano coperte da ; alcune celle in ogni parte rossa della tabella sono sufficienti per dipingerlo di rosso. Le aree bianche sono esplicitamente richieste per non contenere alcuna cella richiesta.ΓΓ

Esempi per il primo caso sono la distanza di modifica e la sottosequenza comune più lunga , il secondo caso si applica a Bellman & Ford e CYK . Esempi meno ovvi includono quelli che funzionano sulle diagonali anziché sulle file (o colonne) in quanto possono essere ruotate per adattarsi ai casi proposti; vedere la risposta di Joe per un esempio.

Non ho un esempio (naturale) per il terzo caso! Quindi la mia domanda è: quali sono esempi di ricorsioni / problemi di programmazione dinamica del caso tre?


2
Caso 3 sussume casi 1 e 2.
jeffe

No, nonostante l'aspetto. Ad esempio, un'istanza del caso 1 non può avere una cella richiesta nell'area in alto a sinistra, mentre un'istanza del caso 3 deve avere una cella richiesta nell'area in alto a sinistra. Ho modificato la spiegazione per chiarire.
Raffaello

Risposte:


15

Ci sono molti altri esempi di algoritmi di programmazione dinamica che non si adattano affatto al tuo modello.

  • Il problema di sottosequenza crescente più lungo richiede solo una tabella unidimensionale.

  • Esistono diversi algoritmi di programmazione dinamica naturale le cui tabelle richiedono tre o anche più dimensioni. Ad esempio: trova il rettangolo bianco dell'area massima in una bitmap. L'algoritmo di programmazione dinamica naturale utilizza una tabella tridimensionale.

  • Ma soprattutto, la programmazione dinamica non riguarda le tabelle ; si tratta di svolgere una ricorsione. Esistono molti algoritmi di programmazione dinamica naturale in cui la struttura dei dati utilizzata per memorizzare i risultati intermedi non è un array, poiché la ricorrenza che si sta svolgendo non è su un intervallo di numeri interi. Due semplici esempi sono trovare la più grande serie indipendente di vertici in un albero e trovare la più grande sottostruttura comune di due alberi. Un esempio più complesso è l' algoritmo di approssimazione per il problema del venditore ambulante euclideo di Arora e Mitchell.(1+ϵ)


Grazie per la risposta, ma ho limitato esplicitamente la domanda ai problemi bidimensionali e allo schema di calcolo canonico basato su tabella (modificato per rendere ancora più chiaro quel punto). Sono a conoscenza del quadro più generale, ma non mi interessa a questo punto.
Raffaello

9
Okay, ma penso davvero che ti manchi il punto.
JeffE,

Dato che ci sono molti voti positivi, ho pensato che avrei dovuto chiarire questo punto: questo post non risponde alla domanda e in realtà non ci tenta nemmeno.
Raffaello

2
@Raphael è corretto. La mia "risposta" non è una risposta ma una critica alla domanda, ma era troppo lungo per un commento.
JeffE,

3

La funzione di calcolo di Ackermann è in questo spirito. Per calcolare devi conoscere A ( m , n - 1 ) e A ( m - 1 , k ) per alcuni k grandi . La seconda coordinata diminuisce oppure la prima diminuisce e la seconda potenzialmente aumenta.A(m,n)A(m,n1)A(m1,k)k

Ciò non si adatta idealmente ai requisiti, poiché il numero di colonne è infinito e il calcolo viene solitamente eseguito dall'alto verso il basso con la memorizzazione, ma penso che valga la pena menzionarlo.


1
A(m1,A(m,n1))

1
Non sono sicuro del motivo per cui questa risposta è stata sottoposta a downgrade, in quanto è una buona risposta. La funzione Ackermann si presta molto bene alla programmazione dinamica. In generale, qualsiasi funzione definita ricorsivamente che viene ripetutamente calcolata per gli stessi argomenti si presta alla programmazione dinamica. Per vederlo bisogna solo implementarlo e confrontare i tempi di esecuzione. Ciò che richiede anni per il calcolo con la normale funzione di Ackermann può richiedere alcuni secondi con quella di programmazione dinamica.
Jules,

@Jules: Il problema per lo schema di tabella canonica è che non si conosce a priori un (primitivo ricorsivo) legato alla dimensione della tabella. Ovviamente puoi fare memoria, ma non nel modo consueto. Quindi sì, può essere fattibile per DP ma non si adatta alla classe di problemi di cui mi occupo.
Raffaello

1
Non penso sia un requisito per DP avere un limite a priori sulla dimensione del tavolo? Infatti, come menziona JeffE, la cache non deve essere affatto una tabella. Può essere qualsiasi struttura di dati associativa. DP è davvero un'idea molto semplice: si desidera calcolare una funzione definita in modo ricorsivo, ma questa funzione viene chiamata più volte sugli stessi argomenti. DP è l'ottimizzazione in cui si introduce una cache per assicurarsi di calcolare ogni caso una sola volta. Esistono molte funzioni che non rientrano in nessuno dei tuoi casi, anche se sono funzioni di due numeri interi limitati.
Jules,

2

Questo non si adatta esattamente al caso 3, ma non so se qualcuno dei tuoi casi catturi un problema molto comune usato per insegnare la programmazione dinamica: la moltiplicazione a catena di matrici . Per risolvere questo problema (e molti altri, questo è solo quello canonico) riempiamo la matrice diagonale per diagonale anziché riga per riga.

Quindi la regola è qualcosa del genere:

diagMatrix


1
Scritto in questo modo non si adatta in nessun caso. Tuttavia, se si ruota di 45 gradi in senso orario si ottiene il caso 2 (e tutte le proprietà implicite). Questo vale anche per altri esempi che funzionano anche dalla diagonale verso gli angoli. Ma grazie per averlo menzionato!
Raffaello

@Raphael non è immediatamente ovvio che siano equivalenti, potresti volerlo menzionare nella tua domanda.
Joe,

0

So che è un esempio sciocco, ma penso che un semplice problema iterativo come

Trova la somma dei numeri in una matrice quadrata

potrebbe qualificarsi. Il tradizionale "per ogni riga per ogni colonna" sembra il tuo caso 3.


-1

Questo non è esattamente lo spazio di ricerca che stai cercando, ma ho un'idea della parte superiore della mia testa che potrebbe essere di aiuto.

Problema:

n×nMxM

Risposta

Questo può essere risolto nel seguente modo ricorsivo:

k=1+n2xmk,kx<mk,kmi,jkinkjn1/4x>mk,k1/434n2x(34)3n2


1
Questa non è un'istanza di programmazione dinamica, vero?
Raffaello
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.