2

I'm working on problem 24 from Project Euler which is as follows:

A permutation is an ordered arrangement of objects. For example, 3124 is one possible permutation of the digits 1, 2, 3 and 4. If all of the permutations are listed numerically or alphabetically, we call it lexicographic order. The lexicographic permutations of 0, 1 and 2 are:

012 021 102 120 201 210

What is the millionth lexicographic permutation of the digits 0, 1, 2, 3, 4, 5, 6, 7, 8 and 9?

I am trying to solve this using Haskell, and started with a brute force approach:

import Data.List
(sort . permutation) [0..9] !! 999999

But this takes way too long, and I'm wondering if it's because the program first gets all the permutations, then sorts them all, then finally takes the millionth element, which is a lot more work than it needs to do.

So I was thinking I could speed things up if I were to write a function that enumerates the permutations already in lexicographic order, so that we could just stop at the millionth element and have the answer.

The algorithm I have in mind is to first sort the input list x, then take the first (smallest) element and prepend it to the permutations of the remaining elements in lexicographic order. These orderings would be found by recursively calling the original function on the now already sorted tail of x (which means our original function should have a way of flagging whether or not the input list is sorted). Then we continue with the next largest element of x and so on until we have the full ordered list. Unfortunately I am still a Haskell beginner and my attempts to write this function have failed. Any hints as to how this might be done?

dsaxton
  • 995
  • 2
  • 10
  • 23

1 Answers1

5

I have a thought that's too long for a comment, but isn't a working solution in its entirety. Still, it's a plan that should work nearly instantly.

Start with generating permutations in lexicographic order. This is easy to do with a recursive algorithm. First, select the least element available, and recursively generate permutations of the remaining elements, prepending the selected element to each permutation. Then select the second element lexicographically and continue on up.

For what it's worth, this is the standard-ish nondeterministic-select based permutation algorithm you often find in Haskell instructional materials, if the input list is sorted into increasing order. It's not the algorithm used by Data.List.permutations, which is designed to be faster and productive with infinite input.

But you can do better than this. You don't need to generate all the permutations before the target one. You can skip ahead, and it turns out to be really easy.

All you need to do is look at the number of permutation you are targeting, let's call it k, and use it to index into the permutations. If the inputs are sorted lexicographically, the first element of the result is the element at index q, followed by the permutation of the remaining elements at index r, given (q, r) = divMod k (fact(n - 1)).

I'm sure there are ways to get it faster than this, but that should make it basically instant for small numbers like a million anyway.

Carl
  • 26,500
  • 4
  • 65
  • 86