Diferença entre dividir e conquistar Algo e programação dinâmica

140

Qual é a diferença entre os algoritmos de divisão e conquista e os algoritmos de programação dinâmica? Como os dois termos são diferentes? Eu não entendo a diferença entre eles.

Por favor, tome um exemplo simples para explicar qualquer diferença entre os dois e em que base eles parecem semelhantes.

saplingPro
fonte

Respostas:

156

Dividir e conquistar

Divide and Conquer funciona dividindo o problema em subproblemas, conquiste cada subproblema recursivamente e combine essas soluções.

Programaçao dinamica

A programação dinâmica é uma técnica para resolver problemas com subproblemas sobrepostos. Cada subproblema é resolvido apenas uma vez e o resultado de cada subproblema é armazenado em uma tabela (geralmente implementada como uma matriz ou tabela de hash) para futuras referências. Essas sub-soluções podem ser usadas para obter a solução original e a técnica de armazenamento das soluções do sub-problema é conhecida como memorização.

Você pode pensar em DP = recursion + re-use

Um exemplo clássico para entender a diferença seria ver essas duas abordagens para obter o enésimo número de fibonacci. Verifique este material do MIT.


Abordagem de dividir e conquistar Abordagem de dividir e conquistar

Abordagem de programação dinâmica insira a descrição da imagem aqui

OneMoreError
fonte
9
como você fez as imagens? usando o mouse?
21413 Vihaan Verma
34
Eu acho que a linha mais importante em toda essa resposta é a seguinte: "subproblemas sobrepostos". DP tem, dividir e conquistar não #
Hasan Iqbal
@HasanIqbalAnik Subproblema sobreposto significa um problema que ocorre repetidamente. Como resolver o fn-2 no exemplo mostrado acima. Portanto, na D&C existe e é por isso que não é tão eficiente quanto a DP.
Meena Chaudhary
1
Estranho! 'Subproblemas sobrepostos' você está falando sobre o problema, mas 'programação dinâmica' é um tipo de algoritmo. Eu acho que é importante distinguir 'problemas' e 'algoritmos'.
ZHU
Sim, o DP memoriza as partes sobrepostas para obter uma vantagem sobre Divide and Conquer.
imagineerThat
25

A outra diferença entre dividir e conquistar e a programação dinâmica pode ser:

Dividir e conquistar:

  1. Faz mais trabalho nos subproblemas e, portanto, tem mais consumo de tempo.
  2. Ao dividir e conquistar, os subproblemas são independentes um do outro.

Programaçao dinamica:

  1. Resolve os subproblemas apenas uma vez e os armazena na tabela.
  2. Na programação dinâmica, o subproblema não é independente.
ASHWINI KOLEKAR
fonte
Os algoritmos de divisão e conquista não necessariamente funcionam mais do que suas alternativas de DP. Um exemplo é o algoritmo de Erickson para encontrar progressões aritméticas máximas.
Michael Foukarakis 6/06/2016
17

às vezes, ao programar recursivamente, você chama a função com os mesmos parâmetros várias vezes, o que não é necessário.

O famoso exemplo de números 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)
}

Vamos executar o 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

Então chamamos: 1 vezes F (4) 2 vezes F (3) 3 vezes F (2) 2 vezes F (1)

Abordagem de programação dinâmica: se você chamar uma função com o mesmo parâmetro mais de uma vez, salve o resultado em uma variável para acessá-lo diretamente na próxima vez. A maneira iterativa:

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

Vamos chamar F (5) novamente:

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

Como você pode ver, sempre que precisar da chamada múltipla, basta acessar a variável correspondente para obter o valor em vez de recalculá-lo.

A propósito, programação dinâmica não significa converter um código recursivo em um código iterativo. Você também pode salvar os sub-resultados em uma variável se desejar um código recursivo. Nesse caso, a técnica é chamada de memorização. Para o nosso exemplo, fica assim:

// 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]                
    }
}

Portanto, o relacionamento com o Divide and Conquer é que os algoritmos de D&D dependem de recursão. E algumas versões deles têm essa "chamada de função múltipla com o mesmo problema de parâmetro". Procure por "multiplicação da cadeia da matriz" e "subsequência comum mais longa" para exemplos onde DP é necessário para melhorar o T (n) de algo de D&D.

AB
fonte
17

Programação dinâmica e semelhanças de dividir e conquistar

Pelo que vejo por enquanto, posso dizer que a programação dinâmica é uma extensão do paradigma de dividir e conquistar .

Eu não os trataria como algo completamente diferente. Como ambos trabalham dividindo recursivamente um problema em dois ou mais subproblemas do mesmo tipo ou relacionados, até que se tornem simples o suficiente para serem resolvidos diretamente. As soluções para os subproblemas são então combinadas para fornecer uma solução para o problema original.

Então, por que ainda temos nomes de paradigmas diferentes e por que chamei a programação dinâmica de extensão? Isso ocorre porque a abordagem de programação dinâmica pode ser aplicada ao problema apenas se o problema tiver certas restrições ou pré-requisitos . Depois disso, a programação dinâmica amplia a abordagem de dividir e conquistar com a técnica de memorização ou tabulação .

Vamos passo a passo ...

Pré-requisitos / restrições de programação dinâmica

Como acabamos de descobrir, existem dois atributos principais que os problemas de divisão e conquista devem ter para que a programação dinâmica seja aplicável:

  • Subestrutura  ideal - a solução ideal pode ser construída a partir de soluções ideais de seus subproblemas

  • Subproblemas sobrepostos  - o problema pode ser dividido em subproblemas que são reutilizados várias vezes ou um algoritmo recursivo para o problema resolve o mesmo subproblema repetidamente, em vez de sempre gerar novos subproblemas

Uma vez atendidas essas duas condições, podemos dizer que esse problema de divisão e conquista pode ser resolvido usando a abordagem de programação dinâmica.

Extensão de programação dinâmica para dividir e conquistar

A abordagem de programação dinâmica estende a abordagem de dividir e conquistar com duas técnicas ( memorização e tabulação ), ambas com o objetivo de armazenar e reutilizar soluções de subproblemas que podem melhorar drasticamente o desempenho. Por exemplo, a implementação recursiva ingênua da função Fibonacci possui complexidade de tempo em O(2^n)que a solução DP faz o mesmo com apenas O(n)tempo.

Memoização (preenchimento de cache de cima para baixo) refere-se à técnica de armazenamento em cache e reutilização de resultados calculados anteriormente. A fibfunção memorizada ficaria assim:

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]
}

A tabulação (preenchimento de cache de baixo para cima) é semelhante, mas se concentra no preenchimento das entradas do cache. A computação dos valores no cache é mais fácil de forma iterativa. A versão de tabulação fibseria assim:

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

Você pode ler mais sobre memorização e comparação de tabulação aqui .

A idéia principal que você deve entender aqui é que, como nosso problema de divisão e conquista tem subproblemas sobrepostos, o armazenamento em cache de soluções de subproblemas se torna possível e, assim, a memorização / tabulação entra em cena.

Então qual é a diferença entre DP e DC?

Como agora estamos familiarizados com os pré-requisitos de DP e suas metodologias, estamos prontos para colocar tudo o que foi mencionado acima em uma imagem.

Programação dinâmica vs dividir e conquistar

Se você quiser ver exemplos de código, pode dar uma olhada em explicações mais detalhadas aqui, onde encontrará dois exemplos de algoritmos: Pesquisa binária e distância mínima de edição (distância de Levenshtein) que ilustram a diferença entre DP e DC.

Oleksii Trekhleb
fonte
1
Offtopic: Você usou uma mesa digitalizadora para desenhar isso?
precisa
1
@GeonGeorge não, o desenho foi feito por caneta e, em seguida, digitalizado
Oleksii Trekhleb
esta é uma das melhores respostas que eu li sobre a organização do DP
Ridhwaan Shakeel
8

Suponho que você já tenha lido a Wikipedia e outros recursos acadêmicos sobre isso, então não reciclarei nenhuma dessas informações. Também devo ressaltar que não sou especialista em ciência da computação, mas vou compartilhar meus dois centavos na minha compreensão desses tópicos ...

Programaçao dinamica

Divide o problema em subproblemas discretos. O algoritmo recursivo para a sequência de Fibonacci é um exemplo de Programação Dinâmica, porque resolve para fib (n) resolvendo primeiro para fib (n-1). Para resolver o problema original, ele resolve um problema diferente .

Dividir e conquistar

Esses algoritmos geralmente resolvem partes semelhantes do problema e, em seguida, as colocam juntas no final. Mergesort é um exemplo clássico de dividir e conquistar. A principal diferença entre este exemplo e o exemplo de Fibonacci é que, em uma fusão, a divisão pode (teoricamente) ser arbitrária e, não importa como você a divide, você ainda está mesclando e classificando. A mesma quantidade de trabalho deve ser feita para mesclar a matriz, não importa como você a divide. A resolução da fib (52) requer mais etapas do que a resolução da fib (2).

parker.sikand
fonte
5

Penso Divide & Conquercomo uma abordagem recursiva e Dynamic Programmingcomo preenchimento de tabelas.

Por exemplo, Merge Sorté um Divide & Conqueralgoritmo, pois em cada etapa, você divide a matriz em duas metades, invoca recursivamente Merge Sortas duas metades e as mescla.

Knapsacké um Dynamic Programmingalgoritmo, pois você está preenchendo uma tabela que representa as melhores soluções para os subproblemas da mochila em geral. Cada entrada na tabela corresponde ao valor máximo que você pode carregar em uma bolsa de peso com os itens 1-j.

ehuang
fonte
1
Embora isso seja verdade para muitos casos, nem sempre é verdade que armazenamos os resultados dos subproblemas em uma tabela.
Gokul
2

Divide and Conquer envolve três etapas em cada nível de recursão:

  1. Divida o problema em subproblemas.
  2. Conquiste os subproblemas, resolvendo-os recursivamente.
  3. Combine a solução para subproblemas na solução para o problema original.
    • É uma abordagem de cima para baixo .
    • Ele trabalha mais em subproblemas e, portanto, tem mais consumo de tempo.
    • por exemplo. O n-ésimo termo da série de Fibonacci pode ser computado na complexidade do tempo O (2 ^ n).

A programação dinâmica envolve as quatro etapas a seguir:

1. Caracterize a estrutura das soluções ideais.
2. Defina recursivamente os valores das soluções ideais.
3. Calcule o valor das soluções ideais.
4. Construa uma solução ideal a partir de informações computadas .

  • É uma abordagem de baixo para cima .
  • Menos consumo de tempo do que dividir e conquistar, pois usamos os valores calculados anteriormente, em vez de computar novamente.
  • por exemplo. O nono termo da série Fibonacci pode ser calculado em O (n) complexidade do tempo.

Para um entendimento mais fácil, vamos ver dividir e conquistar como uma solução de força bruta e sua otimização como programação dinâmica.

Os algoritmos de divisão e conquista de notas com subproblemas sobrepostos só podem ser otimizados com dp.

Neel Alex
fonte
Dividir e conquistar é de baixo para cima e a programação dinâmica é de cima para baixo
Bahgat Mashaly
0
  • Dividir e conquistar
    • Eles invadiram subproblemas não sobrepostos
    • Exemplo: números fatoriais ie fato (n) = n * fato (n-1)
fact(5) = 5* fact(4) = 5 * (4 * fact(3))= 5 * 4 * (3 *fact(2))= 5 * 4 * 3 * 2 * (fact(1))

Como podemos ver acima, nenhum fato (x) é repetido, de modo que o fatorial tem problemas não sobrepostos.

  • Programaçao dinamica
    • Eles entraram em sub-problemas sobrepostos
    • Exemplo: números de Fibonacci, ou seja, fib (n) = fib (n-1) + fib (n-2)
fib(5) = fib(4) + fib(3) = (fib(3)+fib(2)) + (fib(2)+fib(1))

Como podemos ver acima, fib (4) e fib (3) usam ambos fib (2). Da mesma forma, muitos fib (x) são repetidos. é por isso que Fibonacci tem subproblemas sobrepostos.

  • Como resultado da repetição do subproblema no DP, podemos manter esses resultados em uma tabela e economizar esforço de computação. isso é chamado de memorização
ankit.rana
fonte