How to understand the following C# linq code that implements an algorithm to return all combinations of k elements from n


xuehui

Can anyone elaborate on this code, or even provide a non-Linq version of this algorithm:

public static IEnumerable<IEnumerable<T>> Combinations<T>
    (this IEnumerable<T> elements, int k)
{
   return k == 0 ? new[] { new T[0] }
                 : elements.SelectMany(
                       (e, i) =>
                         elements
                         .Skip(i + 1)
                         .Combinations(k - 1)
                         .Select(c => (new[] {e}).Concat(c)));
}
virtual machine

The best way to understand this code is to read Eric Lippert's excellent serial:

Basically, if we have IEnumerable5 items, and we want to make all combinations of size 3, we need to produce the following:

{
                      // 50, 60, 70, 80, 90
    {50, 60, 70},     // T   T   T   F   F
    {50, 60, 80},     // T   T   F   T   F
    {50, 60, 90},     // T   T   F   F   T
    {50, 70, 80},     // T   F   T   T   F
    {50, 70, 90},     // T   F   T   F   T
    {50, 80, 90},     // T   F   F   T   T
    {60, 70, 80},     // F   T   T   T   F
    {60, 70, 90},     // F   T   T   F   T
    {60, 80, 90},     // F   T   F   T   T
    {70, 80, 90}      // F   F   T   T   T
}

Eric's recursive implementation:

// Takes integers n and k, both non-negative.
// Produces all sets of exactly k elements consisting only of 
// integers from 0 through n - 1.
private static IEnumerable<TinySet> Combinations(int n, int k)
{
  // Base case: if k is zero then there can be only one set
  // regardless of the value of n: the empty set is the only set
  // with zero elements.
  if (k == 0)
  { 
    yield return TinySet.Empty;
    yield break;
  }

  // Base case: if n < k then there can be no set of exactly
  // k elements containing values from 0 to n - 1, because sets
  // do not contain repeated elements.

  if (n < k)
    yield break;

  // A set containing k elements where each is an integer from
  // 0 to n - 2 is also a set of k elements where each is an
  // integer from 0 to n - 1, so yield all of those.

  foreach(var r in Combinations(n-1, k))
    yield return r;

  // If we add n - 1 to all the sets of k - 1 elements where each
  // is an integer from 0 to n - 2, then we get a set of k elements
  // where each is an integer from 0 to n - 1.

  foreach(var r in Combinations(n-1, k-1))
    yield return r.Add(n-1);
}

In your case, the code works like this:

   return k == 0
     // if we are done, return empty array
     ? new[] {new T[0]}
     // for each element and each number from 0 to enumerable size
     : elements.SelectMany((e, i) =>
                            elements
     //skip first i elements, as we already produced combination with them
                            .Skip(i + 1)
     //get all the combinations with size k - 1
                            .Combinations(k - 1)
     //add current element to all produced combinations
                            .Select(c => (new[] {e}).Concat(c)));

Such non-recursive form of code will be huge and unreadable, try to understand recursion:

Suppose we have 5 elements IEnumerable: { 16, 13, 2, 4, 100 }, and all combinations of size 2 are required (the total of the result set equals the binomial coefficients from 5 to 2= 5! / (2! * 3!) = 10

Your code will produce:

  1. For 16us, we need all combinations of sizes 1, starting from the second position:
  2. For elements 13we need all size combinations 0starting from the third position
  3. First result:{ 16, 13 }
  4. skip 13, for elements 2we need all size combinations 0starting from the fourth position
  5. Second result:{ 16, 2 }
  6. skip 13, 2, for elements 4we need all size combinations 0starting from the fifth position
  7. Third result:{ 16, 4 }
  8. skip 13, 2, 4, for elements 100we need all size combinations 0starting from the sixth position
  9. Fourth result:{ 16, 100 }
  10. ...repeat all from above 13, 2, 4:
    { 13, 2 }, { 13, 4 }, { 13, 100 }, { 2, 4 }, { 2, 100 },{ 4, 100 }

We got all 10 combinations we needed. The overload the code author is using is this :Enumerable.SelectMany<TSource, TResult> Method (IEnumerable<TSource>, Func<TSource, Int32, IEnumerable<TResult>>)

Selector
type: System.Func<TSource, Int32, IEnumerable<TResult>>
The transformation function applied to each source element;
the second parameter of the function represents the index of the source element.

Related


Algorithm to return all combinations of k elements from n

Frederick I want to write a function that takes an array of letters as a parameter and selects multiple letters. Suppose you provide an array of 8 letters and want to select 3 letters from it. Then you will get: 8! / ((8 - 3)! * 3!) = 56 Returns an array (or

Algorithm to return all combinations of k elements from n

Frederick I want to write a function that takes an array of letters as a parameter and selects multiple letters. Suppose you provide an array of 8 letters and want to select 3 letters from it. Then you will get: 8! / ((8 - 3)! * 3!) = 56 Returns an array (or

get all possible combinations of k elements from a list

Tobias Herman I need a function that does the same thing as itertools.combinations(iterable, r) in python So far I came up with this: {-| forward application -} x -: f = f x infixl 0 -: {-| combinations 2 "ABCD" = ["AB","AC","AD","BC","BD","CD"] -} combinatio

Get all possible combinations of k elements from a list

Tobias Herman I need a function that does the same thing as in pythonitertools.combinations(iterable, r) So far I have come up with this: {-| forward application -} x -: f = f x infixl 0 -: {-| combinations 2 "ABCD" = ["AB","AC","AD","BC","BD","CD"] -} combin

Algorithm (Java) to get all combinations of size n from an array?

Esostack Right now I'm trying to write a function that takes an array and an integer n and gives a list of each combination of size n (i.e. a list of int arrays). I could write it using n nested loops, but that only works for a subset of a certain size. I don'

Algorithm (Java) to get all combinations of size n from an array?

Esostack Right now, I'm trying to write a function that takes an array and an integer n, and gives a list of each combination of size n (hence a list of int arrays). I could write it using n nested loops, but that only works for a subset of a certain size. I c

Algorithm (Java) to get all combinations of size n from an array?

Esostack Right now, I'm trying to write a function that takes an array and an integer n, and gives a list of each combination of size n (hence a list of int arrays). I could write it using n nested loops, but that only works for a subset of a certain size. I c

Algorithm (Java) to get all combinations of size n from an array?

Esostack Right now I'm trying to write a function that takes an array and an integer n and gives a list of each combination of size n (i.e. a list of int arrays). I could write it using n nested loops, but that only works for a subset of a certain size. I don'

How to find all combinations of n items from a set of numbers using LINQ?

think I am trying to write an algorithm to select all combinations of n values from a set of numbers. For example, given a collection:1, 2, 3, 7, 8, 9 All combinations of 2 values in the set are: (1、2),(1、3),(1、7),(1、8),(1、9),(2、3),(2、7),(2、8),(2 ,9),(3,7),(3,

make all combinations of size k from 1 to number n

Ananya Given two numbers n and k, you have to find all possible combinations of k numbers in 1...n. I am using DFS algorithm to achieve this. But my ansarray returns None, whereas if I try to print temp, the combination is generated correctly. What am I doing

make all combinations of size k from 1 to number n

Ananya Given two numbers n and k, you have to find all possible combinations of k numbers in 1...n. I am using DFS algorithm to achieve this. But my ansarray returns None, whereas if I try to print temp, the combination is generated correctly. What am I doing

Algorithm to generate K elements from a slice of N elements

Andrew LeFevre: I'm trying to port an algorithm from a Stackoverflow question in Go . The algorithm I'm trying to use is the following: Given a string slice of arbitrary length and a "depth", find all combinations of elements of length depth in the original sl

Algorithm to generate K elements from a slice of N elements

Andrew LeFevre: I'm trying to port an algorithm from a Stackoverflow question in Go . The algorithm I'm trying to use is the following: Given a string slice of arbitrary length and a "depth", find all combinations of elements of length depth in the original sl