As part of a bigger problem of enumerating a set, I need to write an OCaml function 'choose' which takes a list and outputs as the list of all possible sequences of size k made up of elements of that list (without repeating sequences which can be obtained from each other by permutation). The order they are put in the end list is not relevant.
For example,
choose 2 [1;2;3;4] = [[1;2];[1;3];[1;4];[2;3];[2;4];[3;4]]
Any ideas?
I would like to have the whole thing to be lazy, outputting a lazy list, but if you have a strict solution, that'll be very useful too.
Plugging in again with a Haskell solution (it's just easier to work with lazy lists since they are built-in):
The first two cases follow from the properties of binomial coefficients and more specifically:
n choose 0 = 1
for alln
includingn=0
(that's why it is first to handle the case0 choose 0
). The other one is0 choose k = 0
. The third equation is exact translation of the recursive definition of combinations.Unfortunately when you apply it to an infinite list it returns a trivial solution:
EDIT: OK, so we really want to go trough each combination in a finite number of steps. With the above version we are obviously using only the expression to the left of
++
which generates only combinations starting with 1. We can work around this problem by defining an interesting list zipping function which builds a list by alternately picking the head of each of its argument lists (it's important to be non-strict in the second argument):and use it instead of
++
:lets see:
All
10 choose 3
combinations are there!Here is a strict and suboptimal version. I hope it is clear. It avoids duplicates by assuming there are no duplicates in the input list, and by generating only sublists that are in the same order as in the original list.
The length computation could be factored by passing
l
's length as an argument ofchoose
. That would make the code less readable but more efficient.For the lazy version, sprinkle "lazy" and "Lazy.force" on the code...
EDIT:
A
lazy_list_append
as appears necessary from the comments below:Just for the sake of completeness, I am putting here the final code which brings together the strict code from Pascal with my lazy stuff and all other Pascal's useful comments.
The lazy list type is defined, then two auxiliary lazy functions (append and map), and finally the function "choose" that we aim to define.
The result of evaluating "choose k ls n" is a lazy list of all choices of k elements of list ls, with ls considered up to size n. Note that, as pointed out by Pascal, because of the way the enumeration takes place, the function choose will not cover all choices of an infinite list.
Thanks, this was really useful!
Best, Surikator.