ベクトルからサブベクトルを抽出する最良の方法?


295

私が持っていると仮定しstd::vector(せのはそれを呼び出すmyVecサイズの)N。要素XからYまでのコピーで構成される新しいベクトルを作成する最も簡単な方法は何ですか(0 <= X <= Y <= N-1)。たとえば、sizeのベクトルをmyVec [100000]通過myVec [100999]します150000

これをベクトルで効率的に実行できない場合、代わりに使用する必要がある別のSTLデータ型はありますか?


7
あなたはサブベクトルを抽出したいが、あなたが本当に望んでいるのはビュー/サブベクトルへのアクセスだと私には思える-違いはビューがコピーされないことです-古い学校のC ++は開始ポインタと終了ポインタを使用することです、 std :: vectorのmemが隣接しているという事実を考えると、ポインターを使用して反復することでコピーを回避できるはずですが、コピーを気にしない場合は、以前のスコープで新しいベクターを初期化するだけです。ベクトル
serup

c ++ 11以降、.data()(cplusplus.com/reference/vector/vector/data)があります。しかし、ポインタを用いたものをSTLコンテナの中に推奨され、参照stackoverflow.com/questions/31663770/...
デヴィッド・トス

回答:


371
vector<T>::const_iterator first = myVec.begin() + 100000;
vector<T>::const_iterator last = myVec.begin() + 101000;
vector<T> newVec(first, last);

新しいベクトルを作成するのはO(N)演算ですが、実際にはもっと良い方法はありません。


12
+1、それはO(YX)でもあり、O(N)以下です(この例でははるかに少ない)
orip

74
@oripまあ、結局それはO(N)です。
ヨハンゲレル

55
@GregRogers:Nが特定の数値であるビッグO表記を使用しても意味がありません。Big-Oは、Nがどのように変化するかに関して成長率を伝えます。Johann:1つの変数名を2つの方法で使用しないことをお勧めします。通常はと言うかO(Y-X)、と言いO(Z) where Z=Y-Xます。
Mooing Duck 2013

2
@GregRogersこの方法を使用することにより、新しいベクターを宣言する必要があります。元のベクトルを変更する方法はありますか?myVec(最初、最後)のようなものですか?私はこれが間違っていることを知っていますが、コードで再帰を使用したいので本当に解決策が必要であり、同じベクトルを繰り返し使用する必要があります(変更されていますが)。ありがとう!
ulyssis2 2015年

13
なぜvector<T> newVec(myVec.begin() + 100000, myVec.begin() + 101000);ですか?
aquirdturtle 2017年

88

ベクトルコンストラクタを使用するだけです。

std::vector<int>   data();
// Load Z elements into data so that Z > Y > X

std::vector<int>   sub(&data[100000],&data[101000]);

2
わかりましたが、任意のベクトル要素からイテレータを取得するのがそれほど簡単ではないことに気付きませんでした。
An̲̳̳drew 2009年

5
これらのベクター要素のアドレスを取得することは、ベクターストレージが実際に隣接していない場合に機能しなくなる、移植できないハックです。begin()+ 100000などを使用します
j_random_hacker 2009年

2
私の悪い、明らかに標準では、ベクトルストレージが連続していることを保証しています。それにもかかわらず、begin()+ 100000がそうである一方で、ランダムアクセスをサポートするすべてのコンテナーで動作することが確実に保証されていないため、このようなアドレスで動作することは悪い習慣です。
j_random_hacker 2009年

33
@j_random_hacker:すみません、同意しないでください。std :: vectorのSTL仕様は、このタイプの手順をサポートするように明示的に変更されました。また、ポインターは有効なタイプのイテレーターです。iterator_traits <>を検索する
マーティンヨーク、

6
@ taktak004いいえ。operator[]参照を返すことを覚えておいてください。アクセス違反になるのは、参照を読み書きしたときだけです。どちらも実行せず、代わりにアドレスを取得しているため、UBを呼び出していません。
マーティンヨーク

28

std::vector<T>(input_iterator, input_iterator)、あなたのケースfoo = std::vector<T>(myVec.begin () + 100000, myVec.begin () + 150000);では、例えばここを参照してください


1
Andrewは新しいベクターを作成しようとしているので、 "foo = std :: vector(..."でコピーするのではなく、 "std :: vector foo(..."
Drew Dormann

4
もちろんそうですが、std :: vector <int> foo = std :: vector(...)と入力するか、std :: vector <int> foo(...)と入力するかは関係ありません。
Anteru 2009年

19

最近では、spans を使用しています。だからあなたは書くでしょう:

#include <gsl/span>

...
auto start_pos = 100000;
auto length = 1000;
auto span_of_myvec = gsl::make_span(myvec);
auto my_subspan = span_of_myvec.subspan(start_pos, length);

と同じタイプの1000要素のスパンを取得しますmyvec。またはより簡潔な形式:

auto my_subspan = gsl::make_span(myvec).subspan(1000000, 1000);

(しかし、各数値引数の意味が完全に明確ではないため、これはあまり好きではありません。また、lengthとstart_posが同じ桁数であると、さらに悪くなります。)

とにかく、これはコピーでなく、ベクター内のデータの単なるビューであることを忘れないでください。注意してください。実際のコピーが必要な場合は、次のようにすることができます。

std::vector<T> new_vec(my_subspan.cbegin(), my_subspan.cend());

ノート:


と原則のためだけに使用cbegincendます;)std::cbeginなど。
JHBonarius

1
@JHBonarius:このコードがコンテナの選択にどのようにテンプレートされていないかを見て、特定の利点があるとは思いません。好みの問題だと思います。
einpoklum

10

両方を変更しない場合(アイテムの追加/削除なし-スレッドの問題に注意を払っている限り、既存のものの変更は問題ありません)、単純にdata.begin() + 100000andを渡しdata.begin() + 101000、それらがbegin()end()より小さいベクトルのふりをすることができます。

または、ベクトルストレージは連続していることが保証されているので、1000アイテムの配列を単純に渡すことができます。

T *arrayOfT = &data[0] + 100000;
size_t arrayOfTLength = 1000;

これらの手法はどちらも一定の時間がかかりますが、データの長さが増加しないことを要求し、再割り当てをトリガーします。


これは、元のベクターとサブベクターをリンクする場合にも適しています。
PyRulez 2015年

7

この議論はかなり古いですが、最も単純なものはリスト初期化でまだ言及されていません:

 vector<int> subvector = {big_vector.begin() + 3, big_vector.end() - 2}; 

c ++ 11以上が必要です。

使用例:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main(){

    vector<int> big_vector = {5,12,4,6,7,8,9,9,31,1,1,5,76,78,8};
    vector<int> subvector = {big_vector.begin() + 3, big_vector.end() - 2};

    cout << "Big vector: ";
    for_each(big_vector.begin(), big_vector.end(),[](int number){cout << number << ";";});
    cout << endl << "Subvector: ";
    for_each(subvector.begin(), subvector.end(),[](int number){cout << number << ";";});
    cout << endl;
}

結果:

Big vector: 5;12;4;6;7;8;9;9;31;1;1;5;76;78;8;
Subvector: 6;7;8;9;9;31;1;1;5;76;

6

あなたは型std::vector<...> myVecが何であるかについては触れませんでしたが、ポインタを含まない単純な型または構造体/クラスであり、最高の効率が必要な場合は、直接メモリコピーを実行できます(これは、他の回答が提供されています)。ここのための一般的な例であるstd::vector<type> myVecところtype、この場合には、ありますint

typedef int type; //choose your custom type/struct/class
int iFirst = 100000; //first index to copy
int iLast = 101000; //last index + 1
int iLen = iLast - iFirst;
std::vector<type> newVec;
newVec.resize(iLen); //pre-allocate the space needed to write the data directly
memcpy(&newVec[0], &myVec[iFirst], iLen*sizeof(type)); //write directly to destination buffer from source buffer

2
@Anteruの「コンストラクタの使用」である-O3を使用std::vector(myVec.begin () + 100000, myVec.begin () + 150000);すると、この長いバージョンがまったく同じアセンブリに生成されないのではないでしょうか。
サンソーン

1
たとえば、MSVC ++ 2015は、必要に応じてにコンパイルさstd::vector<>(iter, iter)memmove()ます(コンストラクターが自明の場合、自明の適切な定義)。
Pablo H

1
電話しないでくださいmemcpy。やるstd::copy範囲(2回の反復子)、およびコンパイラを受け入れ、std.libraryを呼び出すために共謀しますまたはコンストラクタmemcpy適切な場合に。
Bulletmagnet


3

Mがサブベクトルのサイズである場合、STLコピーをO(M)パフォーマンスで使用できます。


STL :: copyが同じサイズとタイプの2つのstd :: vector <T>配列で機能するため、正しい方向を指し示しているので@LokiAstariが正しい選択ではないことを示唆する理由がわかります。ここで、OPは、OPの投稿でここに概説されているように、サブセクションを新しい小さな配列にコピーすることを望んでいます: "0 <= X <= Y <= N-1"
Andrew

@ Andrew、std :: copyおよびstd :: back_inserterを使用した例を参照
chrisg

@LokiAstariなんで?
chrisg

2
@LokiAstariこれは、ピアレビューに耐えられなかったこれに対する編集を参照していたため、<br/> vector <T> newvecの例が示されました。std :: copy(myvec.begin()+ 10000、myvec.begin()+10100、std :: back_inserter(newvec)); <br/>この場合、最初に宛先を作成する必要はありませんが、直接初期化する方がより直接的です。
chrisg

1
@chrisg:それも2行です。さらに、それが効率的であることを確認するために、3行目を挿入する必要があります。newvec.reserve(10100 - 10000);。ITは間違いなくオプションであり、技術的には機能します。しかし、あなたが推薦する2つのうちのどれですか?
マーティンヨーク

1

線形時間ではないコレクションを投影する唯一の方法は、遅延して行うことです。この場合、結果の「ベクトル」は、実際には元のコレクションに委譲するサブタイプになります。たとえば、ScalaのList#subseqメソッドは、一定の時間でサブシーケンスを作成します。ただし、これが機能するのは、コレクションが不変で、基になる言語がスポーツガベージコレクションである場合のみです。


c ++でこれを行うには、Xのベクトルではなくshared_ptrのベクトルをXにしてSPをコピーしますが、残念ながら、アトミック操作がSPのパイピングに関係しているので、これより速くなるとは思いません。または、元のベクトルが代わりにベクトルのconst shared_ptrである可能性があり、その中の部分範囲を参照するだけです。ofcあなたはそれをベクトルのshared_ptrにする必要はありませんが、それからあなたは生涯の問題を抱えています...これはすべて私の頭の上にあり、間違っているかもしれません...
NoSenseEtAl

0

これを他の人のためだけに遅らせて投稿します。最初のコーダーは今では完成しているでしょう。単純なデータ型の場合、コピーは必要ありません。古き良きCコードメソッドに戻してください。

std::vector <int>   myVec;
int *p;
// Add some data here and set start, then
p=myVec.data()+start;

次に、ポインターpとlenをサブベクトルが必要なものに渡します。

notelenである必要があります!! len < myVec.size()-start


これはコピーを実行しません。
Trilarion 2017年

0

多分array_view / span GSLライブラリが適切なオプションです。

これも単一ファイルの実装です:array_view


ここに回答をリンクと一緒に追加してください。外部リンクは将来変更される可能性があるため
Panther

0

あるベクトルから別のベクトルに要素を簡単にコピーする
この例では、ペアのベクトルを使用して理解しやすくしています
`

vector<pair<int, int> > v(n);

//we want half of elements in vector a and another half in vector b
vector<pair<lli, lli> > a(v.begin(),v.begin()+n/2);
vector<pair<lli, lli> > b(v.begin()+n/2, v.end());


//if v = [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
//then a = [(1, 2), (2, 3)]
//and b = [(3, 4), (4, 5), (5, 6)]

//if v = [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7)]
//then a = [(1, 2), (2, 3), (3, 4)]
//and b = [(4, 5), (5, 6), (6, 7)]

'
ご覧のように、要素をあるベクトルから別のベクトルに簡単にコピーできます。たとえば、インデックス10から16に要素をコピーする場合は、

vector<pair<int, int> > a(v.begin()+10, v.begin+16);

インデックス10の要素を末尾からインデックスにしたい場合は、その場合

vector<pair<int, int> > a(v.begin()+10, v.end()-5);

これが役立つことを願って、最後のケースで覚えてください v.end()-5 > v.begin()+10


0

さらに別のオプション:コンストラクターを使用できないa thrust::device_vectorとの間を移動する場合などに便利ですthrust::host_vector

std::vector<T> newVector;
newVector.reserve(1000);
std::copy_n(&vec[100000], 1000, std::back_inserter(newVector));

複雑さO(N)も必要

これをトップアンワーコードと組み合わせることができます

vector<T>::const_iterator first = myVec.begin() + 100000;
vector<T>::const_iterator last = myVec.begin() + 101000;
std::copy(first, last, std::back_inserter(newVector));
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.