J'ai un ensemble d'entiers. Je veux trouver la sous- séquence croissante la plus longue de cet ensemble en utilisant la programmation dynamique.
215
J'ai un ensemble d'entiers. Je veux trouver la sous- séquence croissante la plus longue de cet ensemble en utilisant la programmation dynamique.
Réponses:
OK, je vais d'abord décrire la solution la plus simple qui est O (N ^ 2), où N est la taille de la collection. Il existe également une solution O (N log N), que je décrirai également. Recherchez-le ici dans la section Algorithmes efficaces.
Je suppose que les indices du tableau sont de 0 à N - 1. Définissons
DP[i]
donc la longueur de la LIS (sous-séquence croissante la plus longue) qui se termine à l'élément avec l'indexi
. Pour calculer,DP[i]
nous regardons tous les indicesj < i
et vérifions à la fois siDP[j] + 1 > DP[i]
etarray[j] < array[i]
(nous voulons qu'il augmente). Si cela est vrai, nous pouvons mettre à jour l'optimum actuel pourDP[i]
. Pour trouver l'optimum global pour la matrice, vous pouvez prendre la valeur maximale deDP[0...N - 1]
.J'utilise le tableau
prev
pour pouvoir plus tard trouver la séquence réelle non seulement sa longueur. Il suffit de revenir récursivementbestEnd
dans une boucle en utilisantprev[bestEnd]
. La-1
valeur est un signe d'arrêt.OK, maintenant à la
O(N log N)
solution la plus efficace :Soit
S[pos]
défini comme le plus petit entier qui termine une séquence croissante de longueurpos
. Maintenant, parcourez chaque entierX
de l'ensemble d'entrée et procédez comme suit:Si
X
> dernier élément dansS
, ajoutezX
à la fin deS
. Cela signifie essentiellement que nous avons trouvé un nouveau plus grandLIS
.Sinon, recherchez le plus petit élément dans
S
, qui est>=
queX
, et changez-le enX
. Parce qu'ilS
est trié à tout moment, l'élément peut être trouvé à l'aide de la recherche binaire danslog(N)
.Runtime total -
N
entiers et recherche binaire pour chacun d'eux - N * log (N) = O (N log N)Faisons maintenant un vrai exemple:
Collection d'entiers:
2 6 3 4 1 2 9 5 8
Pas:
La longueur du LIS est donc
5
(la taille de S).Pour reconstruire le réel,
LIS
nous utiliserons à nouveau un tableau parent. Soitparent[i]
le prédécesseur de l'élément avec indexi
à laLIS
fin de l'élément avec indexi
.Pour simplifier les choses, nous pouvons conserver dans le tableau
S
, non pas les entiers réels, mais leurs indices (positions) dans l'ensemble. Nous ne gardons pas{1, 2, 4, 5, 8}
, mais gardons{4, 5, 3, 7, 8}
.C'est-à-dire entrée [4] = 1 , entrée [5] = 2 , entrée [3] = 4 , entrée [7] = 5 , entrée [8] = 8 .
Si nous mettons à jour correctement le tableau parent, le LIS réel est:
Passons maintenant à l'important - comment mettre à jour le tableau parent? Il y a deux options:
Si
X
> dernier élément dansS
, alorsparent[indexX] = indexLastElement
. Cela signifie que le parent de l'élément le plus récent est le dernier élément. Nous ajoutons justeX
à la fin deS
.Sinon, recherchez l'index du plus petit élément dans
S
, qui est>=
queX
, et changez-le enX
. Iciparent[indexX] = S[index - 1]
.la source
DP[j] + 1 == DP[i]
alorsDP[i]
ne deviendra pas meilleur avecDP[i] = DP[j] + 1
. Nous essayons d'optimiserDP[i]
.[1,2,5,8]
: 4 vient avant 1 dans le tableau, comment le LIS peut[1,2,4,5,8]
-il être ?[2,3,4,5,8]
. Lisez attentivement - leS
tableauDOES NOT
représente une séquence réelle.Let S[pos] be defined as the smallest integer that ends an increasing sequence of length pos.
L'explication de Petar Minchev m'a aidé à clarifier les choses, mais il était difficile pour moi d'analyser ce que tout était, alors j'ai fait une implémentation Python avec des noms de variables trop descriptifs et beaucoup de commentaires. J'ai fait une solution récursive naïve, la solution O (n ^ 2) et la solution O (n log n).
J'espère que cela aide à clarifier les algorithmes!
La solution récursive
La solution de programmation dynamique O (n ^ 2)
La solution de programmation dynamique O (n log n)
la source
bisect
. Pour une démonstration du fonctionnement d'un algorithme et de ses caractéristiques de performance, j'essayais de garder les choses aussi primitives que possible.En parlant de la solution DP, j'ai trouvé surprenant que personne n'ait mentionné le fait que LIS puisse être réduit à LCS . Tout ce que vous avez à faire est de trier la copie de la séquence d'origine, de supprimer tous les doublons et d'en faire LCS. En pseudocode c'est:
Et l'implémentation complète écrite en Go. Vous n'avez pas besoin de conserver l'intégralité de la matrice n ^ 2 DP si vous n'avez pas besoin de reconstruire la solution.
la source
L'implémentation C ++ suivante inclut également du code qui crée la sous- séquence croissante la plus longue réelle à l' aide d'un tableau appelé
prev
.Implémentation sans pile inversez simplement le vecteur
la source
Voici trois étapes pour évaluer le problème du point de vue de la programmation dynamique:
Si nous prenons comme exemple une séquence {0, 8, 2, 3, 7, 9}, à l'index:
Voici le code C ++ 11 fonctionnel:
la source
Voici une implémentation Scala de l'algorithme O (n ^ 2):
la source
Voici une autre implémentation JAVA O (n ^ 2). Aucune récursivité / mémorisation pour générer la sous-séquence réelle. Juste un tableau de chaînes qui stocke le LIS réel à chaque étape et un tableau pour stocker la longueur du LIS pour chaque élément. C'est sacrément facile. Regarde:
Code en action: http://ideone.com/sBiOQx
la source
Cela peut être résolu dans O (n ^ 2) à l'aide de la programmation dynamique. Le code Python pour le même serait comme: -
Pour l'entrée:
5 19 5 81 50 28 29 1 83 23
la sortie serait:
[1, 2, 1, 3, 3, 3, 4, 1, 5, 3] 5
Le list_index de la liste de sortie est le list_index de la liste d'entrée. La valeur à un list_index donné dans la liste de sortie indique la plus longue longueur de sous-séquence croissante pour ce list_index.
la source
voici l'implémentation java O (nlogn)
la source
Il s'agit d'une implémentation Java en O (n ^ 2). Je n'ai tout simplement pas utilisé la recherche binaire pour trouver le plus petit élément de S, qui est> = que X. J'ai juste utilisé une boucle for. L'utilisation de la recherche binaire rendrait la complexité à O (n logn)
la source
extraire le code en java pour la plus longue sous-séquence croissante avec les éléments du tableau
http://ideone.com/Nd2eba
la source
Cela peut être résolu dans O (n ^ 2) en utilisant la programmation dynamique.
Traitez les éléments d'entrée dans l'ordre et conservez une liste de tuples pour chaque élément. Chaque tuple (A, B), pour l'élément i désigne, A = longueur de la sous-séquence croissante la plus longue se terminant en i et B = indice du prédécesseur de la liste [i] dans la sous-séquence croissante la plus longue se terminant en la liste [i ].
À partir de l'élément 1, la liste des tuple pour l'élément 1 sera [(1,0)] pour l'élément i, parcourez la liste 0..i et trouvez la liste d'éléments [k] telle que la liste [k] <liste [i] , la valeur de A pour l'élément i, Ai sera Ak + 1 et Bi sera k. S'il existe plusieurs éléments de ce type, ajoutez-les à la liste des tuples pour l'élément i.
À la fin, trouvez tous les éléments avec une valeur maximale de A (longueur de LIS se terminant à l'élément) et revenez en arrière en utilisant les tuples pour obtenir la liste.
J'ai partagé le code pour le même à http://www.edufyme.com/code/?id=66f041e16a60928b05a7e228a89c3799
la source
Implémentation Java O (n ^ 2):
la source
même s'il existe un moyen de résoudre ce problème en temps O (nlogn) (cela résout en temps O (n ^ 2)), mais cela donne néanmoins l'approche de programmation dynamique qui est également bonne.
la source
Voici ma solution Leetcode en utilisant la recherche binaire: ->
la source
Solution LIS la plus simple en C ++ avec complexité temporelle O (nlog (n))
SORTIE:
4
la source
Conséquence croissante la plus longue (Java)
la source
J'ai implémenté LIS en java en utilisant la programmation dynamique et la mémorisation. Avec le code, j'ai fait un calcul de complexité, c'est-à-dire pourquoi c'est O (n Log (base2) n). Comme je pense que les explications théoriques ou logiques sont bonnes mais la démonstration pratique est toujours meilleure pour la compréhension.
Pendant que je courais le code ci-dessus -
la source
L'approche O (NLog (N)) pour trouver la sous-séquence croissante la plus longue
Maintenons un tableau où le ième élément est le plus petit nombre possible avec lequel une sous-séquence de taille ai peut se terminer.
J'évite volontairement d'autres détails car la réponse la plus votée l'explique déjà, mais cette technique conduit finalement à une implémentation soignée utilisant la structure de données définie (au moins en c ++).
Voici l'implémentation en c ++ (en supposant qu'une augmentation stricte de la taille de la sous-séquence la plus longue est requise)
la source