दो वैक्टर को सुगम बनाने का सबसे अच्छा तरीका क्या है?


189

मैं मल्टीट्रेडिंग का उपयोग कर रहा हूं और परिणामों को मर्ज करना चाहता हूं। उदाहरण के लिए:

std::vector<int> A;
std::vector<int> B;
std::vector<int> AB;

मैं चाहता हूं कि AB को A की सामग्री और B की सामग्री उस क्रम में होनी चाहिए। ऐसा कुछ करने का सबसे कुशल तरीका क्या है?


1
यदि आप बड़े आकार के कंटेनरों के साथ काम करते समय दक्षता की तलाश करते हैं, तो सूची का उपयोग करने के लिए यह अधिक कुशल हो सकता है, जहां आप कई सूचक कार्यों के साथ एक-दूसरे को विभाजित कर सकते हैं। लेकिन सूची में अंतरिक्ष उपरि है (एकल लिंक की गई सूची का उपयोग करने पर विचार करें)।
केमिन झोउ

जवाबों:


318
AB.reserve( A.size() + B.size() ); // preallocate memory
AB.insert( AB.end(), A.begin(), A.end() );
AB.insert( AB.end(), B.begin(), B.end() );

6
धन्यवाद! रिज़र्व के बारे में नहीं सोचा होगा।
jmasterx

10
इसे प्रत्येक तत्व को कॉपी करना चाहिए, इसलिए यह O (n)
किरिल वी। ल्यदविंस्की

1
निश्चित नहीं है कि एक नया प्रश्न पूछा जाए या नहीं, लेकिन क्या शब्दार्थ को ध्यान में रखते हुए इस उत्तर को बेहतर बनाया जा सकता है? क्या ऐसा कोई तरीका है जिससे मैं संकलक को यह निर्देश / निर्देश दे सकूँ कि सभी तत्वों पर लूपिंग करने के बजाय एक ही मेमोरी मूव करें?
ब्रो डी कैट

2
@boycy No. यह एक तत्व को push_back करने के लिए निरंतर समय परिशोधन है। वापस n तत्वों को धकेलने के लिए O (n)
कोनराड लिंडनबैच

1
@ कोनराड मैं अन्यथा नहीं था, लेकिन स्पष्टीकरण के लिए धन्यवाद। ध्यान दें कि सम्मिलित किए जाने वाले तत्वों की संख्या में डाले गए तत्वों की संख्या के संदर्भ में कभी नहीं दिया जाता है - जो हमेशा O (n) देगा - लेकिन कंटेनर में पहले से मौजूद तत्वों की संख्या के संदर्भ में , क्योंकि यह इसकी मापनीयता का माप प्रदान करता है ।
ब्वॉयसी

64

यह ठीक वही है जो सदस्य फ़ंक्शन std::vector::insertके लिए है

std::vector<int> AB = A;
AB.insert(AB.end(), B.begin(), B.end());

4
@ निक: धीमी तुलना में क्या?
GManNickG

2
हो सकता है कि यह तत्व के प्रत्येक डालने पर पर्याप्त जगह की जांच करता है? पहले से रिजर्व का इस्तेमाल करने से इसमें तेजी आएगी।
RvdK

10
@ निक: मुझे आश्चर्य नहीं होगा अगर हर आधुनिक stdlib कार्यान्वयन insertरैंडम-एक्सेस पुनरावृत्तियों और आरक्षित अप-फ्रंट पर विशेष है।
GManNickG

1
@Gman: यह एक उचित बिंदु है क्योंकि हम जानते हैं कि स्रोत भी एक वेक्टर है (जहां पुनरावृत्त distanceमें O (1) जटिलता है)। फिर भी, insertजब आप अक्सर योजना बनाकर बेहतर कर सकते हैं , तो प्रदर्शन की गारंटी कुछ इस तरह की होती है।
निक बैस्टिन

2
अंतरिक्ष के लिए @RvdK जाँच केवल कुछ निर्देश हैं: भार क्षमता, आकार की तुलना, सशर्त कूद; जिनमें से अधिकांश मामलों के लिए नगण्य लागत है। चूंकि size < capacityअधिकांश समय, शाखा भविष्यवाणी की संभावना कम पाइपलाइन गिनती को छोड़कर गैर-वास्तविक शाखा के निर्देशों का निर्देश पाइपलाइन में होने का कारण होगा, शाखा-प्रेरित विलंबता को कम करना। यह एक अच्छा वेक्टर कार्यान्वयन, प्लस सीपीयू अनुदेश पाइपलाइन और [अच्छा] शाखा की भविष्यवाणी मानता है, लेकिन वे एक आधुनिक टूलकिन और डेस्कटॉप मशीन के लिए बहुत विश्वसनीय धारणाएं हैं। हालांकि स्मार्टफोन्स के बारे में नहीं जानते ..
बॉयसी

27

इस बात पर निर्भर करता है कि क्या आपको वास्तव में दो वैक्टरों को शारीरिक रूप से समाप्‍त करने की जरूरत है या आप पुनरावृत्ति के लिए संघटन के रूप को देना चाहते हैं। बढ़ावा :: समारोह में शामिल हों

http://www.boost.org/doc/libs/1_43_0/libs/range/doc/html/range/reference/utilities/join.html

यह आपको देगा।

std::vector<int> v0;
v0.push_back(1);
v0.push_back(2);
v0.push_back(3);

std::vector<int> v1;
v1.push_back(4);
v1.push_back(5);
v1.push_back(6);
...

BOOST_FOREACH(const int & i, boost::join(v0, v1)){
    cout << i << endl;
}

आपको देना चाहिए

1
2
3
4
5
6

नोट बूस्ट :: ज्वाइन दो वैक्टर को एक नए कंटेनर में कॉपी नहीं करता है, लेकिन पुनरावृत्तियों (रेंज) की एक जोड़ी उत्पन्न करता है जो दोनों कंटेनरों की अवधि को कवर करता है। कुछ प्रदर्शन ओवरहेड होंगे, लेकिन शायद कम है कि पहले सभी डेटा को एक नए कंटेनर में कॉपी करें।


1
अछा सुझाव। थोड़ी देर सोचने के बाद मुझे लगा कि इस लक्ष्य को बढ़ावा पुस्तकालयों के उपयोग के बिना भी पूरा किया जा सकता है। मैंने एक उत्तर पोस्ट किया है जिसमें बताया गया है कि कैसे।
रोनाल्ड सूजा

11

पर आधारित किरिल वी। लयाडविंस्की के जवाब के , मैंने एक नया संस्करण बनाया। यह स्निपेट टेम्पलेट और ओवरलोडिंग का उपयोग करता है। इसके साथ, आप लिख सकते हैं vector3 = vector1 + vector2और vector4 += vector3। आशा है कि यह मदद कर सकता है।

template <typename T>
std::vector<T> operator+(const std::vector<T> &A, const std::vector<T> &B)
{
    std::vector<T> AB;
    AB.reserve(A.size() + B.size());                // preallocate memory
    AB.insert(AB.end(), A.begin(), A.end());        // add A;
    AB.insert(AB.end(), B.begin(), B.end());        // add B;
    return AB;
}

template <typename T>
std::vector<T> &operator+=(std::vector<T> &A, const std::vector<T> &B)
{
    A.reserve(A.size() + B.size());                // preallocate memory without erase original data
    A.insert(A.end(), B.begin(), B.end());         // add B;
    return A;                                        // here A could be named AB
}

1
क्या आप प्रत्येक वेक्टर के तत्वों को एक दूसरे से जोड़ना चाहते हैं? या आप को जोड़ने का मतलब है? यह अभी स्पष्ट है लेकिन अगले 5 वर्षों के लिए ..? यदि अर्थ अस्पष्ट है तो आपको ऑपरेटर को अधिभार नहीं देना चाहिए।
SR

2
@ एसआर का मतलब मैं संक्षिप्त करना चाहता हूं। मैंने यह जवाब 3 साल पहले लिखा था। मुझे अभी भी पता है कि इसका क्या मतलब है। वहां कोई समस्या नहीं। यदि C ++ अपना स्वयं का अधिभार प्रदान कर सकता है तो यह और भी बेहतर होगा। (और हाँ ::लिया गया है;)
aloisdg

निश्चित रूप से सामान्य रूप से स्पष्ट v1 + v2नहीं है जो इसके अलावा प्रतिनिधित्व नहीं करता है।
अपोलिस


वैकल्पिक का उपयोग @एफ # की तरह होगा
aloisdg

6

ब्रैडगोनसर्फिंग के उत्तर की दिशा में, कई बार एक को वास्तव में दो वैक्टर (ओ (एन)) को बदलने की आवश्यकता नहीं होती है, लेकिन इसके बजाय बस उनके साथ काम करें जैसे कि वे (ओ (1)) । यदि यह आपका मामला है, तो इसे बूस्ट लाइब्रेरी की आवश्यकता के बिना किया जा सकता है।

चाल एक वेक्टर प्रॉक्सी बनाने के लिए है: एक आवरण वर्ग जो दोनों वैक्टरों के संदर्भों में हेरफेर करता है, बाहरी रूप से एकल, सन्निहित के रूप में देखा जाता है।

उपयोग

std::vector<int> A{ 1, 2, 3, 4, 5};
std::vector<int> B{ 10, 20, 30 };

VecProxy<int> AB(A, B);  // ----> O(1). No copies performed.

for (size_t i = 0; i < AB.size(); ++i)
    std::cout << AB[i] << " ";  // 1 2 3 4 5 10 20 30

कार्यान्वयन

template <class T>
class VecProxy {
private:
    std::vector<T>& v1, v2;
public:
    VecProxy(std::vector<T>& ref1, std::vector<T>& ref2) : v1(ref1), v2(ref2) {}
    const T& operator[](const size_t& i) const;
    const size_t size() const;
};

template <class T>
const T& VecProxy<T>::operator[](const size_t& i) const{
    return (i < v1.size()) ? v1[i] : v2[i - v1.size()];
};

template <class T>
const size_t VecProxy<T>::size() const { return v1.size() + v2.size(); };

मुख्य लाभ

इसे बनाने के लिए O (1) (निरंतर समय) और न्यूनतम अतिरिक्त मेमोरी आवंटन है।

कंसीडर के लिए कुछ STUFF

  • आपको केवल इसके लिए जाना चाहिए यदि आप वास्तव में जानते हैं कि आप संदर्भों के साथ क्या कर रहे हैंयह समाधान किए गए प्रश्न के विशिष्ट उद्देश्य के लिए अभिप्रेत है, जिसके लिए यह बहुत अच्छा काम करता है । इसे किसी अन्य संदर्भ में नियोजित करने के लिए अप्रत्याशित व्यवहार हो सकता है यदि आप सुनिश्चित नहीं हैं कि संदर्भ कैसे काम करते हैं।
  • इस उदाहरण में, एबी नहीं है एक नॉन-कास्ट एक्सेस ऑपरेटर ([]) प्रदान । इसे शामिल करने के लिए स्वतंत्र महसूस करें, लेकिन ध्यान रखें: चूंकि एबी में संदर्भ होते हैं, इसलिए इसे निर्दिष्ट करने के लिए मान ए और / या बी के भीतर मूल तत्वों को भी प्रभावित करेगा कि क्या यह एक वांछनीय विशेषता है या नहीं, यह एक आवेदन-विशिष्ट प्रश्न होना चाहिए। ध्यान से विचार करें।
  • सीधे ए या बी (जैसे मान निर्दिष्ट करना, छांटना, आदि) में किए गए कोई भी परिवर्तन भी एबी को "संशोधित" करेंगे। यह आवश्यक रूप से खराब नहीं है (वास्तव में, यह बहुत आसान हो सकता है: एबी को कभी भी ए और बी दोनों के लिए खुद को सिंक्रनाइज़ रखने के लिए स्पष्ट रूप से अपडेट होने की आवश्यकता नहीं है), लेकिन यह निश्चित रूप से एक व्यवहार है जिसके बारे में पता होना चाहिए। महत्वपूर्ण अपवाद: ए और / या बी को बड़ा करने के लिए इनका नेतृत्व करने के लिए ये स्मृति में (वास्तविक स्थान की आवश्यकता के लिए) हो सकता है, और यह बदले में एबी को अमान्य कर देगा।
  • क्योंकि एक तत्व की हर पहुंच एक परीक्षण से पहले होती है (जैसे, "i <v1.size ()"), VecProxy एक्सेस टाइम, हालांकि स्थिर, वैक्टर की तुलना में थोड़ा धीमा भी है।
  • यह दृष्टिकोण n वैक्टर के लिए सामान्यीकृत किया जा सकता है। मैंने कोशिश नहीं की है, लेकिन यह एक बड़ी बात नहीं होनी चाहिए।

2

एक और सरल संस्करण जो अभी तक उल्लेख नहीं किया गया था:

copy(A.begin(),A.end(),std::back_inserter(AB));
copy(B.begin(),B.end(),std::back_inserter(AB));

और मर्ज एल्गोरिथ्म का उपयोग कर:

#include <algorithm> #include <vector> #include <iterator> #include <iostream> #include <sstream> #include <string> template<template<typename, typename...> class Container, class T> std::string toString(const Container<T>& v) { std::stringstream ss; std::copy(v.begin(), v.end(), std::ostream_iterator<T>(ss, "")); return ss.str(); }; int main() { std::vector<int> A(10); std::vector<int> B(5); //zero filled std::vector<int> AB(15); std::for_each(A.begin(), A.end(), [](int& f)->void { f = rand() % 100; }); std::cout << "before merge: " << toString(A) << "\n"; std::cout << "before merge: " << toString(B) << "\n"; merge(B.begin(),B.end(), begin(A), end(A), AB.begin(), [](int&,int&)->bool {}); std::cout << "after merge: " << toString(AB) << "\n"; return 1; }


-1

यदि आपके वैक्टर * छंटे हुए हैं, तो <एल्गोरिथम> से set_union देखें

set_union(A.begin(), A.end(), B.begin(), B.end(), AB.begin());

लिंक में एक अधिक गहन उदाहरण है

* धन्यवाद rlbond


4
इसके अलावा, यह एक सीधी बात के रूप में एक ही बात नहीं करता है - आउटपुट रेंज में तत्व अद्वितीय हैं, जो ओपी चाहते थे (वे भी तुलनीय नहीं हो सकते हैं) हो सकता है। यह निश्चित रूप से इसे करने का सबसे कुशल तरीका नहीं है।
पीटर

-1

सभी समाधान सही हैं, लेकिन मुझे यह आसान लगा कि इसे लागू करने के लिए केवल एक फ़ंक्शन लिखें। इस तरह:

template <class T1, class T2>
void ContainerInsert(T1 t1, T2 t2)
{
    t1->insert(t1->end(), t2->begin(), t2->end());
}

इस तरह आप अस्थायी प्लेसमेंट से बच सकते हैं:

ContainerInsert(vec, GetSomeVector());
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.