Die akzeptierte Antwort ist wunderschön und schnell verständlich, wenn Sie mit Baumrekursion vertraut sind. Da Eleganz gesucht wurde, scheint das Öffnen dieses langen, schlafenden Fadens etwas unnötig zu sein.
Es wurde jedoch eine einfachere Lösung gefordert. Iterative Algorithmen erscheinen mir manchmal einfacher. Darüber hinaus wurde Leistung als Qualitätsindikator genannt, und iterative Prozesse sind manchmal schneller als rekursive Prozesse.
Der folgende Code ist tail rekursiv und generiert einen iterativen Prozess. Es erfordert ein Drittel der Zeit, um Kombinationen der Größe 12 aus einer Liste von 24 Elementen zu berechnen.
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 [[]]
Die Idee, dass ein iteratives Verfahren erlaubt ist, um eine Liste der verbleibenden verfügbaren Elemente eine Karte des letzten gewählten Elements vorzunehmen zu berechnen. Diese Karte wird in remainderAfter
gespeichert.
Der Code ist nicht prägnant, noch entspricht es lyrischen Meter und Reim.
Vage verwandte Frage: http://stackoverflow.com/questions/286427/calculating-permutations-in-f – Benjol