全長より短い桁数のSTLを使用してC ++で順列を作成する方法


15

私が持っているc++ vectorstd::pair<unsigned long, unsigned long>、オブジェクト。私はを使用してベクトルのオブジェクトの順列を生成しようとしていますstd::next_permutation()。ただし、permutations予想される返される順列のサイズが指定されているpython の関数と同様に、順列を特定のサイズにする必要があります。

基本的には、c++同等の

import itertools

list = [1,2,3,4,5,6,7]
for permutation in itertools.permutations(list, 3):
    print(permutation)

Pythonデモ

(1, 2, 3)                                                                                                                                                                            
(1, 2, 4)                                                                                                                                                                            
(1, 2, 5)                                                                                                                                                                            
(1, 2, 6)                                                                                                                                                                            
(1, 2, 7)                                                                                                                                                                            
(1, 3, 2)
(1, 3, 4)
..
(7, 5, 4)                                                                                                                                                                            
(7, 5, 6)                                                                                                                                                                            
(7, 6, 1)                                                                                                                                                                            
(7, 6, 2)                                                                                                                                                                            
(7, 6, 3)                                                                                                                                                                            
(7, 6, 4)                                                                                                                                                                            
(7, 6, 5) 

そのPythonデモを追加してくれて@ Jarod42に感謝します:)
d4rk4ng31

私はpythonの結果がわからないので、私の側でそれをしなければなりませんでしたが、C ++でそれを行う方法を知っているとかなり確信していました。
Jarod42

補足として、重複した入力をどのように処理し(1, 1)ますか?Pythonの順列は重複を提供しますが[(1, 1), (1, 1)]、重複はstd::next_permutation避けます(のみ{1, 1})。
Jarod42

ええと。重複なし
d4rk4ng31

回答:


6

2つのループを使用する場合があります。

  • 各nタプルを取る
  • そのnタプルの順列を反復する
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()));
}

デモ


このコードの時間の複雑さはどうなりますか?平均的な場合はO(places_required * n)、最悪の場合はO(n ^ 2)になりますか?私はまた、最良の場合、すなわち、1つの場所のためのO(n)を推測しています
d4rk4ng31を

2
@ d4rk4ng31:実際、各順列は1回だけ発生します。の複雑さは、std::next_permutationスワップ(線形)をカウントするため、「不明確」です。サブベクトルの抽出は改善できますが、複雑さを変えるとは思いません。さらに、順列の数はベクトルサイズに依存するため、2パラメーターは独立していません。
Jarod42

そうじゃないのstd::vector<T>& v
LF

@LF:意図的なものです。私は呼び出し元の値を変更する必要がないと考えています(私はv現在ソートしています)。const参照で渡し、代わりに本文にソートされたコピーを作成します。
Jarod42

@ Jarod42申し訳ありませんが、コードを完全に読み間違えました。ええ、値渡しはここで正しいことです。
LF

4

効率が主な問題ではない場合は、すべての順列を反復処理し、サフィックスごとに異なる順列のみを選択してそれらをスキップでき(N - k)!ます。たとえばN = 4, k = 2、には順列があります。

12 34 <
12 43
13 24 <
13 42
14 23 <
14 32
21 34 <
21 43
23 14 <
23 41
24 13 <
24 31
...

ここでは、わかりやすくするためにスペースを挿入し、各(N-k)! = 2! = 2-nd順列をでマークしました<

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 << ' ';
    });
}

出力:

12 13 14 21 23 24 31 32 34 41 42 43

3

以下は、std::next_permutation直接使用しない効率的なアルゴリズムですが、その機能の働き馬を利用しています。つまり、std::swapそしてstd::reverse。プラスとして、それは辞書式順序です。

#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());
    }
}

そしてそれを私たちが持っていると呼びます:

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;
}

出力は次のとおりです。

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

ここにideoneから実行可能なコードがあります


2
署名でさらに模倣することもできますbool nextPartialPermutation(It begin, It mid, It end)
Jarod42


@ Jarod42、それは本当に素晴らしい解決策です。回答として追加する必要があります...
ジョセフウッド

私の最初のアイデアはあなたの答えを改善することでしたが、まあ、追加しました。
Jarod42

3

イテレータインターフェースを使用してJoseph Woodの回答を回すと、次のようなメソッドがある場合がありますstd::next_permutation

template <typename IT>
bool next_partial_permutation(IT beg, IT mid, IT end) {
    if (beg == mid) { return false; }
    if (mid == end) { return std::next_permutation(beg, end); }

    auto p1 = mid;

    while (p1 != end && !(*(mid - 1) < *p1))
        ++p1;

    if (p1 != end) {
        std::swap(*p1, *(mid - 1));
        return true;
    } else {
        std::reverse(mid, end);
        auto p3 = std::make_reverse_iterator(mid);

        while (p3 != std::make_reverse_iterator(beg) && !(*p3 < *(p3 - 1)))
            ++p3;

        if (p3 == std::make_reverse_iterator(beg)) {
            std::reverse(beg, end);
            return false;
        }

        auto p2 = end - 1;

        while (!(*p3 < *p2))
            --p2;

        std::swap(*p3, *p2);
        std::reverse(p3.base(), end);
        return true;
    }
}

デモ


1

これはいくつかの考えの後の私の解決策です

#include <algorithm>
#include <iostream>
#include <set>
#include <vector>

int main() {
    std::vector<int> job_list;
    std::set<std::vector<int>> permutations;
    for (unsigned long i = 0; i < 7; i++) {
        int job;
        std::cin >> job;
        job_list.push_back(job);
    }
    std::sort(job_list.begin(), job_list.end());
    std::vector<int> original_permutation = job_list;
    do {
        std::next_permutation(job_list.begin(), job_list.end());
        permutations.insert(std::vector<int>(job_list.begin(), job_list.begin() + 3));
    } while (job_list != original_permutation);

    for (auto& permutation : permutations) {
        for (auto& pair : permutation) {
            std::cout << pair << " ";
        }
        std::endl(std::cout);
    }

    return 0;
}

あなたの考えをコメントしてください


2
私と同等ではなく、Evgの回答と同等です(ただし、Evgは重複をより効率的にスキップします)。permute実際にはset.insert(vec);、大きな要因を取り除くだけの可能性があります。
Jarod42

現在、時間はどれくらい複雑ですか?
d4rk4ng31

1
私はO(nb_total_perm * log(nb_res))nb_total_permほとんどが結果のサイズです)factorial(job_list.size())と言うでしょう、それでまだ大きすぎます。(ただし、Evgとは逆の重複入力を処理する)nb_respermutations.size()
Jarod42
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.