कुल लंबाई से कम स्थानों की संख्या के लिए एसटीएल का उपयोग करके सी ++ में क्रमचय कैसे बनाया जाए


15

मैं एक है c++ vectorके साथ std::pair<unsigned long, unsigned long>वस्तुओं। मैं सदिश की वस्तुओं के उपयोग की अनुमति उत्पन्न करने की कोशिश कर रहा हूं std::next_permutation()। हालाँकि, मैं चाहता हूं कि क्रमपरिवर्तन एक दिए गए आकार का हो, आप जानते हैं, permutationsअजगर में फ़ंक्शन के समान है जहां अपेक्षित वापसी क्रमांकन का आकार निर्दिष्ट है।

मूल रूप से, के c++बराबर है

import itertools

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

पायथन डेमो

(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) 

धन्यवाद @ Jarod42 कि अजगर डेमो जोड़ने के लिए :)
d4rk4ng31

यह मेरी तरफ से करना था, क्योंकि मुझे अजगर परिणाम नहीं पता है, लेकिन मुझे यकीन है कि मुझे पता है कि इसे सी ++ में कैसे करना है।
Jarod42

एक साइड नोट के रूप में, आप डुप्लिकेट इनपुट को (1, 1)कैसे संभालना चाहते हैं ? अजगर क्रमपरिवर्तन डुप्लिकेट प्रदान करता है [(1, 1), (1, 1)], जबकि std::next_permutationडुप्लिकेट (केवल {1, 1}) से बचें ।
Jarod42

उह .. नहीं। कोई डुप्लिकेट नहीं
d4rk4ng31

जवाबों:


6

आप 2 छोरों का उपयोग कर सकते हैं:

  • प्रत्येक n-tuple लें
  • उस 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()));
}

डेमो


इस कोड की समय जटिलता क्या होगी? क्या यह औसत केस के लिए O (स्थान_अंकित * n) और सबसे खराब स्थिति के लिए O (n ^ 2) होगा? मैं सबसे अच्छे मामले के लिए O (n) का अनुमान लगा रहा हूं, अर्थात एक जगह
d4rk4ng31

2
@ d4rk4ng31: हम वास्तव में केवल एक बार प्रत्येक क्रमपरिवर्तन का सामना करते हैं। जटिलता std::next_permutation"अस्पष्ट" है क्योंकि यह स्वैप (रैखिक) को गिनता है। उप वेक्टर के निष्कर्षण में सुधार किया जा सकता है, लेकिन मुझे नहीं लगता कि यह जटिलता को बदलता है। इसके अलावा क्रमपरिवर्तन की संख्या वेक्टर आकार पर निर्भर करती है, इसलिए 2 पैरामीटर स्वतंत्र नहीं हैं।
Jarod42

ऐसा नहीं होना चाहिए std::vector<T>& v?
एलएफ

@ एलएफ: यह उद्देश्य पर है। मुझे लगता है कि मुझे कॉलर का मान बदलने की ज़रूरत नहीं है (मैं vवर्तमान में सॉर्ट करता हूं )। मैं कॉन्स्ट रेफरेंस से गुजर सकता हूं, और इसके बजाय बॉडी में एक सॉर्ट की गई कॉपी बना सकता हूं।
Jarod42

@ Jarod42 ओह क्षमा करें, मैंने कोड को पूरी तरह से गलत बताया है। हाँ, मूल्य से गुजरना यहाँ पर सही है।
एलएफ

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क्रमपरिवर्तन को चिह्नित किया <

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

यहाँ विचारधारा से चलने योग्य कोड है


2
आप हस्ताक्षर के साथ और भी अधिक नकल कर सकते हैंbool nextPartialPermutation(It begin, It mid, It end)
Jarod42


@ Jarod42, यह एक बहुत अच्छा समाधान है। आपको इसे एक उत्तर के रूप में जोड़ना चाहिए ...
जोसेफ वुड

मेरा प्रारंभिक विचार आपके उत्तर को सुधारना था, लेकिन ठीक है, जोड़ा गया।
Jarod42

3

टोहिया इंटरफ़ेस के साथ जोसेफ वुड का जवाब देना, आपके पास एक तरीका हो सकता है 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
मेरे बराबर नहीं है, यह एवग के उत्तर के बराबर है, (लेकिन ईवीजी स्काइप्स अधिक कुशलता से नकल करता है)। permuteवास्तव में केवल set.insert(vec);एक बड़े कारक को हटा सकता है।
Jarod42

अब समय की जटिलता क्या है?
d4rk4ng31

1
मैं कहूंगा O(nb_total_perm * log(nb_res))( nb_total_permजो कि ज्यादातर है factorial(job_list.size())और nb_resपरिणाम का आकार :) permutations.size(), इसलिए अभी भी बहुत बड़ा है। (लेकिन अब आप Evg के विपरीत डुप्लिकेट इनपुट संभालते हैं)
Jarod42
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.