web-dev-qa-db-fra.com

Différence entre Divide and Conquer Algo et programmation dynamique

Quelle est la différence entre les algorithmes Divide and Conquer et les algorithmes de programmation dynamique? En quoi les deux termes sont-ils différents? Je ne comprends pas la différence entre eux.

Veuillez prendre un exemple simple pour expliquer toute différence entre les deux et pour quel motif elles semblent similaires.

122
saplingPro

Divide and Conquer

Diviser et conquérir fonctionne en divisant le problème en sous-problèmes, en conquérant chaque sous-problème de manière récursive et en combinant ces solutions.

Programmation dynamique

La programmation dynamique est une technique permettant de résoudre les problèmes de sous-problèmes qui se chevauchent. Chaque sous-problème n'est résolu qu'une fois et le résultat de chaque sous-problème est stocké dans une table (généralement implémentée sous la forme d'un tableau ou d'une table de hachage) pour des références futures. Ces sous-solutions peuvent être utilisées pour obtenir la solution d'origine et la technique de stockage de ces solutions est connue sous le nom de mémorisation.

Vous pouvez penser à DP = recursion + re-use

Un exemple classique pour comprendre la différence serait de voir ces deux approches pour obtenir le nième nombre de fibonacci. Vérifiez ceci matériel du MIT.


Approche Divide and Conquer  Divide and Conquer approach

Approche de programmation dynamique  enter image description here

136
OneMoreError

L’autre différence entre diviser pour régner et programmation dynamique pourrait être:

Diviser et conquérir:

  1. Fait plus de travail sur les sous-problèmes et a donc plus de temps.
  2. Dans diviser et conquérir les sous-problèmes sont indépendants les uns des autres.

Programmation dynamique:

  1. Résout les sous-problèmes une seule fois, puis les stocke dans la table.
  2. En programmation dynamique, les sous-problèmes ne sont pas indépendants.
22
ASHWINI KOLEKAR

parfois, lorsque vous programmez de manière récursive, vous appelez la fonction avec les mêmes paramètres plusieurs fois, ce qui est inutile.

Le fameux exemple de Fibonacci:

           index: 1,2,3,4,5,6...
Fibonacci number: 1,1,2,3,5,8...

function F(n) {
    if (n < 3)
        return 1
    else
        return F(n-1) + F(n-2)
}

Courons F (5):

F(5) = F(4) + F(3)
     = {F(3)+F(2)} + {F(2)+F(1)}
     = {[F(2)+F(1)]+1} + {1+1}
     = 1+1+1+1+1

Nous avons donc appelé: 1 fois F(4) 2 fois F(3) 3 fois F(2) 2 fois F (1)

Approche de programmation dynamique: si vous appelez une fonction avec le même paramètre plusieurs fois, enregistrez le résultat dans une variable pour y accéder directement lors de la prochaine fois. La manière itérative:

if (n==1 || n==2)
    return 1
else
    f1=1, f2=1
    for i=3 to n
         f = f1 + f2
         f1 = f2
         f2 = f

Appelons F(5) encore:

fibo1 = 1
fibo2 = 1 
fibo3 = (fibo1 + fibo2) = 1 + 1 = 2
fibo4 = (fibo2 + fibo3) = 1 + 2 = 3
fibo5 = (fibo3 + fibo4) = 2 + 3 = 5

Comme vous pouvez le constater, chaque fois que vous avez besoin d'appels multiples, vous n'avez qu'à accéder à la variable correspondante pour obtenir la valeur au lieu de la recalculer.

À propos, la programmation dynamique ne signifie pas convertir un code récursif en un code itératif. Vous pouvez également enregistrer les sous-résultats dans une variable si vous souhaitez un code récursif. Dans ce cas, la technique s'appelle la mémorisation. Pour notre exemple, il ressemble à ceci:

// declare and initialize a dictionary
var dict = new Dictionary<int,int>();
for i=1 to n
    dict[i] = -1

function F(n) {
    if (n < 3)
        return 1
    else
    {
        if (dict[n] == -1)
            dict[n] = F(n-1) + F(n-2)

        return dict[n]                
    }
}

Donc, la relation avec Divide and Conquer est que les algorithmes de D & D reposent sur la récursion. Et certaines versions d’entre elles ont cet "appel à plusieurs fonctions avec le même problème de paramètre". Recherchez "multiplication de chaîne matricielle" et "sous-séquence la plus longue commune" pour de tels exemples où DP est nécessaire pour améliorer le T(n) de D & D algo.

17
A.B.

Programmation dynamique et similarités Divide-and-Conquer

Comme je le vois pour le moment, je peux dire que la programmation dynamique est une extension du paradigme de diviser pour régner .

Je ne les traiterais pas comme quelque chose de complètement différent. Parce que , ils travaillent tous les deux en décomposant récursivement un problème en deux sous-problèmes ou plus du même type ou d'un type lié, jusqu'à ce que ceux-ci deviennent suffisamment simples pour être résolus directement. Les solutions aux sous-problèmes sont ensuite combinées pour donner une solution au problème initial.

Alors pourquoi avons-nous toujours des noms de paradigmes différents alors et pourquoi j’ai appelé la programmation dynamique une extension. En effet, une approche de programmation dynamique peut être appliquée au problème uniquement si le problème comporte certaines restrictions ou conditions préalables . Et après cette programmation dynamique, élargit l’approche diviser pour mieux régner avec mémoization ou tabulation technique.

Allons-y pas à pas…

Programmation dynamique Prérequis/Restrictions

Comme nous venons de le découvrir, il existe deux attributs clés que le problème de division et de conquête doit avoir pour que la programmation dynamique soit applicable:

  • Sous-structure optimale - une solution optimale peut être construite à partir de solutions optimales de ses sous-problèmes

  • Chevauchement de sous-problèmes - le problème peut être divisé en sous-problèmes réutilisés plusieurs fois ou un algorithme récursif du problème résout le même sous-problème plusieurs fois. que toujours générer de nouveaux sous-problèmes

Une fois que ces deux conditions sont remplies, nous pouvons dire que ce problème de division et de conquête peut être résolu en utilisant une approche de programmation dynamique.

Extension de programmation dynamique pour Divide and Conquer

L’approche de programmation dynamique étend l’approche diviser pour régner avec deux techniques ( la mémoisation et la tabulation ) qui ont tous deux pour but de stocker et de réutiliser des solutions de sous-problèmes susceptibles d’améliorer considérablement les performances. Par exemple, l'implémentation récursive naïve de la fonction de Fibonacci a une complexité temporelle de O(2^n), où la solution DP effectue la même chose avec seulement O(n) temps.

La mémorisation (remplissage du cache descendant) fait référence à la technique de mise en cache et de réutilisation des résultats précédemment calculés. La fonction mémoisée fib ressemblerait donc à ceci:

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 tabulation (remplissage du cache de bas en haut) est similaire mais se concentre sur le remplissage des entrées du cache. Le calcul des valeurs dans le cache est plus facile à effectuer de manière itérative. La version de tabulation de fib ressemblerait à ceci:

tabFib(n) {
    mem[0] = 0
    mem[1] = 1
    for i = 2...n
        mem[i] = mem[i-2] + mem[i-1]
    return mem[n]
}

Vous pouvez en savoir plus sur la mémoisation et la comparaison de tabulation ici .

L'idée principale que vous devez comprendre ici est que parce que notre problème de division et de conquête a des sous-problèmes qui se chevauchent, la mise en cache de solutions de sous-problèmes devient possible et donc la mémorisation/la tabulation s'intensifie sur la scène.

Alors, quelle est la différence entre DP et DC Après tout

Étant donné que nous connaissons maintenant les conditions préalables et les méthodologies de DP, nous sommes prêts à mettre en une seule photo tout ce qui a été mentionné ci-dessus.

Dynamic Programming vs Divide-and-Conquer

Si vous voulez voir des exemples de code, vous pouvez consulter explication plus détaillée ici où vous trouverez deux exemples d'algorithmes: Recherche binaire et Distance minimale d'édition (Distance de Levenshtein) illustrant la différence entre les points de vente. et DC.

9
Oleksii Trekhleb

Je suppose que vous avez déjà lu Wikipedia et d’autres ressources académiques à ce sujet. Je ne vais donc pas recycler ces informations. Je dois également préciser que je ne suis en aucun cas un expert en informatique, mais je partagerai mes deux cents sur ma compréhension de ces sujets ...

Programmation dynamique

Décompose le problème en sous-problèmes distincts. L'algorithme récursif de la séquence de Fibonacci est un exemple de programmation dynamique car il résout pour fib (n) en résolvant d'abord pour fib (n-1). Afin de résoudre le problème initial, il résout un problème différent.

Diviser et conquérir

Ces algorithmes résolvent généralement des parties similaires du problème, puis les rassemblent à la fin. Mergesort est un exemple classique de division et de conquête. La principale différence entre cet exemple et l'exemple de Fibonacci réside dans le fait que, dans un mergesort, la division peut (théoriquement) être arbitraire et que, quelle que soit la manière dont vous la découpez, vous continuez à fusionner et à trier. La même quantité de travail doit être effectuée pour fusionner le tableau, quelle que soit la manière dont vous le divisez. Résoudre pour fib (52) nécessite plus d’étapes que résoudre pour fib (2).

8
parker.sikand

Je pense que Divide & Conquer Est une approche récursive et que Dynamic Programming Est un remplissage de table.

Par exemple, Merge Sort Est un algorithme Divide & Conquer. Dans chaque étape, vous divisez le tableau en deux moitiés, appelez récursivement Merge Sort Sur les deux moitiés, puis vous les fusionnez.

Knapsack est un algorithme Dynamic Programming, car vous remplissez un tableau représentant des solutions optimales aux sous-problèmes de l'ensemble du sac à dos. Chaque entrée dans le tableau correspond à la valeur maximale que vous pouvez emporter dans un sac de poids w les éléments 1-j donnés.

5
ehuang