This problem is a good brain teaser. It must be a golf code. :)
let rec permute list count = seq { match list with | y::ys when count > 0 -> for n in permute list (count - 1) do yield Seq.map (fun li -> y::li) n yield Seq.concat (permute ys count) | y::ys -> yield Seq.singleton [] | [] -> () }
Ace example
permute ["1";"11"] 2 |> Seq.concat |> Seq.iter (printfn "%A") ["1"; "1"] ["1"; "11"] ["11"; "11"]
ABC example
permute ["A";"B";"C"] 3 |> Seq.concat |> Seq.iter (printfn "%A");; ["A"; "A"; "A"] ["A"; "A"; "B"] ["A"; "A"; "C"] ["A"; "B"; "B"] ["A"; "B"; "C"] ["A"; "C"; "C"] ["B"; "B"; "B"] ["B"; "B"; "C"] ["B"; "C"; "C"] ["C"; "C"; "C"]
y::li is where all the coordinated work is done. You can replace it with y + li if all you need is strings. You must also yield Seq.singleton a "" insted []
Performance Update:
This problem is well remembered and gives much better performance not seen in trivial cases.
let memoize2 f = let cache = Dictionary<_,_>() fun xy -> let ok, res = cache.TryGetValue((x, y)) if ok then res else let res = fxy cache.[(x, y)] <- res res // permute ["A";"B";"C"] 400 |> Seq.concat |> Seq.length |> printf "%A" // Real: 00:00:07.740, CPU: 00:00:08.234, GC gen0: 118, gen1: 114, gen2: 4 let rec permute = memoize2(fun list count -> seq { match list with | y::ys when count > 0 -> for n in permute list (count - 1) do yield Seq.map (fun li -> y::li) n |> Seq.cache yield Seq.concat (permute ys count) | y::ys -> yield Seq.singleton [] | [] -> () } |> Seq.cache)
I also have a memoized kvb solution and it works 15% faster than mine.
// permute ["A";"B";"C"] 400 |> Seq.length |> printf "%A" // Real: 00:00:06.587, CPU: 00:00:07.046, GC gen0: 87, gen1: 83, gen2: 4 let rec permute = memoize2 (fun list n -> match n with | 0 -> Seq.singleton [] | n -> seq { match list with | x::xs -> yield! permute list (n-1) |> Seq.map (fun l -> x::l) yield! permute xs n | [] -> () } |> Seq.cache)
source share