মোট দৈর্ঘ্যের চেয়ে কম জায়গাগুলির জন্য এসটিএল ব্যবহার করে কীভাবে সি ++ এ ক্রোমুটেশন তৈরি করবেন


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) 

পাইথন ডেমো যুক্ত করার জন্য @ জারোড 42 ধন্যবাদ :)
d4rk4ng31

আমার পক্ষে এটি করতে হয়েছিল, যেহেতু আমি অজগর ফলাফলটি জানি না, তবে আমি নিশ্চিত যে আমি সি ++ তে এটি কীভাবে করব know
জারোড 42

পার্শ্ব নোট হিসাবে, আপনি কীভাবে ডুপ্লিকেট ইনপুট পরিচালনা করতে চান (1, 1)? পাইথন একাধিক বিন্যাসন সদৃশ প্রদান করে [(1, 1), (1, 1)]যেহেতু, std::next_permutationএড়ানোর সদৃশ (শুধুমাত্র {1, 1})।
জারোড 42

আহ .. না। কোনও সদৃশ নেই
d4rk4ng31

এছাড়াও, দয়া করে প্রশ্নের নিজের উত্তরটি একবার দেখুন।
d4rk4ng31

উত্তর:


6

আপনি 2 টি লুপ ব্যবহার করতে পারেন:

  • প্রতিটি এন-টিপল নিন
  • সেই এন-টিপলের ক্রমবর্ধনের উপর পুনরাবৃত্তি করুন
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()));
}

ডেমো


এই কোড সময় জটিলতা কি হবে? এটি কি গড় ক্ষেত্রে ও (জায়গাগুলি_ প্রয়োজনীয় * এন) এবং সবচেয়ে খারাপ ক্ষেত্রে ও (এন ^ 2) হবে? আমি ও (এন)
কেও

2
@ d4rk4ng31: আমরা প্রকৃতপক্ষে একবারে প্রতিটি আদেশের মুখোমুখি হই। std::next_permutationঅদলবদল (লিনিয়ার) হিসাবে গণ্য হওয়ায় এর জটিলতা "অস্পষ্ট"। সাব ভেক্টরের নিষ্কাশন উন্নত করা যেতে পারে, তবে আমি মনে করি না এটি জটিলতা পরিবর্তন করে। ক্রম সংযোজন সংখ্যা ভেক্টরের আকারের উপর নির্ভর করে, সুতরাং 2 টি প্যারামিটার স্বতন্ত্র নয়।
জারোড 42

তা কি হওয়া উচিত নয় std::vector<T>& v?
এলএফ

@ এলএফ: এটি উদ্দেশ্যমূলক। আমি বিবেচনা করি যে আমাকে কলারের মান পরিবর্তন করতে হবে না (আমি vবর্তমানে সারণি করছি )। আমি কনস্টেন্ট রেফারেন্স দিয়ে যেতে পারি এবং এর পরিবর্তে শরীরে বাছাই করা একটি অনুলিপি তৈরি করতে পারি।
জারোড 42

@ জারোড 42 ওহ দুঃখিত, আমি পুরোপুরি কোডটি ভুলভাবে লিখেছি। হ্যাঁ, মান দ্বারা পাস করা এখানে সঠিক জিনিস thing
এলএফ

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

এখানে থেকে runnable কোড ideone


2
আপনি স্বাক্ষর দিয়ে আরও নকল করতে পারেনbool nextPartialPermutation(It begin, It mid, It end)
জারোড 42


@ জারোড 42, এটি একটি দুর্দান্ত সমাধান। আপনার এটি উত্তর হিসাবে যুক্ত করা উচিত ...
জোসেফ উড

আমার প্রাথমিক ধারণাটি ছিল আপনার উত্তরটি উন্নত করা, তবে ঠিক আছে added
জারোড 42

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

ডেমো


0

কিছু চিন্তাভাবনার পরে এটি আমার সমাধান

#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);একটি বড় ফ্যাক্টর অপসারণ করা যেতে পারে ।
জারোড 42

সময়ের জটিলতা এখন কী?
d4rk4ng31

1
আমি বলব O(nb_total_perm * log(nb_res))( nb_total_permযা বেশিরভাগ factorial(job_list.size())এবং nb_resফলাফলের আকার permutations.size():), তাই এখনও খুব বড়। (তবে এখন আপনি এভগের বিপরীতে ডুপ্লিকেট ইনপুট পরিচালনা করেন)
জারোড 42
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.