मैं एक ही तरह से दो वैक्टर को कैसे माप सकता हूं, मापदंड के साथ जो वैक्टर में से केवल एक का उपयोग करता है?


85

मैं एक ही तरह से दो वैक्टर को कैसे माप सकता हूं, मापदंड के साथ जो वैक्टर में से केवल एक का उपयोग करता है?

उदाहरण के लिए, मान लें कि मेरे पास एक ही आकार के दो वैक्टर हैं:

vector<MyObject> vectorA;
vector<int> vectorB;

मैं तो vectorAकुछ तुलना समारोह का उपयोग कर सॉर्ट करता हूं । कि छँटाई reordered vectorA। मैं कैसे एक ही reordering के लिए लागू किया जा सकता है vectorB?


एक विकल्प एक संरचना बनाना है:

struct ExampleStruct {
    MyObject mo;
    int i;
};

और फिर एक वेक्टर को सॉर्ट करें जिसमें एक एकल वेक्टर की सामग्री शामिल है vectorAऔर vectorBज़िप की गई है:

// vectorC[i] is vectorA[i] and vectorB[i] combined
vector<ExampleStruct> vectorC;

यह एक आदर्श समाधान की तरह प्रतीत नहीं होता है। क्या अन्य विकल्प हैं, खासकर सी ++ 11 में?


क्या आप शायद कुछ इनपुट और संबंधित वांछित आउटपुट के साथ एक उदाहरण प्रदान कर सकते हैं? मुझे इस सवाल को समझने में परेशानी हो रही है
एंडी प्रॉल

मुझे लगता है कि वह (प्रभावी रूप से) सामग्री को छांटना चाहता है vectorAऔर vectorBदोनों सामग्री से vectorB
मूइंग डक

मुझे लगता है कि यह प्रश्न एक सटीक डुप्लिकेट के पास है यदि कोई सटीक डुप्लिकेट नहीं है
मिंग डक

एक तीसरी वेक्टर (सूचकांकों में 0, ... वेक्टरए.साइज़ ()), वेक्टरए में मूल्यों के आधार पर और वेक्टरबी पर उन सूचकांकों को "लागू" करने के बारे में क्या? जैसे की stackoverflow.com/a/10581051/417197
André

व्यक्तिगत रूप से, मैं बल्कि ए vector<pair<MyObject, int>>। तब आपको सिंक से बाहर होने वाली दो सूचियों के बारे में चिंता करने की ज़रूरत नहीं होगी; एक प्रकार के डेटा के दोनों सेटों को एक साथ पुन: व्यवस्थित करता है। और लिखने के लिए कोई अतिरिक्त संरचना नहीं है।
cHao

जवाबों:


118

एक क्रमपरिवर्तन ढूँढना

एक std::vector<T>और एक तुलना को देखते हुए T, हम चाहते हैं कि यदि आप इस तुलना का उपयोग करके वेक्टर को क्रमबद्ध करने के लिए उपयोग करते हैं तो आप इसका उपयोग कर पाएंगे।

template <typename T, typename Compare>
std::vector<std::size_t> sort_permutation(
    const std::vector<T>& vec,
    Compare& compare)
{
    std::vector<std::size_t> p(vec.size());
    std::iota(p.begin(), p.end(), 0);
    std::sort(p.begin(), p.end(),
        [&](std::size_t i, std::size_t j){ return compare(vec[i], vec[j]); });
    return p;
}

एक क्रमपरिवर्तन लागू करना

एक std::vector<T>और एक क्रमचय को देखते हुए , हम एक नए निर्माण में सक्षम होना चाहते हैं std::vector<T>जो क्रमपरिवर्तन के अनुसार पुन: व्यवस्थित हो।

template <typename T>
std::vector<T> apply_permutation(
    const std::vector<T>& vec,
    const std::vector<std::size_t>& p)
{
    std::vector<T> sorted_vec(vec.size());
    std::transform(p.begin(), p.end(), sorted_vec.begin(),
        [&](std::size_t i){ return vec[i]; });
    return sorted_vec;
}

आप निश्चित apply_permutationरूप से सदिश को बदलने के लिए संशोधित कर सकते हैं जो आप इसे एक नई छांटी गई प्रति को वापस करने के बजाय देते हैं। यह दृष्टिकोण अभी भी रैखिक समय जटिलता है और आपके वेक्टर में प्रति आइटम एक बिट का उपयोग करता है। सैद्धांतिक रूप से, यह अभी भी रैखिक अंतरिक्ष जटिलता है; लेकिन, व्यवहार में, जब sizeof(T)स्मृति उपयोग में बड़ी कमी नाटकीय हो सकती है। ( विवरण देखें )

template <typename T>
void apply_permutation_in_place(
    std::vector<T>& vec,
    const std::vector<std::size_t>& p)
{
    std::vector<bool> done(vec.size());
    for (std::size_t i = 0; i < vec.size(); ++i)
    {
        if (done[i])
        {
            continue;
        }
        done[i] = true;
        std::size_t prev_j = i;
        std::size_t j = p[i];
        while (i != j)
        {
            std::swap(vec[prev_j], vec[j]);
            done[j] = true;
            prev_j = j;
            j = p[j];
        }
    }
}

उदाहरण

vector<MyObject> vectorA;
vector<int> vectorB;

auto p = sort_permutation(vectorA,
    [](T const& a, T const& b){ /*some comparison*/ });

vectorA = apply_permutation(vectorA, p);
vectorB = apply_permutation(vectorB, p);

साधन


4
मेरे संकलक में, मुझे "तुलना और तुलना" को "तुलना करना" बदलना पड़ा।
लघुशंका

2
Std :: iota function (vs2012) को चलाने के लिए मुझे हेडर <संख्यात्मक> को भी शामिल करना होगा।
स्मिथ

1
" आप निश्चित apply_permutationरूप से सदिश को बदलने के लिए संशोधित कर सकते हैं जो आप एक नई छांटी गई प्रति को लौटाने के बजाय देते हैं। " क्या आप इसे भी उसी तरह से लागू करेंगे apply_permutation?
ildjarn

2
@ildjarn मैंने आपके सुझाव के साथ उत्तर को अपडेट कर दिया है।
तीमुथियुस शील्ड

2
उत्कृष्ट स्निपेट के लिए धन्यवाद! हालाँकि, Sort_permutation घोषणा Compare& compareको const होना चाहिए। अन्यथा आप std :: less <T> या समान का उपयोग नहीं कर सकते।
कोटकोटकोटा

9

साथ सीमा-v3 , यह आसान है, तरह एक ज़िप दृश्य है:

std::vector<MyObject> vectorA = /*..*/;
std::vector<int> vectorB = /*..*/;

ranges::v3::sort(ranges::view::zip(vectorA, vectorB));

या स्पष्ट रूप से प्रक्षेपण का उपयोग करें:

ranges::v3::sort(ranges::view::zip(vectorA, vectorB),
                 std::less<>{},
                 [](const auto& t) -> decltype(auto) { return std::get<0>(t); });

डेमो


VS2017 पर कोशिश की, कुछ भी संकलित या काम नहीं किया, कोई फर्क नहीं पड़ता कि मैंने क्या कोशिश की। यह लाइब्रेरी VS2019, GCC और LLVM पर काम करने के लिए जानी जाती है।
कंटैंगो

4

मैं जिस एक्सटेंशन के साथ आया हूं, उसमें मैं योगदान देना चाहूंगा। लक्ष्य एक ही समय में एक सरल वाक्यविन्यास का उपयोग करके कई वैक्टर को क्रमबद्ध करने में सक्षम होना है।

sortVectorsAscending(criteriaVec, vec1, vec2, ...)

एल्गोरिथ्म एक टिमोथी के रूप में ही प्रस्तावित है लेकिन वैरेडिक टेम्प्लेट का उपयोग कर रहा है , इसलिए हम एक ही समय में मनमाने प्रकार के कई वैक्टर को सॉर्ट कर सकते हैं।

यहाँ कोड स्निपेट है:

template <typename T, typename Compare>
void getSortPermutation(
    std::vector<unsigned>& out,
    const std::vector<T>& v,
    Compare compare = std::less<T>())
{
    out.resize(v.size());
    std::iota(out.begin(), out.end(), 0);

    std::sort(out.begin(), out.end(),
        [&](unsigned i, unsigned j){ return compare(v[i], v[j]); });
}

template <typename T>
void applyPermutation(
    const std::vector<unsigned>& order,
    std::vector<T>& t)
{
    assert(order.size() == t.size());
    std::vector<T> st(t.size());
    for(unsigned i=0; i<t.size(); i++)
    {
        st[i] = t[order[i]];
    }
    t = st;
}

template <typename T, typename... S>
void applyPermutation(
    const std::vector<unsigned>& order,
    std::vector<T>& t,
    std::vector<S>&... s)
{
    applyPermutation(order, t);
    applyPermutation(order, s...);
}

template<typename T, typename Compare, typename... SS>
void sortVectors(
    const std::vector<T>& t,
    Compare comp,
    std::vector<SS>&... ss)
{
    std::vector<unsigned> order;
    getSortPermutation(order, t, comp);
    applyPermutation(order, ss...);
}

// make less verbose for the usual ascending order
template<typename T, typename... SS>
void sortVectorsAscending(
    const std::vector<T>& t,
    std::vector<SS>&... ss)
{
    sortVectors(t, std::less<T>(), ss...);
}

Ideone में इसका परीक्षण करें ।

मैं इस ब्लॉग पोस्ट में इसे थोड़ा बेहतर समझाता हूं ।


3

क्रमपरिवर्तन का उपयोग करते हुए जगह-जगह छँटाई

मैं टिमोथी की तरह एक क्रमचय का उपयोग करूंगा, हालांकि यदि आपका डेटा बहुत बड़ा है और आप सॉर्ट किए गए वेक्टर के लिए अधिक मेमोरी आवंटित नहीं करना चाहते हैं तो आपको इसे जगह में करना चाहिए । यहाँ एक O (n) (रैखिक जटिलता) का एक उदाहरण है, जिसमें क्रमपरिवर्तन का उपयोग करके छँटाई की गई है :

ट्रिक को क्रमबद्धता और रिवर्स क्रमपरिवर्तन प्राप्त करना है, यह जानने के लिए कि अंतिम सॉर्टिंग चरण द्वारा ओवरराइट किए गए डेटा को कहां रखा जाए।

template <class K, class T> 
void sortByKey(K * keys, T * data, size_t size){
    std::vector<size_t> p(size,0);
    std::vector<size_t> rp(size);
    std::vector<bool> sorted(size, false);
    size_t i = 0;

    // Sort
    std::iota(p.begin(), p.end(), 0);
    std::sort(p.begin(), p.end(),
                    [&](size_t i, size_t j){ return keys[i] < keys[j]; });

    // ----------- Apply permutation in-place ---------- //

    // Get reverse permutation item>position
    for (i = 0; i < size; ++i){
        rp[p[i]] = i;
    }

    i = 0;
    K savedKey;
    T savedData;
    while ( i < size){
        size_t pos = i;
        // Save This element;
        if ( ! sorted[pos] ){
            savedKey = keys[p[pos]];
            savedData = data[p[pos]];
        }
        while ( ! sorted[pos] ){
            // Hold item to be replaced
            K heldKey  = keys[pos];
            T heldData = data[pos];
            // Save where it should go
            size_t heldPos = rp[pos];

            // Replace 
            keys[pos] = savedKey;
            data[pos] = savedData;

            // Get last item to be the pivot
            savedKey = heldKey;
            savedData = heldData;

            // Mark this item as sorted
            sorted[pos] = true;

            // Go to the held item proper location
            pos = heldPos;
        }
        ++i;
    }
}

1
हालाँकि ऐसा लगता है कि यह O (N ^ 2) है, यह नहीं है। यदि आइटम सॉर्ट नहीं किया गया है, तो केवल आंतरिक निष्पादित किया जाता है। डेटा को सॉर्ट करते समय भीतर की तरह, यह पुनरावृत्तियों के समय बाहरी रूप से बंद हो जाता है ...
MtCS

2
  1. अपने व्यक्तिगत वैक्टर से जोड़े का एक वेक्टर बनाएं।
    जोड़ियों के वेक्टर को जोड़े के वेक्टर में
    जोड़ना

  2. एक कस्टम सॉर्ट तुलनित्र बनाएं:
    कस्टम ऑब्जेक्ट्स के एक वेक्टर को सॉर्ट करना
    http://rosettacode.org/wiki/Sort_using_a_custom_comparator#C.2B.2B

  3. जोड़े के अपने वेक्टर को क्रमबद्ध करें।

  4. अलग-अलग वैक्टर में जोड़े के अपने वेक्टर को अलग करें।

  5. इन सभी को एक फंक्शन में रखें।

कोड:

std::vector<MyObject> vectorA;
std::vector<int> vectorB;

struct less_than_int
{
    inline bool operator() (const std::pair<MyObject,int>& a, const std::pair<MyObject,int>& b)
    {
        return (a.second < b.second);
    }
};

sortVecPair(vectorA, vectorB, less_than_int());

// make sure vectorA and vectorB are of the same size, before calling function
template <typename T, typename R, typename Compare>
sortVecPair(std::vector<T>& vecA, std::vector<R>& vecB, Compare cmp)
{

    std::vector<pair<T,R>> vecC;
    vecC.reserve(vecA.size());
    for(int i=0; i<vecA.size(); i++)
     {
        vecC.push_back(std::make_pair(vecA[i],vecB[i]);   
     }

    std::sort(vecC.begin(), vecC.end(), cmp);

    vecA.clear();
    vecB.clear();
    vecA.reserve(vecC.size());
    vecB.reserve(vecC.size());
    for(int i=0; i<vecC.size(); i++)
     {
        vecA.push_back(vecC[i].first);
        vecB.push_back(vecC[i].second);
     }
}

संदर्भों के जोड़े के वेक्टर?
मूइंग डक

मुझे वही मिलता है जो आपका मतलब है, लेकिन संदर्भों के जोड़े आसानी से काम नहीं कर रहे हैं।
ruben2020

1
@ ruben2020: यह कर सकता है , लेकिन ऐसा सिर्फ बैंड-इश्यू को प्रभावित करता है। यदि आपके पास डेटा के दो टुकड़े पर्याप्त रूप से जुड़े हुए हैं जो कि एक को छाँटना चाहिए तो दूसरे को छाँटना चाहिए, ऐसा लगेगा कि आपके पास वास्तव में जो नहीं है वह एक अभी तक एकीकृत वस्तु है।
cHao

1

मैं यह मान रहा हूं कि वेक्टरए और वेक्टरबी की लंबाई समान है। आप एक और वेक्टर बना सकते हैं, चलो इसे पॉस कहते हैं, जहां:

pos[i] = the position of vectorA[i] after sorting phase

और फिर, आप पीओएस का उपयोग करके वेक्टरब को सॉर्ट कर सकते हैं, अर्थात जहां वेक्टरबर्सट बना सकते हैं:

vectorBsorted[pos[i]] = vectorB[i]

और फिर वेक्टरबसर्ड को अनुक्रमणिका के समान क्रमांकन द्वारा सदिश के रूप में क्रमबद्ध किया जाता है।


0

मुझे यकीन नहीं है कि अगर यह काम करता है, लेकिन मैं इस तरह से कुछ का उपयोग करेगा। दो वैक्टर को क्रमबद्ध करने के लिए उदाहरण के लिए, मैं अवरोही बुलबुला सॉर्ट विधि और वेक्टर जोड़े का उपयोग करूंगा।

बबलिंग सॉर्टिंग के लिए, मैं एक फ़ंक्शन बनाऊंगा जिसमें एक वेक्टर जोड़ी की आवश्यकता होती है।

void bubbleSort(vector< pair<MyObject,int> >& a)
{
    bool swapp = true;
    while (swapp) {
        int key;
        MyObject temp_obj;
        swapp = false;
        for (size_t i = 0; i < a.size() - 1; i++) {
            if (a[i].first < a[i + 1].first) {
                temp_obj = a[i].first;
                key = a[i].second;

                a[i].first = a[i + 1].first;
                a[i + 1].first = temp_obj;

                a[i].second = a[i + 1].second;
                a[i + 1].second = key;

                swapp = true;
            }
        }
    }
}

उसके बाद मैं आपके 2 वेक्टर मानों को एक वेक्टर जोड़ी में डालूँगा। यदि आप एक ही समय में मान जोड़ने में सक्षम हैं तो बबल सॉर्ट फ़ंक्शन को कॉल करने की तुलना में इस एक का उपयोग करें।

vector< pair<MyObject,int> > my_vector;

my_vector.push_back( pair<MyObject,int> (object_value,int_value));

bubbleSort(my_vector);

यदि आप अपने 2 वैक्टर में जोड़ने के बाद मूल्यों का उपयोग करना चाहते हैं, तो आप इस का उपयोग कर सकते हैं और बबल सॉर्ट फ़ंक्शन को कॉल कर सकते हैं।

vector< pair<MyObject,int> > temp_vector;

for (size_t i = 0; i < vectorA.size(); i++) {
            temp_vector.push_back(pair<MyObject,int> (vectorA[i],vectorB[i]));
        }

bubbleSort(temp_vector);

आशा है कि ये आपकी मदद करेगा। सादर, कनेर


0

मैंने हाल ही में एक उचित ज़िप इटरेटर लिखा है जो stl एल्गोरिदम के साथ काम करता है। यह आपको इस तरह कोड का उत्पादन करने की अनुमति देता है:

std::vector<int> a{3,1,4,2};
std::vector<std::string> b{"Alice","Bob","Charles","David"};

auto zip = Zip(a,b);
std::sort(zip.begin(), zip.end());

for (const auto & z: zip) std::cout << z << std::endl;

यह एक ही हेडर में निहित है और एकमात्र आवश्यकता C ++ 17 है। GitHub पर इसे देखें ।

कोडरेव्यू पर एक पोस्ट भी है जिसमें सभी स्रोत कोड शामिल हैं।


0

टिमोथी शील्ड्स के जवाब के आधार पर।
एक छोटे से apply_permutaionमोड़ के साथ आप एक गुना अभिव्यक्ति के उपयोग के साथ एक ही बार में विभिन्न प्रकार के कई वैक्टरों को क्रमपरिवर्तन लागू कर सकते हैं।

template <typename T, typename... Ts>
void apply_permutation(const std::vector<size_t>& perm, std::vector<T>& v, std::vector<Ts>&... vs) {

    std::vector<bool> done(v.size());
    for(size_t i = 0; i < v.size(); ++i) {
        if(done[i]) continue;
        done[i] = true;
        size_t prev = i;
        size_t curr = perm[i];
        while(i != curr) {
            std::swap(v[prev], v[curr]);
            (std::swap(vs[prev], vs[curr]), ...);
            done[curr] = true;
            prev = curr;
            curr = perm[curr];
        }
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.