I have the following recursive function for outputting partial combinations:
void comb(string sofar, string rest, int n) {
string substring;
if (n == 0)
cout << sofar << endl;
else {
for (size_t i = 0; i < rest.length(); i++) {
substring = rest.substr(i + 1, rest.length());
comb(sofar + rest[i], substring, n - 1);
}
}
}
called like so:
comb("", "abcde", 3);
By partial combinations, I mean that it uses n choices and r elements (rather than n choices, n elements).
However, I would like to take the order of elements into account (i.e. permutations). I can find many algorithms for full permutations, but not partial.
You can do this with the standard library:
This relies on the fact that
next_permutation
generates permutations in lexicographic order. So if the tail of the sequence is sorted, reversing the tail and then asking for the next permutation of the entire sequence moves the head to the next permutation of a subset, and leaves the tail once again sorted for the next iteration.In case it's not obvious, the functor is called with a pair of iterators representing the range of the permuted combination.
It's time for a performance reality check here. If you are only interested in visiting the permutation of 5 things 3 at a time, stop reading now, as the number of visitations is so small that it doesn't matter much (unless perhaps you are doing this a billion times).
But if you need to visit more things, and more things at a time, performance can really start to matter. For example what about visiting the string of 26: "abcdefghijklmnopqrstuvwxyz", six items at a time? With permutations, costs grow very quickly...
For performance testing it is best to comment out the output to the terminal as that tends to be a very slow operation which swamps everything else.
This answer (the currently accepted one) looks like this:
On my system (
clang++ -std=c++14 test.cpp -O3
) this outputs:This answer using
std::next_permutation
is significantly faster.which outputs:
This is 69% faster! A lot of this speed increase can be attributed to the lack of allocation and deallocation that is implicit in the first algorithm.
However I would like to point you to an even faster algorithm.
The driver looks like:
The output is:
This is 30 times faster than the answer using
std::next_permutation
and 51 times faster than the currently accepted answer! And asn
andr
grow, the divergence of these performance numbers grows as well.The linked library is free and open source. The implementation is at the link and can be copy/pasted out of it. I won't claim that it is simple. I only claim that its performance is compelling. The difference in performance is so dramatic that it can make the difference between making the problem solvable in a practical amount of time or not.
You want permutations, so you should capture characters before AND after
i
insubstring
:Complete program on coliru:
Outputs (reformatted):