The Alogirthm
Recently, I have entered a brave (new?) world of Clojure and was looking for a small project to take it for a ride. I stopped on a popular/interesting enough little problem, that subsumed a certain interview question which I was once unfortunate enough to stumble through with half my brain tied behind my back (long story).
The algorithm to generate permutations in sequence is actually quite simple and intuitive, I believe it originates somewhere in India thousands of years ago. At least that was the impression I got from a Wikipedia article. Anyway, given a fully ordered set S, generate all permutations of it. The algorithm works for sets with repetition, but we can assume no repetitions for simplicity:
- (Since the set is fully ordered), sort it in ascending order, represent it as a sequence. Output it.
- Find the next sequence consisting of the elements of S and following the current one immediately based on the ordering relationship. Output it.
- Repeat 2, until no such sequence can be found.
The juice here is of course 2. To illustrate with an example. Suppose our sequence is just numbers “{}” here mean sequence, order matters: S ={1, 2, 3, 4}. The next one in lexicographical order is: {1, 2, 4, 3}. The one after it: {1, 3, 2, 4}. The one after: {1, 3, 4, 2}. Shall we do one more? {1, 4, 2, 3} Maybe just one more, so it becomes even clearer: {1, 4, 3, 2}.
So the key here, in case you have not caught on yet:
- Starting from the end of the sequence, going backwards, find the first smallest position in the sequence, if such exists, where the element in current position can be swapped with the element in the found position to make the sequence “larger” than the previous one. This is accomplished if the element at the current position is less than the element at the found position. Swap elements in the current and the found positions.
- Sort the sub-sequence starting at the found + 1 position, ascending. So, in the above example we get: {1, 3, 2, 4}
For instance, looking at {1, 2, 4, 3} we find: (2 < 3), (2 < 4), (1 < 2). But 2 < 3 is the very first pair that fits the bill (we are starting from the back), so we don't go any further and swap them. We get {1, 3, 4, 2}
Et c’est tout!
(How simple is that? I bet the brute force recursive “solution” feels very stupid right about now).
Implementation
At this point it’s all but unnecessary to actually show the implementations. However, I just had fun with some of the Clojure concepts/structures, and wanted to compare them to what I am used to in F#. So this is the point. Meta-programming. The Clojure code is here and F# – here.
Observations:
Immutability
The way the algorithm is structured, immutability is intuitive: we are producing a new sequence in each step, and old sequences should also be preserved and output. Should have been a no-brainer, since we are dealing with functional languages with immutable structures. And here was a slight jolt. Consider a function that swaps elements in the sequence (the algorithm calls for swapping, so…):
Clojure:
(defn swapDigits [v pos1 pos2] (assoc v pos2 (v pos1) pos1 (v pos2)))
(I was using strings of digits as input sets in my initial awkward steps through the language, hence the function name).
Now, I wanted something as short and as sweet as this for F#, so I cheated! .NET arrays are mutable and used I .NET arrays instead of F# lists (hope functional programming purists will commute a very harsh sentence I deserve for writing the following, and not condemn me to Java programming for life):
let swapPositions (v : 'a array) pos1 pos2 = let res = Array.copy v res.[pos1] <- v.[pos2] res.[pos2] <- v.[pos1] res
So yes. The code at line 2 is pure sin and I should have used F# lists which are immutable, and the code should have been:
let swapPositions (v : 'a list) pos1 pos2 = v |> List.permute (fun i -> if i = pos1 then pos2 elif i = pos2 then pos1 else i)
but frankly, this is not the (very) first thing that comes to mind if you haven’t been using the language for a long time, and not as clear as the Clojure solution, which was close enough to the surface for a noob like myself to scoop it up.
Alright. This has gone on long enough, more soon…
One thought on “Generating Permutations: Clojure or F#: Part 1”