web-dev-qa-db-ja.com

2つのベクトルを連結する最良の方法は何ですか?

マルチトレッドを使用していますが、結果をマージしたいです。例えば:

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

ABにAのコンテンツとBのコンテンツをこの順序で持たせたい。このようなことをする最も効率的な方法は何ですか?

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

これはまさに、std::vector::insertメンバー関数の目的です

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

実際に2つのベクトルを物理的に連結する必要があるか、反復のために連結したように見せたいかによって異なります。 boost :: join関数

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

Boost :: joinは2つのベクトルを新しいコンテナーにコピーせず、両方のコンテナーのスパンをカバーするイテレーターのペア(範囲)を生成します。パフォーマンスのオーバーヘッドは多少ありますが、最初にすべてのデータを新しいコンテナーにコピーするよりも少ないかもしれません。

23
bradgonesurfing

Kiril V. Lyadvinsky answer に基づいて、新しいバージョンを作成しました。このスニペットはテンプレートとオーバーロードを使用します。これを使用すると、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
}
8
aloisdg

まだ言及されていないもう1つの単純なバリアント:

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;
}
3
D. Alex

Bradgonesurfingの答えの方向では、多くの場合、2つのベクトルを連結するために実際には必要ではなく、単に連結されているかのように動作するではありません。それはあなたの場合のようであり、Boostライブラリを使用せずに行うことができます。

トリックは、ベクトルプロキシを作成することです。外部からは単一の連続したベクトルと見なされるreferencesを操作するラッパークラスです。ベクター。

使用法

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

VecProxy<int> AB(A, B);  // ----> O(1)

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

std::cout << AB[6]; // ----> Output: 20

実装

template <class T>
class VecProxy {
private:
    std::vector<T>& v1;
    std::vector<T>& v2;
public:
    VecProxy(std::vector<T>& ref1, std::vector<T>& ref2) : v1(ref1), v2(ref2) {}
    T& operator[](const size_t& i);
    const T& operator[](const size_t& i) const;
    const size_t size() const;
};
template<class T>
T& VecProxy<T>::operator[](const size_t& i){
    return (i < v1.size()) ? v1[i] : v2[i - v1.size()];
};
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)(一定の時間)で、最小限の追加メモリ割り当てが必要です。実際には、| B |を置き換えるため、巨大なベクトルを考慮する場合でも高速な操作です。 (または| A | + | B |)要素はゼロでコピーします。また、目的の動作を正確に実現します。

使用される手法に関係なく、ベクトル連結は少なくともO(| B |)です(BがAに追加される場合)。あなたの場合、3番目のベクトルABで作業するつもりなので、O(| A | + | B |)です。ベクターのサイズと必要な連結操作の数の両方によっては、これがボトルネックになる可能性があります。上記のトリックで対処します。

検討するもの

  • 参照を処理するときに何をしているのかを本当に知っている場合にのみ、それのために行くべきですこのソリューションは、作成された質問の特定の目的のために意図されており、かなりうまく機能しています。参照がどのように機能するかわからない場合、他のコンテキストで使用すると、予期しない動作が発生する可能性があります。
  • この例では、ABはconstだけでなく、non-constアクセスも提供します。削除してください。 ABには参照が含まれるため、値を割り当てると、Aおよび/またはB内の元の要素にも影響します。これが望ましい機能であるかどうかにかかわらず、慎重に検討する必要があるアプリケーション固有の質問です。
  • 同様に、AまたはBに対する変更(値の割り当て、サイズ変更など)もABを「変更」します。これは必ずしも悪いことではありません(実際、非常に便利な場合があります:AとBの両方との同期を維持するためにABを明示的に更新する必要はありません)が、それは確かに注意しなければならない動作です。
  • 要素へのすべてのアクセスの前にテスト(つまり、「i <v1.size()」)があるため、VecProxyのアクセス時間も一定ではありますが、ベクターのアクセス時間よりも遅くなります。したがって、たとえば、1秒あたり数百万のアクセス操作を実行する必要がある場合、つまりアクセス操作にボトルネックがある場合、これは進むべき方法ではない可能性があります。
  • このアプローチは、n個のベクトルに一般化できます。試したことはありませんが、大したことではないはずです。
  • このようなプロキシにコピーコンストラクターを提供することは(結局のところ、ベクターではないため)(私の目には)意味がありません。
  • すべての要素が同じコンテナに関係しているわけではないため、VecProxyをgloballyに並べ替えることはできません(少なくとも簡単ではありません)。
2
Ronald Chiesse

ベクトルがソートされている場合は、 set_union from <algorithm>を確認してください。

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

リンクにはより徹底的な例があります

*ありがとうrlbond

0
Cogwheel

すべての解決策は正しいですが、これを実装する関数を書くだけで簡単になることがわかりました。このような:

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

これにより、次のような一時的な配置を回避できます。

ContainerInsert(vec, GetSomeVector());
0
user3360767