I’ve done many articles over the years on different ways to manipulate sets and sequences in C#: The Cartesian product is when you have a sequence of sequences, say

{ { 1, 2 }, { 10, 11, 12} }

and produce the sequence of all possible sequences that take one element from each of the original sequences. That would be:

{ {1, 10}, {1, 11}, {1, 12}, {2, 10}, {2, 11}, {2, 12} }

in this case. The permutations of a sequence, say, {20, 30, 40} are all the different ways to reorder that sequence:

{ {20, 30, 40}, {20, 40, 30}, {30, 20, 40}, {30, 40, 20}, {40, 20, 30}, {40, 30, 20} }

Today I want to talk about a problem I’ve never covered, which is how to generate all the combinations of a sequence. The combinations are all the ways to take some given number of elements from the sequence. Or, equivalently, all the subsequences of a given length. (A subsequence of a sequence has elements from the sequence in the same order, but some elements from the original sequence can be missing.) For example, if we have the sequence {50, 60, 70, 80, 90} and we wish to choose three, then the combinations are:

{ {50, 60, 70}, {50, 60, 80}, {50, 60, 90}, {50, 70, 80}, {50, 70, 90}, {50, 80, 90}, {60, 70, 80}, {60, 70, 90}, {60, 80, 90}, {70, 80, 90} }

How many such subsequences are there? If there are n items in the original sequence and we are choosing k of those items then I will give you without proof or argument that the number of possible combinations is n!/k!(n-k)!. Let’s just double check that: we had five elements, we chose three of them, 5!/3!(5-3)! is in fact 10. This formula produces the binomial coefficient, and it has many interesting properties beyond those in combinatorics.

When writing computer programs it is wise to state the edge cases. Given the same sequence, what if we wish to choose none of them? There does exist a subsequence which has zero elements, so we should produce it; the answer would be { { } } . What if we have a sequence of five items and we wish to choose six of them? There is no way to do that; there is no six-element subsequence. So the answer should be { } , the empty sequence of sequences.

The first clever bit is to notice that essentially what we are doing in each subsequence is deciding whether or not to include each element from the sequence. Let’s lay out those subsequences again and indicate whether the corresponding member was included or not, true or false:

{ // 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 }

Aha! If we can enumerate all the sequences of n bits that have exactly k true then we have solved our problem.

Next time: We’ll do just that.