la respuesta aceptada es magnífico y rápidamente comprensible si está familiarizado con la recursividad de árbol. Desde que se buscó la elegancia, la apertura de este largo hilo inactivo parece algo innecesario.
Sin embargo, se pidió una solución más simple. Los algoritmos iterativos a veces me parecen más simples. Además, se mencionó el rendimiento como un indicador de calidad, y los procesos iterativos a veces son más rápidos que los recursivos.
El siguiente código es cola recursivo y genera un proceso iterativo. Se requiere un tercio de la cantidad de tiempo para calcular combinaciones de tamaño 12 de una lista de 24 elementos.
let combinations size aList =
let rec pairHeadAndTail acc bList =
match bList with
| [] -> acc
| x::xs -> pairHeadAndTail (List.Cons ((x,xs),acc)) xs
let remainderAfter = aList |> pairHeadAndTail [] |> Map.ofList
let rec comboIter n acc =
match n with
| 0 -> acc
| _ ->
acc
|> List.fold (fun acc alreadyChosenElems ->
match alreadyChosenElems with
| [] -> aList //Nothing chosen yet, therefore everything remains.
| lastChoice::_ -> remainderAfter.[lastChoice]
|> List.fold (fun acc elem ->
List.Cons (List.Cons (elem,alreadyChosenElems),acc)
) acc
) []
|> comboIter (n-1)
comboIter size [[]]
La idea de que permite un proceso iterativo es comprobar la validez de calcular un mapa del último elemento seleccionado a una lista de los elementos disponibles restantes. Este mapa se almacena en remainderAfter
.
El código no es conciso, ni se ajusta al ritmo lírico ni a la rima.
Pregunta vagamente relacionada: http://stackoverflow.com/questions/286427/calculating-permutations-in-f – Benjol