順列生成とは
順列生成とは、複数の要素があるとき、その要素を並べる順番のパターンを全て生成することを意味する。
例えば {1, 2, 3} という要素列が有る場合、その全ての順列は以下のようになる。
{1, 2, 3}
{1, 3, 2}
{2, 1, 3}
{2, 3, 1}
{3, 1, 2}
{3, 2, 1}
上記を自分で生成するのは、結構難しいのだが、std::next_permutation または std::prev_permutation を使えば、上記を簡単に生成することが出来るぞ。
next_permutation
まずは、具体的な使い方から見てみよう。
#include <iostream>
#include <algorithm> // for next_permutation
#include <vector>
using namespace std;
int main() {
const int N = 3;
vector<int> v(N);
iota(v.begin(), v.end(), 1); // v に 1, 2, ... N を設定
do {
for(auto x : v) cout << x << " "; cout << "\n"; // v の要素を表示
} while( next_permutation(v.begin(), v.end()) ); // 次の順列を生成
return 0;
}
上記コードをビルド・実行すると、下図のようになる。
1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1
next_permutation() は配列またはコンテナクラスの範囲 [first, last) を引数で受け取り、その範囲の要素の次の順列を生成してくれる。
生成順序は、各順列が昇順になるように生成してくれる。※ 上記の各順列を3桁の数値とみなせば、各数値が昇順に並んでいるでしょ。
例えば、順列 {2, 1, 3} が与えられらた場合は、その次の順番の順列 {2, 3, 1} を生成する。
一番最後の順列 {3, 2, 1} が与えられた場合は、一番最初の順列 {1, 2, 4} を生成する。
next_permutation() は最後から最初の順列を生成した場合のみ false を返し、それ以外の場合は true を返す。
なので、最初に要素を昇順に並べておき、do - while ループで、next_permutation() がfalse を返すまでループすると、 全順列が生成されることになる。
なので、順列の数が予めわかっていれば、初期順列を一番最初のものにする必要は無い。
vector<int> v(3);
v[0], v[1], v[2] に異なる数値を設定
for(int i = 0; i < 6; ++i) { // 異なる数字3つの順列は6通りある
for(auto x : v) cout << x << " "; cout << "\n"; // v の要素を表示
next_permutation(v.begin(), v.end()); // 次の順列を生成
}
順列に同じ数字が入っている場合でも、next_permutation() は正しく動作してくれる。
vector<int> v{1, 2, 2}; // 要素が昇順に並んだ順列
do {
for(auto x : v) cout << x << " "; cout << "\n"; // v の要素を表示
} while( next_permutation(v.begin(), v.end()) ); // 次の順列を生成
上記を実行すると、下記のように出力される。
1 2 2 2 1 2 2 2 1
prev_permutation
prev_permutation と next_permutation の違い、順列を降順に並べた順序で、順列を生成する。 そして、降順に並べた最後から、最初の順序を生成した時点で false を返す。
したがって、初期順列は、要素を降順に並べておく。
vector<int> v{3, 2, 1}; // 要素が降順に並んだ順列
do {
for(auto x : v) cout << x << " "; cout << "\n"; // v の要素を表示
} while( prev_permutation(v.begin(), v.end()) ); // 前の順列を生成
上記を実行すると、下記のように出力される。
3 2 1 3 1 2 2 3 1 2 1 3 1 3 2 1 2 3

