Tomas' solution is pretty elegant: it's short, purely functional and lazy. I think it could even be tail-recursive. In addition, he makes permutations lexicographically. However, we can improve performance by half, using an internal internal solution, while still exposing the external interface.
The permutations function accepts the general sequence e , as well as the general comparison function f : ('a -> 'a -> int) and lazily displays immutable permutations lexicographically. The comparison functionality allows us to create permutations of elements that are not necessarily comparable , as well as easily determine reverse or user orders.
The permute internal function is an imperative implementation of the described algorithm here . The conversion function let comparer f = { new System.Collections.Generic.IComparer<'a> with member self.Compare(x,y) = fxy } allows you to use the overload System.Array.Sort , which makes its own custom settings at the sub- range using IComparer .
let permutations fe = ///Advances (mutating) perm to the next lexical permutation. let permute (perm:'a[]) (f: 'a->'a->int) (comparer:System.Collections.Generic.IComparer<'a>) : bool = try //Find the longest "tail" that is ordered in decreasing order ((s+1)..perm.Length-1). //will throw an index out of bounds exception if perm is the last permuation, //but will not corrupt perm. let rec find i = if (f perm.[i] perm.[i-1]) >= 0 then i-1 else find (i-1) let s = find (perm.Length-1) let s' = perm.[s] //Change the number just before the tail (s') to the smallest number bigger than it in the tail (perm.[t]). let rec find i imin = if i = perm.Length then imin elif (f perm.[i] s') > 0 && (f perm.[i] perm.[imin]) < 0 then find (i+1) i else find (i+1) imin let t = find (s+1) (s+1) perm.[s] <- perm.[t] perm.[t] <- s' //Sort the tail in increasing order. System.Array.Sort(perm, s+1, perm.Length - s - 1, comparer) true with | _ -> false //permuation sequence expression let c = f |> comparer let freeze arr = arr |> Array.copy |> Seq.readonly seq { let e' = Seq.toArray e yield freeze e' while permute e' fc do yield freeze e' }
Now for convenience we have the following: let flip fxy = fyx :
let permutationsAsc e = permutations compare e let permutationsDesc e = permutations (flip compare) e
Stephen Swensen Jul 05 '10 at 15:32 2010-07-05 15:32
source share