Eu tenho um conjunto de números inteiros. Eu quero encontrar a subsequência crescente mais longa desse conjunto usando programação dinâmica.
215
Eu tenho um conjunto de números inteiros. Eu quero encontrar a subsequência crescente mais longa desse conjunto usando programação dinâmica.
Respostas:
OK, descreverei primeiro a solução mais simples que é O (N ^ 2), onde N é o tamanho da coleção. Também existe uma solução O (N log N), que descreverei também. Procure aqui na seção Algoritmos eficientes.
Assumirei que os índices da matriz são de 0 a N - 1. Então, vamos definir
DP[i]
o comprimento do LIS (Maior subsequência crescente) que termina no elemento com o índicei
. Para calcularDP[i]
, analisamos todos os índicesj < i
e verificamos seDP[j] + 1 > DP[i]
earray[j] < array[i]
(queremos que ele esteja aumentando). Se isso for verdade, podemos atualizar o atual ideal paraDP[i]
. Para encontrar o melhor global para a matriz, você pode obter o valor máximoDP[0...N - 1]
.Eu uso a matriz
prev
para poder encontrar mais tarde a sequência real, não apenas seu comprimento. Basta voltar recursivamente a partirbestEnd
de um loop usandoprev[bestEnd]
. O-1
valor é um sinal para parar.OK, agora a
O(N log N)
solução mais eficiente :Vamos
S[pos]
ser definidos como o menor número inteiro que termina uma sequência crescente de comprimentopos
. Agora, percorra todos os números inteirosX
do conjunto de entrada e faça o seguinte:Se
X
> último elementoS
entrar, adicioneX
no final deS
. Essencialmente, isso significa que encontramos um novo maiorLIS
.Caso contrário, encontre o menor elemento em
S
, que é>=
thanX
e altere-o paraX
. PorqueS
é ordenada a qualquer momento, o elemento pode ser encontrado usando pesquisa binária emlog(N)
.Tempo de execução total -
N
números inteiros e uma pesquisa binária para cada um deles - N * log (N) = O (N log N)Agora vamos fazer um exemplo real:
Coleção de números inteiros:
2 6 3 4 1 2 9 5 8
Passos:
Portanto, o comprimento do LIS é
5
(o tamanho de S).Para reconstruir o real,
LIS
usaremos novamente uma matriz pai. Letparent[i]
Ser o antecessor do elemento com índicei
noLIS
final no elemento com índicei
.Para simplificar, podemos manter a matriz
S
, não os inteiros reais, mas seus índices (posições) no conjunto. Nós não mantemos{1, 2, 4, 5, 8}
, mas mantemos{4, 5, 3, 7, 8}
.Isso é entrada [4] = 1 , entrada [5] = 2 , entrada [3] = 4 , entrada [7] = 5 , entrada [8] = 8 .
Se atualizarmos corretamente a matriz pai, o LIS real será:
Agora, o importante: como atualizamos a matriz pai? Existem duas opções:
Se
X
> último elemento entrarS
, entãoparent[indexX] = indexLastElement
. Isso significa que o pai do elemento mais novo é o último elemento. Nós apenas anexamosX
ao final deS
.Caso contrário, encontre o índice do menor elemento em
S
, que é>=
thanX
e altere-o paraX
. Aquiparent[indexX] = S[index - 1]
.fonte
DP[j] + 1 == DP[i]
, em seguida,DP[i]
não vai se tornar melhor comDP[i] = DP[j] + 1
. Estamos tentando otimizarDP[i]
.[1,2,5,8]
: 4 vem antes de 1 na matriz, como pode ser o LIS[1,2,4,5,8]
?[2,3,4,5,8]
. Leia com atenção - aS
matrizDOES NOT
representa uma sequência real.Let S[pos] be defined as the smallest integer that ends an increasing sequence of length pos.
A explicação de Petar Minchev ajudou a esclarecer as coisas para mim, mas era difícil analisar o que era tudo, então fiz uma implementação em Python com nomes de variáveis excessivamente descritivos e muitos comentários. Eu fiz uma solução recursiva ingênua, a solução O (n ^ 2) e a solução O (n log n).
Espero que ajude a esclarecer os algoritmos!
A solução recursiva
A solução de programação dinâmica O (n ^ 2)
A solução de programação dinâmica O (n log n)
fonte
bisect
. Para demonstrar como um algoritmo funciona e suas características de desempenho, eu estava tentando manter as coisas o mais primitivas possível.Falando sobre a solução DP, achei surpreendente que ninguém mencionasse o fato de que o LIS pode ser reduzido a LCS . Tudo o que você precisa fazer é classificar a cópia da sequência original, remover todas as duplicatas e executar o LCS. No pseudocódigo, é:
E a implementação completa escrita em Go. Você não precisa manter toda a matriz n ^ 2 DP se não precisar reconstruir a solução.
fonte
A implementação C ++ a seguir inclui também algum código que cria a subsequência crescente mais longa real usando uma matriz chamada
prev
.Implementação sem pilha apenas inverta o vetor
fonte
Aqui estão três etapas para avaliar o problema do ponto de vista da programação dinâmica:
Se tomarmos como exemplo uma sequência {0, 8, 2, 3, 7, 9}, no índice:
Aqui está o código C ++ 11 funcional:
fonte
Aqui está uma implementação Scala do algoritmo O (n ^ 2):
fonte
Aqui está outra implementação O (n ^ 2) JAVA. Nenhuma recursão / memorização para gerar a subsequência real. Apenas uma matriz de strings que armazena o LIS real em cada estágio e uma matriz para armazenar o comprimento do LIS para cada elemento. Muito fácil. Dar uma olhada:
Código em ação: http://ideone.com/sBiOQx
fonte
Isso pode ser resolvido em O (n ^ 2) usando a programação dinâmica. Código Python para o mesmo seria como: -
Para entrada:
5 19 5 81 50 28 29 1 83 23
saída seria:
[1, 2, 1, 3, 3, 3, 4, 1, 5, 3] 5
O list_index da lista de saída é o list_index da lista de entrada. O valor em um determinado list_index na lista de saída indica o maior comprimento de subsequência crescente para esse list_index.
fonte
aqui está a implementação do java O (nlogn)
fonte
Esta é uma implementação Java em O (n ^ 2). Eu apenas não usei a Pesquisa binária para encontrar o menor elemento em S, que é> = que X. Eu apenas usei um loop for. Usar a Pesquisa Binária tornaria a complexidade em O (n logn)
fonte
faça o checkout do código em java para maior subsequência crescente com os elementos da matriz
http://ideone.com/Nd2eba
fonte
Isso pode ser resolvido em O (n ^ 2) usando programação dinâmica.
Processe os elementos de entrada em ordem e mantenha uma lista de tuplas para cada elemento. Cada tupla (A, B), para o elemento i, denota, A = comprimento da subseqüência crescente mais longa que termina em ie B = índice do predecessor da lista [i] na subseqüência crescente mais longa que termina na lista [i ]
A partir do elemento 1, a lista de tupla para o elemento 1 será [(1,0)] para o elemento i, verifique a lista 0..i e encontre a lista de elementos [k] de forma que a lista [k] <lista [i] , o valor de A para o elemento i, Ai será Ak + 1 e Bi será k. Se houver vários desses elementos, adicione-os à lista de tuplas do elemento i.
No final, encontre todos os elementos com valor máximo de A (comprimento do LIS terminado no elemento) e volte usando as tuplas para obter a lista.
Compartilhei o código para o mesmo em http://www.edufyme.com/code/?id=66f041e16a60928b05a7e228a89c3799
fonte
Implementação O (n ^ 2) java:
fonte
mesmo que exista uma maneira pela qual você possa resolver isso no tempo O (nlogn) (isso resolve no tempo O (n ^ 2)), mas ainda assim dessa maneira forneça a abordagem de programação dinâmica que também é boa.
fonte
Aqui está minha solução Leetcode usando a Pesquisa binária: ->
fonte
Solução LIS mais simples em C ++ com complexidade de tempo O (nlog (n))
SAÍDA:
4
fonte
Maior sequência crescente (Java)
fonte
Eu implementei o LIS em java usando programação dinâmica e memorização. Junto com o código, fiz o cálculo da complexidade, ou seja, por que é O (n Log (base2) n). Na minha opinião, explicações teóricas ou lógicas são boas, mas a demonstração prática é sempre melhor para a compreensão.
Enquanto eu corria o código acima -
fonte