マルチトレッドを使用しており、結果をマージしたいと考えています。例えば:
std::vector<int> A;
std::vector<int> B;
std::vector<int> AB;
ABにAの内容とBの内容をこの順序で持たせたい。このようなことをする最も効率的な方法は何ですか?
マルチトレッドを使用しており、結果をマージしたいと考えています。例えば:
std::vector<int> A;
std::vector<int> B;
std::vector<int> AB;
ABにAの内容とBの内容をこの順序で持たせたい。このようなことをする最も効率的な方法は何ですか?
回答:
AB.reserve( A.size() + B.size() ); // preallocate memory
AB.insert( AB.end(), A.begin(), A.end() );
AB.insert( AB.end(), B.begin(), B.end() );
これがまさにメンバー関数のstd::vector::insert
目的です
std::vector<int> AB = A;
AB.insert(AB.end(), B.begin(), B.end());
insert
ランダムアクセスイテレータに特化し、事前に予約されたすべての最新のstdlib実装が驚くに値しません。
distance
はO(1)の複雑さを持っています)。それでも、のパフォーマンスの保証は、insert
事前に計画することでより良い結果が得られる場合があることに留意する必要があります。
size < capacity
、ほとんどの時間、分岐予測は、おそらく低反復回数を除き、分岐によって誘発される遅延を最小限に抑え、命令パイプラインになるように非再割り当てブランチの指示を引き起こします。これは、優れたベクトル実装と、CPU命令パイプラインおよび[適切な]分岐予測を前提としていますが、これらは、最新のツールチェーンとデスクトップマシンにとってかなり信頼できる前提です。しかし、スマートフォンについては知らない..
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つのベクトルを新しいコンテナーにコピーしないことに注意してください。両方のコンテナーのスパンをカバーするイテレーターのペア(範囲)を生成します。パフォーマンスのオーバーヘッドは多少ありますが、最初にすべてのデータを新しいコンテナにコピーするよりは少ないかもしれません。
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
}
::
が取られます;)
v1 + v2
加算を表さないことは、一般的にはっきりとはわかりません。
@
F#のように使用することもできます
Bradgonesurfingの回答の方向では、多くの場合、実際には 2つのベクトルを連結する必要はありませんが(O(n))、代わりにそれらを連結したかのように操作します(O(1))。これが当てはまる場合は、Boostライブラリがなくても実行できます。
コツは、ベクトルプロキシを作成することです。外部的には単一の連続したベクトルとして見られる、両方のベクトルへの参照を操作するラッパークラスです。
使用法
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)(一定の時間)であり、追加のメモリ割り当ては最小限です。
考慮すべき事項
まだ言及されていないもう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;
}
すべてのソリューションは正しいですが、これを実装する関数を記述するだけの方が簡単であることがわかりました。このような:
template <class T1, class T2>
void ContainerInsert(T1 t1, T2 t2)
{
t1->insert(t1->end(), t2->begin(), t2->end());
}
これにより、次のような一時的な配置を回避できます。
ContainerInsert(vec, GetSomeVector());