La réponse acceptée est magnifique et rapidement compréhensible si vous êtes familier avec la récursivité des arbres. Puisque l'élégance était recherchée, ouvrir ce long fil dormant semble un peu inutile.
Cependant, une solution plus simple a été demandée. Les algorithmes itératifs me semblent parfois plus simples. En outre, la performance a été mentionnée comme un indicateur de qualité, et les processus itératifs sont parfois plus rapides que les processus récursifs.
Le code suivant est récursif et génère un processus itératif. Il faut un tiers du temps pour calculer des combinaisons de taille 12 à partir d'une liste de 24 éléments.
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 [[]]
L'idée qui permet un processus itératif consiste à pré-calculer une carte du dernier élément choisi de la liste des éléments disponibles restants. Cette carte est stockée dans remainderAfter
.
Le code n'est pas concis et ne correspond pas non plus à un compteur et une rime lyriques.
Vaguement question connexe: http://stackoverflow.com/questions/286427/calculating-permutations-in-f – Benjol