How to create a permutation in c++ using STL for number of places lower than the total length
You might use 2 loops:
- Take each n-tuple
- iterate over permutations of that n-tuple
template <typename F, typename T>
void permutation(F f, std::vector<T> v, std::size_t n)
{
std::vector<bool> bs(v.size() - n, false);
bs.resize(v.size(), true);
std::sort(v.begin(), v.end());
do {
std::vector<T> sub;
for (std::size_t i = 0; i != bs.size(); ++i) {
if (bs[i]) {
sub.push_back(v[i]);
}
}
do {
f(sub);
}
while (std::next_permutation(sub.begin(), sub.end()));
} while (std::next_permutation(bs.begin(), bs.end()));
}
Demo
If efficiency is not the primary concern, we can iterate over all permutations and skip those that differ on a suffix selecting only each (N - k)!
-th one. For example, for N = 4, k = 2
, we have permutations:
12 34 <
12 43
13 24 <
13 42
14 23 <
14 32
21 34 <
21 43
23 14 <
23 41
24 13 <
24 31
...
where I inserted a space for clarity and marked each (N-k)! = 2! = 2
-nd permutation with <
.
std::size_t fact(std::size_t n) {
std::size_t f = 1;
while (n > 0)
f *= n--;
return f;
}
template<class It, class Fn>
void generate_permutations(It first, It last, std::size_t k, Fn fn) {
assert(std::is_sorted(first, last));
const std::size_t size = static_cast<std::size_t>(last - first);
assert(k <= size);
const std::size_t m = fact(size - k);
std::size_t i = 0;
do {
if (i++ == 0)
fn(first, first + k);
i %= m;
}
while (std::next_permutation(first, last));
}
int main() {
std::vector<int> vec{1, 2, 3, 4};
generate_permutations(vec.begin(), vec.end(), 2, [](auto first, auto last) {
for (; first != last; ++first)
std::cout << *first;
std::cout << ' ';
});
}
Output:
12 13 14 21 23 24 31 32 34 41 42 43
Here is a an efficient algorithm that doesn't use std::next_permutation
directly, but makes use of the work horses of that function. That is, std::swap
and std::reverse
. As a plus, it's in lexicographical order.
#include <iostream>
#include <vector>
#include <algorithm>
void nextPartialPerm(std::vector<int> &z, int n1, int m1) {
int p1 = m1 + 1;
while (p1 <= n1 && z[m1] >= z[p1])
++p1;
if (p1 <= n1) {
std::swap(z[p1], z[m1]);
} else {
std::reverse(z.begin() + m1 + 1, z.end());
p1 = m1;
while (z[p1 + 1] <= z[p1])
--p1;
int p2 = n1;
while (z[p2] <= z[p1])
--p2;
std::swap(z[p1], z[p2]);
std::reverse(z.begin() + p1 + 1, z.end());
}
}
And calling it we have:
int main() {
std::vector<int> z = {1, 2, 3, 4, 5, 6, 7};
int m = 3;
int n = z.size();
const int nMinusK = n - m;
int numPerms = 1;
for (int i = n; i > nMinusK; --i)
numPerms *= i;
--numPerms;
for (int i = 0; i < numPerms; ++i) {
for (int j = 0; j < m; ++j)
std::cout << z[j] << ' ';
std::cout << std::endl;
nextPartialPerm(z, n - 1, m - 1);
}
// Print last permutation
for (int j = 0; j < m; ++j)
std::cout << z[j] << ' ';
std::cout << std::endl;
return 0;
}
Here is the output:
1 2 3
1 2 4
1 2 5
1 2 6
1 2 7
.
.
.
7 5 6
7 6 1
7 6 2
7 6 3
7 6 4
7 6 5
Here is runnable code from ideone