[Haskell-cafe] Re: Longest increasing subsequence

ChrisK haskell at list.mightyreason.com
Thu Apr 10 20:18:38 EDT 2008

It is late, but I was not sleepy enough, so here is my first translation of the 
algorithm to a functional approach...

{- Quote wikipedia: http://en.wikipedia.org/wiki/Longest_increasing_subsequence

  L = 0
  M[0] = 0
  for i = 1, 2, ... n:
     binary search for the largest j ≤ L such that X[M[j]] < X[i] (or set j = 0 
if no such value exists)
     P[i] = M[j]
     if j == L or X[i] < X[M[j+1]]:
        M[j+1] = i
        L = max(L, j+1)

X[i] defined for i = 1,2,3…
So X[0] is not defined.
Now, rethink '0' as Nothing, and 1≤j≤L since X[M[0]] is also undefined.

Not that after the binary search that one the three conditions holds:

X[i] ≤ X[M[1]]
   "The same or a new minimum value"
   P[i] is created and set to Nothing
   If X[i] < X[M[1]] then M[1] is changed to i

X[M[j]] < X[i] ≤ X[M[j+1]] for some j<L
   "A value greater than the minimum and equal or less than the maximum"
   P[i] is set to Just M[j]
   If X[i] == X[M[j+1]] then there is no need to update M[j+1]
   If X[i] < X[M[j+1]] then M[j+1] is changed to i

X[M[L]] < X[i]
   "A new maximum value"
   P[i] is set to Just M[L]
   M[L+1] is created and set to i
   L is set to L+1

Wikipedia is too loose.  X[M[1]], X[M[2]], …, X[M[L]] is not "nondecreasing", 
but must be strictly increasing.  This is really sloppy of wikipedia.

The P[i] are just a stack, create a linked list going in and pull
apart on way out, will by O(N).

If you do not separately track the min and max values, then the
algorithm works like this:

Make a map mu from the set of X[M[j]] to M[j], starting empty.
Make a P as a list of Maybe Int, starting as [].
Note that "size mu" will always by L, and starts off as 0.
For i=1,2,3…:
  do a Data.Map.splitLookup using pivot X[i] to get (map1,m,map2) and find which
  of the three cases we are in:
   If there is a null map2 then third case:
     If empty mu then prepend Nothing to P.
       else get "M[L]" from "snd (snd (findMax mu))", prepend Just "M[L]" to P.
     Create new my by inserting key X[i] with value i to mu.
   If there is a null map1 then first case (Note that mu cannot be empty):
     Prepend Nothing to P
     get min from "fst (findMin mu)"
     If X[i] < min then make new mu from replacing key X[i] with value i
                        with (Data.Map.updateMin).
   Otherwise this is the middle case and map1 and map2 are both non-empty.
     Get "M[j]" from (snd (findMax map1)) and prepend Just "M[j]" to P.
     If 'm' is (Just {}) then "X[i] < X[M[j+1]]" and do not change mu
       else change mu to Data.Map.adjust key X[i] with value i on mu

Also: keep track of the length of P, which is ultimate N, where 1≤i≤N.

Each operation in the loop with index i is order "log (i-1)"

Note that the "j" is never explicitly tracked.  It is implicit in the order of 
the keys of the map "mu".

Once you are done, you have a maximum subsequence length of (size mu),
and the stack P is just the P[i]'s in reverse order.  You can get the
last index i of the longest subsequence from (snd (findMax mu)) and
backtrack to get the other i's by carefully popping the stack P (in a
single traversal) and keeping only the indices you need until you
reach a Nothing.


More information about the Haskell-Cafe mailing list