Ricerca…


introduzione

La programmazione dinamica è un concetto ampiamente utilizzato e viene spesso utilizzato per l'ottimizzazione. Si riferisce alla semplificazione di un problema complicato suddividendolo in sottoprocessi più semplici in modo ricorsivo, in genere approccio Bottom-up. Ci sono due attributi chiave che un problema deve avere affinché la programmazione dinamica sia applicabile "Sottostruttura ottimale" e "Sottosistemi sovrapposti". Per ottenere la sua ottimizzazione, la programmazione Dynamics utilizza un concetto chiamato Memorizzazione

Osservazioni

La programmazione dinamica è un miglioramento di Brute Force, vedi questo esempio per capire come si può ottenere una soluzione di programmazione dinamica da Brute Force.

Una soluzione di programmazione dinamica ha 2 requisiti principali:

  1. Problemi sovrapposti

  2. Sottostruttura ottimale

Sottoprogetti sovrapposti significa che i risultati di versioni più piccole del problema vengono riutilizzati più volte per arrivare alla soluzione del problema originale

Optimal Substructure significa che esiste un metodo per calcolare un problema dai suoi sottoproblemi.

Una soluzione di programmazione dinamica ha 2 componenti principali, lo stato e la transizione

Lo stato fa riferimento a un sottoproblema del problema originale.

La transizione è il metodo per risolvere un problema in base ai suoi sottoproblemi

Il tempo impiegato da una soluzione di programmazione dinamica può essere calcolato come No. of States * Transition Time . Quindi se una soluzione ha stati N^2 e la transizione è O(N) , allora la soluzione richiederebbe circa O(N^3) tempo.

Problema dello zaino


0-1 Zaino

Il Problema dello zaino è un problema quando viene dato un insieme di oggetti, ciascuno con un peso, un valore e esattamente 1 copia , determinare quale articolo (i) includere in una collezione in modo che il peso totale sia inferiore o uguale a un dato limite e il valore totale è il più ampio possibile.

Esempio C ++:

Implementazione :

int knapsack(vector<int> &value, vector<int> &weight, int N, int C){
    int dp[C+1];                               
    for (int i = 1; i <= C; ++i){
        dp[i] = -100000000;                   
    }
    dp[0] = 0;
    for (int i = 0; i < N; ++i){
        for (int j = C; j >= weight[i]; --j){
            dp[j] = max(dp[j],dp[j-weight[i]]+value[i]);
        }
    }
    return dp[C];
}

Test :

3 5
5 2
2 1
3 2

Uscita :

3

Ciò significa che il valore massimo può essere raggiunto è 3, che si ottiene scegliendo (2,1) e (3,2).


Zaino illimitato

Il problema dello zaino non legato è un problema che, dato un insieme di elementi, ciascuno con un peso, un valore e copie infinite , determina il numero di ogni oggetto da includere in una collezione in modo che il peso totale sia inferiore o uguale a un dato limite e il valore totale è il più grande possibile.

Python (2.7.11) Esempio:

Implementazione :

def unbounded_knapsack(w, v, c): # weight, value and capactiy
    m = [0]
    for r in range(1, c+1):
        val = m[r-1]
        for i, wi in enumerate(w):
            if wi > r:
                continue
            val = max(val, v[i] + m[r-wi])
        m.append(val)
    return m[c] # return the maximum value can be achieved

La complessità di tale implementazione è O(nC) , che n è il numero di elementi.

Test :

w = [2, 3, 4, 5, 6]
v = [2, 4, 6, 8, 9]

print unbounded_knapsack(w, v, 13)

Uscita :

20

Ciò significa che il valore massimo può essere raggiunto è 20, che si ottiene scegliendo (5, 8), (5, 8) e (3, 4).

Algoritmo di pianificazione del lavoro ponderato

Algoritmo di pianificazione dei lavori ponderati può anche essere indicato come algoritmo di selezione delle attività ponderate.

Il problema è che, dati determinati lavori con l'ora di inizio e l'ora di fine e un profitto che si ottiene quando si finisce il lavoro, qual è il profitto massimo che si può dare dato che non è possibile eseguire due lavori in parallelo?

Questo assomiglia ad Activity Selection usando Greedy Algorithm, ma c'è una svolta in più. Cioè, invece di massimizzare il numero di posti di lavoro finiti, ci concentriamo sulla realizzazione del massimo profitto. Il numero di lavori eseguiti non importa qui.

Diamo un'occhiata a un esempio:

+-------------------------+---------+---------+---------+---------+---------+---------+
|          Name           |    A    |    B    |    C    |    D    |    E    |    F    |
+-------------------------+---------+---------+---------+---------+---------+---------+
|(Start Time, Finish Time)|  (2,5)  |  (6,7)  |  (7,9)  |  (1,3)  |  (5,8)  |  (4,6)  |
+-------------------------+---------+---------+---------+---------+---------+---------+
|         Profit          |    6    |    4    |    2    |    5    |    11   |    5    |
+-------------------------+---------+---------+---------+---------+---------+---------+

I lavori sono indicati con un nome, il loro inizio e fine e il profitto. Dopo alcune iterazioni, possiamo scoprire se eseguiamo Job-A e Job-E , possiamo ottenere il massimo profitto di 17. Ora come scoprirlo usando un algoritmo?

La prima cosa che facciamo è ordinare i lavori per il loro tempo di completamento in ordine decrescente. Perché lo facciamo? È perché se selezioniamo un lavoro che richiede meno tempo per terminare, lasciamo il maggior tempo per scegliere altri lavori. Abbiamo:

+-------------------------+---------+---------+---------+---------+---------+---------+
|          Name           |    D    |    A    |    F    |    B    |    E    |    C    |
+-------------------------+---------+---------+---------+---------+---------+---------+
|(Start Time, Finish Time)|  (1,3)  |  (2,5)  |  (4,6)  |  (6,7)  |  (5,8)  |  (7,9)  |
+-------------------------+---------+---------+---------+---------+---------+---------+
|         Profit          |    5    |    6    |    5    |    4    |    11   |    2    |
+-------------------------+---------+---------+---------+---------+---------+---------+

Avremo un array temporaneo aggiuntivo Acc_Prof di dimensione n (Qui, n indica il numero totale di lavori). Ciò conterrà il profitto massimo accumulato nell'esecuzione dei lavori. Non capisco? Aspetta e guarda. Inizializzeremo i valori dell'array con il profitto di ogni lavoro. Ciò significa che Acc_Prof [i] in un primo momento deterrà il profitto derivante dall'eseguire l' i-esimo lavoro.

+-------------------------+---------+---------+---------+---------+---------+---------+
|         Acc_Prof        |    5    |    6    |    5    |    4    |    11   |    2    |
+-------------------------+---------+---------+---------+---------+---------+---------+

Ora denotiamo la posizione 2 con i , e la posizione 1 sarà denotata con j . La nostra strategia sarà quella di iterare j da 1 a i-1 e dopo ogni iterazione, noi incrementiamo i di 1, fino a quando i diventa n + 1.

                               j        i

+-------------------------+---------+---------+---------+---------+---------+---------+
|          Name           |    D    |    A    |    F    |    B    |    E    |    C    |
+-------------------------+---------+---------+---------+---------+---------+---------+
|(Start Time, Finish Time)|  (1,3)  |  (2,5)  |  (4,6)  |  (6,7)  |  (5,8)  |  (7,9)  |
+-------------------------+---------+---------+---------+---------+---------+---------+
|         Profit          |    5    |    6    |    5    |    4    |    11   |    2    |
+-------------------------+---------+---------+---------+---------+---------+---------+
|         Acc_Prof        |    5    |    6    |    5    |    4    |    11   |    2    |
+-------------------------+---------+---------+---------+---------+---------+---------+

Controlliamo se Job [i] e Job [j] si sovrappongono, cioè se il tempo di fine di Job [j] è maggiore dell'ora di inizio di Job [i] , quindi questi due lavori non possono essere eseguiti insieme. Tuttavia, se non si sovrappongono, controlleremo se Acc_Prof [j] + Profitto [i]> Acc_Prof [i] . Se questo è il caso, aggiorneremo Acc_Prof [i] = Acc_Prof [j] + Profitto [i] . Questo è:

if Job[j].finish_time <= Job[i].start_time
    if Acc_Prof[j] + Profit[i] > Acc_Prof[i]
        Acc_Prof[i] = Acc_Prof[j] + Profit[i]
    endif
endif

Qui Acc_Prof [j] + Profitto [i] rappresenta il profitto accumulato di questi due posti di lavoro. Controlliamolo per il nostro esempio:

Qui Job [j] si sovrappone a Job [i] . Quindi questi non possono essere fatti insieme. Poiché il nostro j è uguale a i-1 , incrementiamo il valore di i in i + 1 cioè 3 . E facciamo j = 1 .

                               j                   i

+-------------------------+---------+---------+---------+---------+---------+---------+
|          Name           |    D    |    A    |    F    |    B    |    E    |    C    |
+-------------------------+---------+---------+---------+---------+---------+---------+
|(Start Time, Finish Time)|  (1,3)  |  (2,5)  |  (4,6)  |  (6,7)  |  (5,8)  |  (7,9)  |
+-------------------------+---------+---------+---------+---------+---------+---------+
|         Profit          |    5    |    6    |    5    |    4    |    11   |    2    |
+-------------------------+---------+---------+---------+---------+---------+---------+
|         Acc_Prof        |    5    |    6    |    5    |    4    |    11   |    2    |
+-------------------------+---------+---------+---------+---------+---------+---------+

Ora Job [j] e Job [i] non si sovrappongono. L'importo totale del profitto che possiamo ottenere selezionando questi due lavori è: Acc_Prof [j] + Profitto [i] = 5 + 5 = 10 che è maggiore di Acc_Prof [i] . Quindi aggiorniamo Acc_Prof [i] = 10 . Aumentiamo anche j di 1. Otteniamo,

                                         j         i
 
+-------------------------+---------+---------+---------+---------+---------+---------+
|          Name           |    D    |    A    |    F    |    B    |    E    |    C    |
+-------------------------+---------+---------+---------+---------+---------+---------+
|(Start Time, Finish Time)|  (1,3)  |  (2,5)  |  (4,6)  |  (6,7)  |  (5,8)  |  (7,9)  |
+-------------------------+---------+---------+---------+---------+---------+---------+
|         Profit          |    5    |    6    |    5    |    4    |    11   |    2    |
+-------------------------+---------+---------+---------+---------+---------+---------+
|         Acc_Prof        |    5    |    6    |    10   |    4    |    11   |    2    |
+-------------------------+---------+---------+---------+---------+---------+---------+

Qui, Job [j] si sovrappone a Job [i] e j è anche uguale a i-1 . Quindi incrementiamo i di 1, e facciamo j = 1 . Noi abbiamo,

                               j                             i
 
+-------------------------+---------+---------+---------+---------+---------+---------+
|          Name           |    D    |    A    |    F    |    B    |    E    |    C    |
+-------------------------+---------+---------+---------+---------+---------+---------+
|(Start Time, Finish Time)|  (1,3)  |  (2,5)  |  (4,6)  |  (6,7)  |  (5,8)  |  (7,9)  |
+-------------------------+---------+---------+---------+---------+---------+---------+
|         Profit          |    5    |    6    |    5    |    4    |    11   |    2    |
+-------------------------+---------+---------+---------+---------+---------+---------+
|         Acc_Prof        |    5    |    6    |    10   |    4    |    11   |    2    |
+-------------------------+---------+---------+---------+---------+---------+---------+

Ora, Job [j] e Job [i] non si sovrappongono, otteniamo il profitto accumulato 5 + 4 = 9 , che è maggiore di Acc_Prof [i] . Aggiorniamo Acc_Prof [i] = 9 e incrementiamo j di 1.

                                         j                   i
 
+-------------------------+---------+---------+---------+---------+---------+---------+
|          Name           |    D    |    A    |    F    |    B    |    E    |    C    |
+-------------------------+---------+---------+---------+---------+---------+---------+
|(Start Time, Finish Time)|  (1,3)  |  (2,5)  |  (4,6)  |  (6,7)  |  (5,8)  |  (7,9)  |
+-------------------------+---------+---------+---------+---------+---------+---------+
|         Profit          |    5    |    6    |    5    |    4    |    11   |    2    |
+-------------------------+---------+---------+---------+---------+---------+---------+
|         Acc_Prof        |    5    |    6    |    10   |    9    |    11   |    2    |
+-------------------------+---------+---------+---------+---------+---------+---------+

Ancora Job [j] e Job [i] non si sovrappongono. Il profitto accumulato è: 6 + 4 = 10 , che è maggiore di Acc_Prof [i] . Aggiorniamo nuovamente Acc_Prof [i] = 10 . Aumentiamo j di 1. Otteniamo:

                                                   j         i
 
+-------------------------+---------+---------+---------+---------+---------+---------+
|          Name           |    D    |    A    |    F    |    B    |    E    |    C    |
+-------------------------+---------+---------+---------+---------+---------+---------+
|(Start Time, Finish Time)|  (1,3)  |  (2,5)  |  (4,6)  |  (6,7)  |  (5,8)  |  (7,9)  |
+-------------------------+---------+---------+---------+---------+---------+---------+
|         Profit          |    5    |    6    |    5    |    4    |    11   |    2    |
+-------------------------+---------+---------+---------+---------+---------+---------+
|         Acc_Prof        |    5    |    6    |    10   |    10   |    11   |    2    |
+-------------------------+---------+---------+---------+---------+---------+---------+

Se continuiamo questo processo, dopo aver ripetuto l'intera tabella usando i , la nostra tabella sarà finalmente la seguente:

+-------------------------+---------+---------+---------+---------+---------+---------+
|          Name           |    D    |    A    |    F    |    B    |    E    |    C    |
+-------------------------+---------+---------+---------+---------+---------+---------+
|(Start Time, Finish Time)|  (1,3)  |  (2,5)  |  (4,6)  |  (6,7)  |  (5,8)  |  (7,9)  |
+-------------------------+---------+---------+---------+---------+---------+---------+
|         Profit          |    5    |    6    |    5    |    4    |    11   |    2    |
+-------------------------+---------+---------+---------+---------+---------+---------+
|         Acc_Prof        |    5    |    6    |    10   |    14   |    17   |    8    |
+-------------------------+---------+---------+---------+---------+---------+---------+

* Sono stati saltati alcuni passaggi per abbreviare il documento.

Se iteriamo attraverso l'array Acc_Prof , possiamo scoprire che il profitto massimo è 17 ! Lo pseudo-codice:

Procedure WeightedJobScheduling(Job)
sort Job according to finish time in non-decreasing order
for i -> 2 to n
    for j -> 1 to i-1
        if Job[j].finish_time <= Job[i].start_time
            if Acc_Prof[j] + Profit[i] > Acc_Prof[i]
                Acc_Prof[i] = Acc_Prof[j] + Profit[i]
            endif
        endif
    endfor
endfor

maxProfit = 0
for i -> 1 to n
    if maxProfit < Acc_Prof[i]
        maxProfit = Acc_Prof[i]
return maxProfit

La complessità del popolamento dell'array Acc_Prof è O (n 2 ). L'array traversal prende O (n) . Quindi la complessità totale di questo algoritmo è O (n 2 ).

Ora, se vogliamo scoprire quali lavori sono stati eseguiti per ottenere il massimo profitto, dobbiamo attraversare la matrice in ordine inverso e se Acc_Prof corrisponde a maxProfit , invieremo il nome del lavoro in una pila e sottrai Profitto di quel lavoro da maxProfit . Lo faremo fino a maxProfit> 0 o raggiungiamo il punto iniziale dell'array Acc_Prof . Lo pseudo-codice sarà simile a:

Procedure FindingPerformedJobs(Job, Acc_Prof, maxProfit):
S = stack()
for i -> n down to 0 and maxProfit > 0
    if maxProfit is equal to Acc_Prof[i]
        S.push(Job[i].name
        maxProfit = maxProfit - Job[i].profit
    endif
endfor

La complessità di questa procedura è: O (n) .

Una cosa da ricordare, se ci sono più piani di lavoro che possono darci il massimo profitto, possiamo trovare solo un programma di lavoro tramite questa procedura.

Modifica distanza

L'affermazione del problema è come se venissero dati due string str1 e str2, quindi quanti numeri minimi di operazioni possono essere eseguiti sullo str1 che viene convertito in str2.

Implementazione in Java

public class EditDistance {

public static void main(String[] args) {
    // TODO Auto-generated method stub
    String str1 = "march";
    String str2 = "cart";
    
    EditDistance ed = new EditDistance();
    System.out.println(ed.getMinConversions(str1, str2));
}

public int getMinConversions(String str1, String str2){
    int dp[][] = new int[str1.length()+1][str2.length()+1];
    for(int i=0;i<=str1.length();i++){
        for(int j=0;j<=str2.length();j++){
            if(i==0)
                dp[i][j] = j;
            else if(j==0)
                dp[i][j] = i;
            else if(str1.charAt(i-1) == str2.charAt(j-1))
                dp[i][j] = dp[i-1][j-1];
            else{
                dp[i][j] = 1 + Math.min(dp[i-1][j], Math.min(dp[i][j-1], dp[i-1][j-1]));
            }
        }
    }
    return dp[str1.length()][str2.length()];
}

}

Produzione

3

La più lunga successiva in comune

Se veniamo dati con le due stringhe, dobbiamo trovare la più lunga sotto-sequenza comune presente in entrambe.

Esempio

LCS per le sequenze di input "ABCDGH" e "AEDFHR" è "ADH" di lunghezza 3.

LCS per le sequenze di input "AGGTAB" e "GXTXAYB" è "GTAB" di lunghezza 4.

Implementazione in Java

public class LCS {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String str1 = "AGGTAB";
        String str2 = "GXTXAYB";
        LCS obj = new LCS();
        System.out.println(obj.lcs(str1, str2, str1.length(), str2.length()));
        System.out.println(obj.lcs2(str1, str2));
    }
    
    //Recursive function
    public int lcs(String str1, String str2, int m, int n){
        if(m==0 || n==0)
            return 0;
        if(str1.charAt(m-1) == str2.charAt(n-1))
            return 1 + lcs(str1, str2, m-1, n-1);
        else
            return Math.max(lcs(str1, str2, m-1, n), lcs(str1, str2, m, n-1));
    }
    
    //Iterative function
    public int lcs2(String str1, String str2){
        int lcs[][] = new int[str1.length()+1][str2.length()+1];
        
        for(int i=0;i<=str1.length();i++){
            for(int j=0;j<=str2.length();j++){
                if(i==0 || j== 0){
                    lcs[i][j] = 0;
                }
                else if(str1.charAt(i-1) == str2.charAt(j-1)){
                    lcs[i][j] = 1 + lcs[i-1][j-1]; 
                }else{
                    lcs[i][j] = Math.max(lcs[i-1][j], lcs[i][j-1]);
                }
            }
        }
        
        return lcs[str1.length()][str2.length()];
    }

}

Produzione

4

Numero di Fibonacci

Approccio bottom-up per la stampa dell'ennesimo numero di Fibonacci mediante la programmazione dinamica.

Albero ricorsivo

                         fib(5)   
                     /             \     
               fib(4)                fib(3)   
             /      \                /     \
         fib(3)      fib(2)         fib(2)    fib(1)
        /     \        /    \       /    \  
  fib(2)   fib(1)  fib(1) fib(0) fib(1) fib(0)
  /    \
fib(1) fib(0)

Sottosistemi sovrapposti

Qui fib (0), fib (1) e fib (3) sono i sottosistemi sovrapposti.fib (0) viene ripetuto 3 volte, fib (1) viene ripetuto 5 volte e fib (3) viene ripetuto 2 volte.

Implementazione

public int fib(int n){
        int f[] = new int[n+1];
        f[0]=0;f[1]=1;
        for(int i=2;i<=n;i++){
            f[i]=f[i-1]+f[i-2];
        }
        return f[n];
    }

Complessità del tempo

Sopra)

La sottostringa comune più lunga

Con 2 string str1 e str2 dobbiamo trovare la lunghezza della più lunga sottostringa comune tra loro.

Esempi

Input: X = "abcdxyz", y = "xyzabcd" Output: 4

La sottostringa comune più lunga è "abcd" ed è di lunghezza 4.

Input: X = "zxabcdezy", y = "yzabcdezx" Output: 6

La sottostringa comune più lunga è "abcdez" ed è di lunghezza 6.

Implementazione in Java

public int getLongestCommonSubstring(String str1,String str2){
        int arr[][] = new int[str2.length()+1][str1.length()+1];
        int max = Integer.MIN_VALUE;
        for(int i=1;i<=str2.length();i++){
            for(int j=1;j<=str1.length();j++){
                if(str1.charAt(j-1) == str2.charAt(i-1)){
                    arr[i][j] = arr[i-1][j-1]+1;
                    if(arr[i][j]>max)
                        max = arr[i][j];
                }
                else
                    arr[i][j] = 0;
            }
        }
        return max;
    }

Complessità del tempo

O (m * n)



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow