値のベクトルがあり、それらがすべて同じであることを確認したい場合、C++でこれを効率的に行う最良の方法は何ですか? Rのような他の言語でプログラミングしている場合、私の考えは、コンテナーの一意の要素のみを返すことであり、一意の要素の長さが1を超える場合、要素を同じにすることはできません。 C++では、これは次のように実行できます。
//build an int vector
std::sort(myvector.begin(), myvector.end());
std::vector<int>::iterator it;
//Use unique algorithm to get the unique values.
it = std::unique(myvector.begin(), myvector.end());
positions.resize(std::distance(myvector.begin(),it));
if (myvector.size() > 1) {
std::cout << "All elements are not the same!" << std::endl;
}
しかし、インターネットとSOについて読んでいると、setやfind_ifアルゴリズムを使用するなどの他の答えがあります。では、これを行う最も効率的な方法は何ですか?私は、すべての要素を並べ替えてからベクトルのサイズを変更する必要があるため、最良の方法ではないと思いますが、おそらく間違っています。
ありがとう、ベン。
std::sort
を使用する必要はありません。より簡単な方法で行うことができます:
if ( std::adjacent_find( myvector.begin(), myvector.end(), std::not_equal_to<>() ) == myvector.end() )
{
std::cout << "All elements are equal each other" << std::endl;
}
std::equal
を使用できます
バージョン1:
//assuming v has at least 1 element
if ( std::equal(v.begin() + 1, v.end(), v.begin()) )
{
//all equal
}
これにより、各要素が前の要素と比較されます。
バージョン2:
//assuming v has at least 1 element
int e = v[0]; //preferably "const auto& e" instead
bool all_equal = true;
for(std::size_t i = 1,s = v.size();i<s && all_equal;i++)
all_equal = e == v[i];
編集:
パフォーマンスに関しては、1億個の要素でテストした後、Visual Studio 2015でversion 1
はversion 2
の約2倍速いことがわかりました。 。これは、vs2015の最新のコンパイラが、int、floatなどを使用する場合、c ++ std実装で sse命令 を使用するためです。
_ mm_testc_si128 を使用すると、std::equal
と同様のパフォーマンスが得られます
ベクトルに制約がない場合、アプローチに関係なく、ベクトルを少なくとも1回反復する必要があります。したがって、最初の要素を選択して、他のすべての要素がそれに等しいことを確認してください。
_std::unique
_の漸近的な複雑さは線形ですが、操作の実際のコストはおそらく必要以上に大きく、インプレースアルゴリズムです(データが進むにつれて変更されます)。
最速のアプローチは、ベクトルに単一の要素が含まれる場合、定義により一意であると仮定することです。ベクトルにさらに要素が含まれている場合は、すべての要素が最初の要素と正確に等しいかどうかを確認する必要があります。そのためには、最初とは異なる最初の要素を見つけ、2番目から検索を開始するだけです。そのような要素がある場合、要素は一意ではありません。
_if (v.size() < 2) return true;
auto different = std::find_if(v.begin()+1, v.end(),
[&v](auto const &x) { x != v[0]; });
return different == v.end();
_
これはC++ 14構文を使用しているため、C++ 11ツールチェーンでは、ラムダで正しい型を使用できます。 C++ 03では、ラムダの代わりに_std::not
_、_std::bind1st/std::bind2nd
_、および_std::equal
_の組み合わせを使用できます。
このアプローチのコストは、distance(start,different element)
比較であり、コピーはありません。比較の数で予想される最悪の線形コスト(およびコピーなし!)
if(std::all_of(myvector.begin()+1, myvector.end(), std::bind(std::equal_to<int>(),
std::placeholders::_1, myvector.front())) {
// all members are equal
}
ソートはO(NlogN)タスクです。
これはO(N)で簡単に解決できるため、現在の方法は貧弱です。
単純なO(N)は、Luchian Grigoreが示唆しているように、ベクトルを1回だけ繰り返し、すべての要素を最初の要素と比較します。
FunctionalPlusを使用できます( https://github.com/Dobiasd/FunctionalPlus ):
std::vector<std::string> things = {"same old", "same old"};
if (fplus::all_the_same(things))
std::cout << "All things being equal." << std::endl;
あなたの特定のケースでは、ベクトル要素を反復処理し、最初の要素とは異なる要素を見つけるだけで十分です。ベクター内のすべての要素を評価する前に停止するのに十分な幸運かもしれません。 (whileループを使用できますが、読みやすくするためにforループを使用しています)
bool uniqueElt = true;
int firstItem = *myvector.begin();
for (std::vector<int>::const_iterator it = myvector.begin()+1; it != myvector.end() ; ++it) {
if(*it != firstItem) {
uniqueElt = false;
break;
}
}
ベクトルに含まれるさまざまな値の数を知りたい場合は、セットを構築してそのサイズをチェックし、内部にあるさまざまな値の数を確認できます。
std::set mySet;
std::copy(mySet.begin(), myvector.begin(), myvector.end());
たぶんこのようなものです。ベクトルを一度だけ横断し、ベクトルの内容を混乱させません。
std::vector<int> values { 5, 5, 5, 4 };
bool equal = std::count_if(values.begin(), values.end(), [ &values ] (auto size) { return size == values[0]; }) == values.size();
ベクトルの値が基本型とは異なる場合、等値演算子を実装する必要があります。
Underscore_dのコメントを考慮した後、考えられる解決策を変更しています
std::vector<int> values { 5, 5, 5, 4 };
bool equal = std::all_of(values.begin(),values.end(),[ &values ] (auto item) { return item == values[0]; });
C++ 14
を使用する別のアプローチ:
bool allEqual = accumulate(v.begin(), v.end(), true, [first = v[0]](bool acc, int b) {
return acc && (b == first);
});
これは次数Nです。
std::count
は、開始要素に一致するすべての要素をカウントします。
std::vector<int> numbers = { 5, 5, 5, 5, 5, 5, 5 };
if (std::count(std::begin(numbers), std::end(numbers), numbers.front()) == numbers.size())
{
std::cout << "Elements are all the same" << std::endl;
}
完全を期すために、まだ最も効率的ではないため、std :: uniqueを使用して、すべてのメンバーが同じかどうかをより効率的に判断できますが、std :: uniqueをこの方法で使用した後は、コンテナ役に立たない:
#include <algorithm>
#include <iterator>
if (std::distance(cntnr.begin(), std::unique(cntnr.begin(), cntnr.end()) == 1)
{
// all members were the same, but
}