Эта проблема - хорошая головоломка. Это должен быть код гольф. :)
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 Пример
permute ["1";"11"] 2
|> Seq.concat
|> Seq.iter (printfn "%A")
["1"; "1"]
["1"; "11"]
["11"; "11"]
Пример ABC
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
- это место, где происходит вся конкрирующая работа. Вы можете заменить его на y + li
, если вам нужны только строки. Вы также должны yield Seq.singleton
и ""
вместо []
Обновление производительности:
Эта проблема хорошо запоминается и дает гораздо лучшую производительность, запоминаемую ни для каких тривиальных случаев.
let memoize2 f =
let cache = Dictionary<_,_>()
fun x y ->
let ok, res = cache.TryGetValue((x, y))
if ok then
res
else
let res = f x y
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)
Я также запомнил решение kvb, и оно работает на 15% быстрее, чем мое.
// 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)