views:

380

answers:

5

One more question about most elegant and simple implementation of element combinations in F#.

It should return all combinations of input elements (either List or Sequence). First argument is number of elements in a combination.

For example:

comb 2 [1;2;2;3];;
[[1;2]; [1;2]; [1;3]; [2;2]; [2;3]; [2;3]]
+4  A: 
let rec comb n l =
  match (n,l) with
  | (0,_) -> [[]]
  | (_,[]) -> []
  | (n,x::xs) ->
      let useX = List.map (fun l -> x::l) (comb (n-1) xs)
      let noX = comb n xs
      useX @ noX
kvb
Fastest solution till now, but less concise.
The_Ghost
+1  A: 

There is more consise version of KVB's answer:

let rec comb n l =
  match (n,l) with
    | (0,_) -> [[]]
    | (_,[]) -> []
    | (n,x::xs) ->
      List.flatten [(List.map (fun l -> x::l) (comb (n-1) xs)); (comb n xs)]
ssp
A: 

My solution is less concise, less effective (altho, no direct recursion used) but it trully returns all combinations (currently only pairs, need to extend filterOut so it can return a tuple of two lists, will do little later).

let comb lst =
    let combHelper el lst =
        lst |> List.map (fun lstEl -> el::[lstEl])
    let filterOut el lst =
        lst |> List.filter (fun lstEl -> lstEl <> el)
    lst |> List.map (fun lstEl -> combHelper lstEl (filterOut lstEl lst)) |> List.concat

comb [1;2;3;4] will return: [[1; 2]; [1; 3]; [1; 4]; [2; 1]; [2; 3]; [2; 4]; [3; 1]; [3; 2]; [3; 4]; [4; 1]; [4; 2]; [4; 3]]

Ray
This solution is not working correctly. It doesn't return combinations, but only pairs of elements.
The_Ghost
It's all possible combinations. Not just tail combinations. comb [1;2;3] is 1 added to each of [2;3], 2 added to each of [1;3], 3 added to each of [1;2]
Ray
> comb 3 [1..4];;val it : int list list = [[1; 2; 3]; [1; 2; 4]; [1; 3; 4]; [2; 3; 4]]With more elements, it should not return pairs, but triples (for n=3)
The_Ghost
+1  A: 

One less concise and more faster solution than ssp:

let rec comb n l = 
    match n, l with
    | 0, _ -> [[]]
    | _, [] -> []
    | k, (x::xs) -> List.map ((@) [x]) (comb (k-1) xs) @ comb k xs
The_Ghost
Could someone write simpler than that solution?
The_Ghost
A: 

Ok, just tail combinations little different approach (without using of library function)

let rec comb n lst =
    let rec findChoices = function
      | h::t -> (h,t) :: [ for (x,l) in findChoices t -> (x,l) ]
      | []   -> []
    [ if n=0 then yield [] else
            for (e,r) in findChoices lst do
                for o in comb (n-1) r do yield e::o  ]
Ray