Przyjęty odpowiedź wspaniały i szybko zrozumiały, jeśli znasz rekursję drzewa. Ponieważ szukano elegancji, otwarcie tej długiej, uśpionej nitki wydaje się nieco niepotrzebne.
Jednak wymagało prostszego rozwiązania. Iteracyjne algorytmy czasem wydają mi się prostsze. Ponadto wydajność została wymieniona jako wskaźnik jakości, a iteracyjne procesy są czasem szybsze niż rekursywne.
Poniższy kod jest rekursywny i generuje proces iteracyjny. Wymaga to jednej trzeciej czasu na obliczenie kombinacji wielkości 12 z listy 24 elementów.
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 [[]]
Pomysł, który pozwala iteracyjny proces jest wstępnie obliczyć mapa ostatniego wybranego elementu do listy pozostałych dostępnych elementów. Ta mapa jest przechowywana w remainderAfter
.
Kod nie jest zwięzły ani nie jest zgodny z licznikiem i rymowanką.
Niejasno powiązane pytanie: http://stackoverflow.com/questions/286427/calculating-permutations-in-f – Benjol