দুটি ভেক্টরকে সংযুক্ত করার সর্বোত্তম উপায় কী?


189

আমি মাল্টিট্রেডিং ব্যবহার করছি এবং ফলাফলগুলি মার্জ করতে চাই। উদাহরণ স্বরূপ:

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

আমি চাই এবি'র A এর বিষয়বস্তু এবং সেই ক্রমে বি এর সামগ্রী থাকতে হবে। এরকম কিছু করার সবচেয়ে কার্যকর উপায় কোনটি?


1
আপনি যখন বড় আকারের পাত্রে কাজ করার জন্য দক্ষতার সন্ধান করছেন তবে এটি তালিকা ব্যবহার করা আরও দক্ষ হবে, যেখানে আপনি বেশ কয়েকটি পয়েন্টার ক্রিয়াকলাপ দিয়ে একে অপরকে ছড়িয়ে দিতে পারেন। তবে তালিকার স্পেস ওভারহেড রয়েছে (একক সংযুক্ত তালিকার ব্যবহার বিবেচনা করুন)।
কেমিন চিউ

উত্তর:


317
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
এটি প্রতিটি উপাদানকে অনুলিপি করা উচিত, সুতরাং এটি ও (এন)
কিরিল ভি লিয়াদভিনস্কি

1
কোনও নতুন প্রশ্ন জিজ্ঞাসা করবেন কিনা তা নিশ্চিত নন, তবে পদক্ষেপ শব্দার্থকে বিবেচনায় নেওয়ার সময় এই উত্তরটি কী আরও উন্নত করা যেতে পারে? কম্পাইলারের কাছে সমস্ত উপাদানগুলির লুপিংয়ের পরিবর্তে একটি একক মেমরি মুভ করার জন্য কীভাবে আশা / নির্দেশ করতে পারি?
ব্রোয়েস ডি ক্যাট

2
@ বয়সি নং এটি কোনও উপাদানকে ধাক্কা দেওয়ার জন্য ধ্রুবক সময়। এন উপাদানগুলিকে পিছনে
ঠেকাতে

1
@ কনরাড আমি অন্যথায় বোঝাতে চাইনি, তবে স্পষ্টতার জন্য ধন্যবাদ। নোট করুন যে কোনও sertোকানো অপারেশনটির জটিলতা কখনই elementsোকানো হচ্ছে এমন উপাদানের সংখ্যার ক্ষেত্রে দেওয়া হয় না - যা সর্বদা ও (এন) দেবে - তবে ইতিমধ্যে পাত্রে থাকা উপাদানগুলির সংখ্যার দিক থেকে, যেহেতু এটি তার পরিমিতিটির একটি পরিমাপ সরবরাহ করে ।
বালক

64

এই অবিকল কি সদস্য ফাংশন std::vector::insertজন্য

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

4
@ নিক: কিসের তুলনায় ধীর?
GManNGG

2
সম্ভবত এটি উপাদান প্রতিটি সন্নিবেশ পর্যাপ্ত জায়গা জন্য পরীক্ষা করে? আগেই রিজার্ভ ব্যবহার করা এটির গতি বাড়িয়ে দেবে।
আরভিডিকে

10
@ নিক: প্রতিটি আধুনিক স্টডলিব বাস্তবায়ন insertএলোমেলো অ্যাক্সেস পুনরুক্তিকারীদের জন্য বিশেষজ্ঞ এবং আপ-ফ্রন্ট সংরক্ষিত থাকলে আমি অবাক হব না ।
GManNickG

1
@ জিম: এটি একটি ন্যায্য বিষয়, কারণ আমরা জানি যে উত্সটিও একটি ভেক্টর (যেখানে পুনরাবৃত্তকারীটির distanceও (1) জটিলতা রয়েছে)। তবুও, পারফরম্যান্স গ্যারান্টিগুলি insertহ'ল কিছু বিষয় মনে রাখার জন্য যখন আপনি প্রায়শই সামনে পরিকল্পনা করে আরও ভাল করতে পারেন।
নিক বেস্টিন

2
@ আরভিডি কে স্থানের জন্য যাচাই করা কেবলমাত্র কয়েকটি নির্দেশ: লোড ক্ষমতা, আকারের সাথে তুলনা করুন, শর্তযুক্ত ঝাঁপ দাও; যার সবগুলিই বেশিরভাগ ক্ষেত্রে নগণ্য ব্যয়। যেহেতু size < capacityঅধিকাংশ সময়, শাখা ভবিষ্যদ্বাণী সম্ভবত অ reallocating শাখা এর নির্দেশাবলী নির্দেশ পাইপলাইন করা যাবে না, কম পুনরাবৃত্তির গণনা ছাড়া শাখা ইনডিউসড লেটেন্সি কমানোর। এটি একটি ভাল ভেক্টর বাস্তবায়ন, প্লাস সিপিইউ নির্দেশিকা পাইপলাইন এবং [ভাল] শাখার পূর্বাভাস অনুমান করে তবে এটি আধুনিক সরঞ্জামচেন এবং ডেস্কটপ মেশিনের জন্য বেশ নির্ভরযোগ্য অনুমান। স্মার্টফোনগুলির সম্পর্কে যদিও জানা নেই ..
বালক

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
আপনি কি একে অপরের সাথে প্রতিটি ভেক্টরের উপাদান যুক্ত করতে চান? বা আপনি সংযোজন মানে? এটা এখন পরিষ্কার তবে পরের ৫ বছরের জন্য ..? অর্থ অস্পষ্ট হলে আপনার ওভারলোড অপারেটরটি করা উচিত নয়।
এসআর

2
@ এসআর আমি বোঝাতে চাইছি আমি এই উত্তরটি 3 বছর আগে লিখেছি। আমি এর অর্থ কী তা এখনও জানি। সমস্যা নেই। যদি সি ++ এর নিজস্ব ওভারলোড সরবরাহ করতে পারে তবে এটি আরও ভাল। (এবং হ্যাঁ ::নেওয়া হয়েছে;)
অলিসডজি কোডিড্যাক্ট ডট কম-

সংজ্ঞায়িতভাবে অবশ্যই স্পষ্ট নয় যা সংযোজনকে v1 + v2উপস্থাপন করে না।
অ্যাপলিস 21


বিকল্পটি @এফ # এর মতো ব্যবহার করা হবে
আলয়েডডি কোডিড্যাকট.কম

5

ব্র্যাডগনসুরফিংয়ের উত্তরের দিকে, বেশিরভাগ সময়ই দু'জন ভেক্টরকে (ও (এন)) সংশ্লেষ করার প্রয়োজন হয় না , বরং তাদের সাথে কেবল এমনভাবে কাজ করুন যেন তারা কনকানটেটেড হয়েছে (ও (1)) । এটি যদি আপনার হয় তবে এটি বুস্ট লাইব্রেরির প্রয়োজন ছাড়াই করা যেতে পারে।

কৌশলটি একটি ভেক্টর প্রক্সি তৈরি করা: একটি র‌্যাপার ক্লাস যা উভয় ভেক্টরের রেফারেন্সগুলি ম্যানিপুলেট করে , বাহ্যিকভাবে একক, সংলগ্ন হিসাবে দেখা যায়।

, USAGE

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

মেইন বেনিফিট

এটি তৈরি করতে ও (1) (ধ্রুবক সময়) এবং কমপক্ষে অতিরিক্ত মেমরির বরাদ্দ রয়েছে।

বিবেচনার জন্য কিছু স্টাফ

  • রেফারেন্সগুলি নিয়ে কাজ করার সময় আপনি কী করছেন তা যদি আপনি সত্যিই জানেন যে আপনি কেবল তখনই এর জন্য যাওয়া উচিতএই সমাধানটি তৈরি করা প্রশ্নের নির্দিষ্ট উদ্দেশ্যে তৈরি করা হয়েছে, যার জন্য এটি বেশ ভালভাবে কাজ করে । অন্য যে কোনও প্রসঙ্গে এটিকে নিয়োগ করা যদি রেফারেন্সগুলি কীভাবে কাজ করে তা সম্পর্কে আপনি নিশ্চিত না হন তবে অপ্রত্যাশিত আচরণ হতে পারে।
  • এই উদাহরণে, এবি নেই না একটি অ-const এক্সেস অপারেটর প্রদান ([])। এটি অন্তর্ভুক্ত নির্দ্বিধায় মনে রাখবেন, তবে মনে রাখবেন: যেহেতু এবি রেফারেন্স ধারণ করে, মান নির্ধারণের ফলে এ এবং এবং বা বি এর মূল উপাদানগুলিকেও প্রভাবিত করবে এটি একটি পছন্দসই বৈশিষ্ট্য কিনা বা না, এটি একটি অ্যাপ্লিকেশন-নির্দিষ্ট প্রশ্ন হওয়া উচিত সাবধানে বিবেচনা করুন।
  • এ বা বি এর মধ্যে সরাসরি যে কোনও পরিবর্তন করা হয়েছে (যেমন মান নির্ধারণ, বাছাই করা ইত্যাদি )ও AB কে "সংশোধন" করবে। এটি অগত্যা খারাপ নয় (আসলে এটি খুব সহজেই কার্যকর হতে পারে: এ এবং বি উভয়ের সাথে নিজেকে সুসংগত রাখার জন্য এবি'র কখনও স্পষ্টভাবে আপডেট করার প্রয়োজন হয় না) তবে অবশ্যই এটি এমন একটি আচরণ যা সম্পর্কে অবহিত হওয়া আবশ্যক। গুরুত্বপূর্ণ ব্যতিক্রম: A এবং / অথবা B এর আকার আরও বড় আকার পরিবর্তন করা এগুলিকে স্মৃতিতে পুনরায় স্থানান্তরিত করতে পারে (স্বচ্ছ স্থানের প্রয়োজনের জন্য), এবং এটি ফলশ্রুতিতে AB কে অবৈধ করে দেবে।
  • কারণ কোনও উপাদানের প্রতিটি অ্যাক্সেস পরীক্ষার আগে হয় (যথা, "i <v1.size ()"), ভেকপ্রক্সির অ্যাক্সেস সময়, যদিও ধ্রুবক থাকে, এটি ভেক্টরগুলির তুলনায়ও কিছুটা ধীর হয়।
  • এই পদ্ধতির এন ভেক্টরগুলিতে সাধারণীকরণ করা যেতে পারে। আমি চেষ্টা করিনি, তবে এটি কোনও বড় বিষয় হওয়া উচিত নয়।

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

যদি আপনার ভেক্টরগুলি সাজানো থাকে *, <algorithm> থেকে 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());
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.