স্টাড :: পরবর্তী_পরিচয় বাস্তবায়ন ব্যাখ্যা


110

আমি কৌতূহল ছিল কীভাবে কীভাবে std:next_permutationবাস্তবায়ন করা হয়েছিল তাই আমি gnu libstdc++ 4.7সংস্করণটি বের করেছি এবং সনাক্তকারীদের স্যানিটাইজ করেছি এবং নীচের ডেমো উত্পাদন করার জন্য ফর্ম্যাটিং করছি ...

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

using namespace std;

template<typename It>
bool next_permutation(It begin, It end)
{
        if (begin == end)
                return false;

        It i = begin;
        ++i;
        if (i == end)
                return false;

        i = end;
        --i;

        while (true)
        {
                It j = i;
                --i;

                if (*i < *j)
                {
                        It k = end;

                        while (!(*i < *--k))
                                /* pass */;

                        iter_swap(i, k);
                        reverse(j, end);
                        return true;
                }

                if (i == begin)
                {
                        reverse(begin, end);
                        return false;
                }
        }
}

int main()
{
        vector<int> v = { 1, 2, 3, 4 };

        do
        {
                for (int i = 0; i < 4; i++)
                {
                        cout << v[i] << " ";
                }
                cout << endl;
        }
        while (::next_permutation(v.begin(), v.end()));
}

আউটপুটটি প্রত্যাশার মতো: http://ideone.com/4nZdx

আমার প্রশ্নগুলি: এটি কীভাবে কাজ করে? মানে কি i, jএবং k? মৃত্যুদণ্ড কার্যকর করার বিভিন্ন অংশে তারা কী মূল্য রাখে? এর সঠিকতার প্রমাণের স্কেচ কী?

স্পষ্টত প্রধান লুপটি প্রবেশের আগে এটি কেবল তুচ্ছ 0 বা 1 উপাদান তালিকার কেসগুলি পরীক্ষা করে। মূল লুপের প্রবেশের সময় আমি শেষ উপাদানটির দিকে ইঙ্গিত করছি (একটি অতীত প্রান্ত নয়) এবং তালিকাটি কমপক্ষে 2 টি উপাদান দীর্ঘ।

মূল লুপের শরীরে কী চলছে?


আরে আপনি কোডটির টুকরোটি কীভাবে সরিয়েছেন? আমি যখন # অন্তর্ভুক্ত <অ্যালগোরিদম> পরীক্ষা করেছিলাম তখন কোডটি সম্পূর্ণ আলাদা ছিল যা আরও ফাংশন নিয়ে গঠিত
মঞ্জুনাথ

উত্তর:


172

আসুন কিছু আদেশের দিকে নজর দিন:

1 2 3 4
1 2 4 3
1 3 2 4
1 3 4 2
1 4 2 3
1 4 3 2
2 1 3 4
...

আমরা কীভাবে এক আদেশ থেকে পরের দিকে যেতে পারি? প্রথমত, আসুন কিছু অন্যভাবে দেখুন। আমরা উপাদানগুলিকে অঙ্ক হিসাবে এবং ক্রমবিন্যাসকে সংখ্যা হিসাবে দেখতে পারি । সমস্যাটি এইভাবে দেখে আমরা ক্রম / সংখ্যাগুলি "আরোহী" ক্রমে অর্ডার করতে চাই

যখন আমরা সংখ্যার অর্ডার করি আমরা "এটিকে স্বল্পতম পরিমাণে বাড়িয়ে" দিতে চাই। উদাহরণস্বরূপ, গণনা করার সময় আমরা 1, 2, 3, 10 গণনা করি না ... কারণ এখনও 4, 5, ... এর মধ্যে রয়েছে এবং যদিও 10 3 এর চেয়ে বড়, তবে অনুপস্থিত সংখ্যা রয়েছে যা দ্বারা অর্জন করা যেতে পারে একটি স্বল্প পরিমাণে 3 বৃদ্ধি। উপরের উদাহরণে আমরা দেখতে পাই যে 1দীর্ঘ সময়ের জন্য প্রথম সংখ্যা হিসাবে রয়ে যায় কারণ শেষ 3 "অঙ্ক" এর অনেকগুলি পুনঃক্রম রয়েছে যা স্বল্প পরিমাণে ক্রমবর্ধমানকে "বৃদ্ধি" করে।

সুতরাং আমরা শেষ পর্যন্ত কখন "ব্যবহার" করব 1? যখন শেষ 3 ডিজিটের আর কোনও অনুমতি নেই।
এবং শেষ 3 অঙ্কের আর কোনও অনুমতি নেই? শেষ 3 টি সংখ্যা যখন অবতরণ ক্রমে থাকে।

আহা! এটি অ্যালগরিদম বোঝার মূল বিষয়। আমরা কেবলমাত্র একটি "অঙ্ক" এর অবস্থানটি পরিবর্তন করি যখন ডানদিকে সমস্ত কিছুই উতরান ক্রমে থাকে কারণ এটি যদি অবতরণ ক্রমে না হয় তবে আরও আরও অনুমোদনের বাকি রয়েছে (অর্থাত্ আমরা অল্প পরিমাণে অনুমানকে "বাড়িয়ে" তুলতে পারি) ।

এবার কোডে ফিরে যাই:

while (true)
{
    It j = i;
    --i;

    if (*i < *j)
    { // ...
    }

    if (i == begin)
    { // ...
    }
}

লুপের প্রথম 2 লাইন থেকে, jএকটি উপাদান এবং iএটির আগে উপাদান।
তারপরে, উপাদানগুলি যদি আরোহী ক্রমে থাকে তবে ( if (*i < *j)) কিছু করুন।
অন্যথায়, যদি পুরো জিনিসটি ক্রমবর্ধমান ক্রমে হয়, ( if (i == begin)) তবে এটিই হ'ল শেষ আদেশ।
অন্যথায়, আমরা চালিয়ে যাচ্ছি এবং আমরা দেখতে পাচ্ছি যে জে এবং আমি মূলত হ্রাস পেয়েছি।

আমরা এখন if (i == begin)অংশটি বুঝতে পারি তাই আমাদের যে অংশটি বুঝতে হবে তা হল if (*i < *j)

এছাড়াও নোট করুন: "তারপরে যদি উপাদানগুলি আরোহী ক্রমে হয় ..." যা আমাদের পূর্ববর্তী পর্যবেক্ষণকে সমর্থন করে যে আমাদের কেবলমাত্র "ডিজিটের কিছু করার দরকার যখন ডানদিকে সমস্ত কিছু" অবতরণ ক্রমে থাকে "। আরোহণের আদেশ ifবিবৃতিটি মূলত বামতম স্থানটি সন্ধান করছে যেখানে "ডানদিকে সমস্ত কিছু অবতরণে রয়েছে"।

আসুন কিছু উদাহরণ আবার দেখুন:

...
1 4 3 2
2 1 3 4
...
2 4 3 1
3 1 2 4
...

আমরা দেখতে পাই যে যখন একটি অঙ্কের ডানদিকে সমস্ত কিছু অস্তিত্বের ক্রমে হয়, আমরা পরবর্তী বৃহত্তম অঙ্কটি পাই এবং এটি সামনে রাখি এবং তারপরে বাকী অঙ্কগুলি আরোহী ক্রমে রাখি

কোডটি দেখুন:

It k = end;

while (!(*i < *--k))
    /* pass */;

iter_swap(i, k);
reverse(j, end);
return true;

ঠিক আছে, যেহেতু ডানদিকে জিনিসগুলি ক্রমবর্ধমান ক্রমে রয়েছে, "পরবর্তী বৃহত্তম অঙ্ক" সন্ধান করতে আমাদের কেবল শেষ থেকে পুনরাবৃত্তি করতে হবে, যা আমরা কোডের প্রথম 3 লাইনে দেখি।

এরপরে, আমরা iter_swap()স্টেটমেন্টটি দিয়ে "পরবর্তী বৃহত্তম অঙ্ক "টিকে সামনের দিকে নিয়ে যাব এবং তারপরে যেহেতু আমরা জানি যে অঙ্কটি পরবর্তী বৃহত্তম ছিল, তাই আমরা জানি যে ডানদিকে অঙ্কগুলি এখনও অবতরণ ক্রমে রয়েছে, সুতরাং এটি আরোহী ক্রমে রাখার জন্য, আমাদের কেবল reverse()এটি করতে হবে।


12
আশ্চর্যজনক ব্যাখ্যা

2
ব্যাখ্যার জন্য ধন্যবাদ! এই অ্যালগরিদমকে ডিক্সিকোগ্রাফিক ক্রমে জেনারেশন বলা হয় । এ জাতীয় অ্যালগরিদমের সংখ্যা রয়েছে Combinatoricsতবে এটি সর্বাধিক ধ্রুপদী।
চেন রো

1
এ জাতীয় অ্যালগরিদমের জটিলতা কী?
ব্যবহারকারী 72708

লেটকোডের ভাল ব্যাখ্যা রয়েছে, leetcode.com/problems/next-permutes/solution
বাইসপজাই

40

জিসিসি বাস্তবায়ন লিক্সোগ্রাফিকাল ক্রমে ক্রমবিন্যাস উত্পন্ন করে। উইকিপিডিয়া এটি নীচে ব্যাখ্যা করেছে:

নিম্নলিখিত অ্যালগরিদম একটি প্রদত্ত অনুমানের পরে ডিক্সিকোগ্রাফিকভাবে পরবর্তী অনুমানের উত্পন্ন করে। এটি স্থানটিতে প্রদত্ত আদেশকে পরিবর্তন করে।

  1. সবচেয়ে বড় সূচক কে সন্ধান করুন যা একটি [কে] <ক [কে + 1]] যদি এরকম কোনও সূচক না থাকে তবে অনুক্রমটি হ'ল শেষ অনুমান perm
  2. সবচেয়ে বড় সূচি l যেমন একটি [কে] <ক [এল] সন্ধান করুন। যেহেতু কে + 1 এমন একটি সূচক, তাই l সংজ্ঞায়িত এবং কে <এলকে সন্তুষ্ট করে।
  3. একটি [কে] একটি [এল] দিয়ে অদলবদল করুন।
  4. চূড়ান্ত উপাদানটিকে [k + 1] থেকে চূড়ান্ত উপাদান a [n] সহ উল্টো করুন।

AFAICT, সমস্ত বাস্তবায়ন একই ক্রম উত্পন্ন করে।
এমএসএলটাররা

12

আর্ট অফ কম্পিউটার প্রোগ্রামিংয়ের বিভাগ 7.২.১.২ এবং .2.২.১.৩ বিভাগে নূথ এই অ্যালগরিদম এবং এর সাধারণীকরণ সম্পর্কে গভীরতার সাথে । তিনি এটিকে "অ্যালগরিদম এল" বলেছেন - স্পষ্টতই এটি ১৩ শ শতাব্দীর পুরানো।


1
আপনি বইয়ের নাম উল্লেখ করতে পারেন?
গ্রোবার

3
টিএওসিপি = কম্পিউটার প্রোগ্রামিংয়ের আর্ট

9

অন্যান্য স্ট্যান্ডার্ড গ্রন্থাগার অ্যালগোরিদম ব্যবহার করে এখানে একটি সম্পূর্ণ বাস্তবায়ন:

template <typename I, typename C>
    // requires BidirectionalIterator<I> && Compare<C>
bool my_next_permutation(I begin, I end, C comp) {
    auto rbegin = std::make_reverse_iterator(end);
    auto rend = std::make_reverse_iterator(begin);
    auto rsorted_end = std::is_sorted_until(rbegin, rend, comp);
    bool has_more_permutations = rsorted_end != rend;
    if (has_more_permutations) {
        auto next_permutation_rend = std::upper_bound(
            rbegin, rsorted_end, *rsorted_end, comp);
        std::iter_swap(rsorted_end, next_permutation_rend);
    }
    std::reverse(rbegin, rsorted_end);
    return has_more_permutations;
}

ডেমো


1
এটি ভাল পরিবর্তনশীল নাম এবং উদ্বেগের পৃথকীকরণের গুরুত্বকে বোঝায়। is_final_permutationএর চেয়ে বেশি তথ্যবহুল begin == end - 1। কল করা is_sorted_until/ upper_boundক্রিয়াকলাপ যুক্তিকে সেই অপারেশনগুলি থেকে পৃথক করে এবং এটিকে আরও বেশি বোধগম্য করে তোলে। অতিরিক্ত রৌপ্য একটি বাইনারি অনুসন্ধান, যখন while (!(*i < *--k));লিনিয়ার হয়, তাই এটি আরও পারফরম্যান্ট।
জোনাথন গাওরিচ

1

সেখানে একটি স্ব ব্যাখ্যামূলক সম্ভব implemetation হয় cppreference ব্যবহার <algorithm>

template <class Iterator>
bool next_permutation(Iterator first, Iterator last) {
    if (first == last) return false;
    Iterator i = last;
    if (first == --i) return false;
    while (1) {
        Iterator i1 = i, i2;
        if (*--i < *i1) {
            i2 = last;
            while (!(*i < *--i2));
            std::iter_swap(i, i2);
            std::reverse(i1, last);
            return true;
        }
        if (i == first) {
            std::reverse(first, last);
            return false;
        }
    }
}

লিঙ্কোগ্রাফিকভাবে পরের ক্রমবিন্যাসে (স্থানটিতে) কন্টেন্টটি পরিবর্তন করুন এবং উপস্থিত থাকলে সত্যায় ফিরে আসুন অন্যথায় বাছাই করুন এবং এটি উপস্থিত না থাকলে মিথ্যা প্রত্যাবর্তন করুন।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.