logo
Published on

Combinations of K Elements from N Elements in C++

Authors

Problem Statement

To kick off this blog, let's start off with a problem statement.

Suppose your friend tells you that his phone's password matches this criteria:

  • It has exactly 88 characters long
  • All the characters are distinct
  • All the characters consist of lowercase English letters (a to z)
  • The password is sorted in an ascending order

Unfortunately, your friend completely forgot his exact password, and needs help from you!

You have the ability to plug your friend's phone to your computer and try to input every possible password to open up his phone (without having to worry about getting those 3030 seconds wait after incorrect attempts and all).

The problem now is... how would you do it?

Idea

Looks like there is no option to solve this problem without using bruteforce technique.

As the title of this blog might hint you already, this looks like generating all combination of taking kk elements from nn elements.

In this case, generating all the combinations of all 88 letters from the 2626 letters in the alphabet.

Putting a little bit of math, we will have exactly C(26,8)=26!8!×18!=1562275\displaystyle C(26, 8) = \frac{26!}{8! \times 18!} = 1562275 combinations, which is only around a million!

Using C++ on Generating Combinations

We can use C++ (The best programming language) for this type of problem mainly using the standard library next_permutation.

In C++, next_permutation is a standard library function that rearranges the elements of a range (e.g., a container like a vector) into the next lexicographically greater permutation.

In simpler terms, it helps you generate the next possible arrangement of elements in ascending order.

For example if you have an array [1,1,2][1, 1, 2] then using the next permutation it will then be [1,2,1][1, 2, 1].

Okay, but wait... aren't we suppose to find combinations? why are we talking permutations stuff?

Next Permutation Trick

We can actually utilize next_permutation to solve our problem statement!

We wanted to generate all combinations of picking kk elements from nn elements.

We can do this with a simple idea.

Let's make an array / vector with size nn, set the first nkn - k elements with 00 and the other kk elements with 11.

And now we can use next_permutation to generate all the possibility of picking kk elements from nn elements.

The idea is that if the value is 11 in some position, that means we have picked that position.

For an example, let's say n=5n=5 and k=2k=2 then we will have an initial array of [0,0,0,1,1][0, 0, 0, 1, 1].

Now our program will generate the following possiblity:

  • [0,0,0,1,1][0, 0, 0, 1, 1] \rightarrow Picking the 44-th and 55-th element
  • [0,0,1,0,1][0, 0, 1, 0, 1] \rightarrow Picking the 33-th and 55-th element
  • [0,0,1,1,0][0, 0, 1, 1, 0] \rightarrow Picking the 33-th and 44-th element
  • \vdots
  • [1,1,0,0,0][1, 1, 0, 0, 0] \rightarrow Picking the 11-th and 22-th element

We can be sure that all the combinations are found!

Code

The code for the solution would look like the following:

const string ALPHABET = "abcdefghijklmnopqrstuvwxyz";

int N = 26, K = 8;
vector<int> p(N, 1); // initialize all elements with the value 1
// set the N - K first elements with 0
// this way the last K elements will have the value 1
for (int i = 0; i < N - K; i++) {
    p[i] = 0;
}
do {
    string password = "";
    for (int i = 0; i < N; i++) {
        if (p[i] == 1) {
            password += ALPHABET[i];
        }
    }
    // use the password to open up the phone!
} while (next_permutation(p.begin(), p.end()));

The complexity would then be the number of possible combinations multiplied by the complexity of next_permutation itself which is at most O(n)O(n)1.

So the total complexity would be around O(C(n,k)n)O(C(n, k) \cdot n), not too bad! (I've actually tried running the code and it performs fast)

In Summary

If you ever need to generate a combination of picking kk elements of nn elements, try using this next_permutation trick!

Footnotes

  1. Based on cppreference, the time complexity is at most n2\frac{n}{2} swaps, where N = std::distance(first, last). Averaged over the entire sequence of permutations, typical implementations use about 33 comparisons and 1.51.5 swaps per call.