Проверка равенства всех элементов вектора в C++

Если у меня есть вектор значений и я хочу проверить, что они все одинаковы, каков наилучший способ сделать это на 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;
}

однако, читая об интернете и так, я вижу другие ответы такие, используя набор или алгоритм find_if. Так что наиболее эффективный способ сделать это и почему? Я полагаю, что мой способ не лучший, поскольку он включает сортировку каждого элемента, а затем изменение размера вектора, но, возможно, я ошибаюсь.

спасибо, Бен.

12 ответов


вам не нужно использовать std::sort. Это можно сделать проще:

if ( std::adjacent_find( myvector.begin(), myvector.end(), std::not_equal_to<int>() ) == 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];

Edit:

что касается производительности, после тестирования с элементами 100m я узнал, что в Visual Studio 2015 version 1 - примерно в два раза быстрее version 2. Это связано с тем, что последний компилятор для vs2015 использует инструкции sse в реализациях std c++ при использовании ints, float и т. д..

если вы используете _mm_testc_si128 вы получите такую же производительность std::equal


учитывая отсутствие ограничений на вектор, вы должны перебирать вектор хотя бы один раз, независимо от подхода. Поэтому просто выберите первый элемент и убедитесь, что все остальные равны ему.


сортировка является задачей O(NlogN).

Это легко разрешимо в O (N), поэтому ваш текущий метод беден.

простой O (N) будет, как предлагает Лучиан Григоре, повторять вектор только один раз, сравнивая каждый элемент с первым элементом.


пока асимптотической сложности std::unique является линейным, фактическая стоимость операции, вероятно, намного больше, чем вам нужно, и это алгоритм inplace (он изменит данные по мере их прохождения).

самый быстрый подход, чтобы предположить, что если вектор содержит один элемент, он уникален по определению. Если вектор содержит больше элементов, то вам просто нужно проверить, все ли они точно равны первому. Для этого вам нужно только найти первый элемент это отличается от первого, начиная поиск со второго. Если такой элемент существует, то он не уникален.

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
}

в вашем конкретном случае было бы достаточно итерации по векторному элементу и нахождения другого элемента от первого. Возможно, вам даже повезет остановиться, прежде чем оценивать все элементы в вашем векторе. (Цикл 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::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;
}

вы можете использовать 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;

может, что-то вроде этого. Он пересекает вектор только один раз и не связывается с векторным контентом.

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

для полноты, потому что он все еще не самый эффективный, вы можете использовать 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
}

другой подход с использованием C++ 14:

bool allEqual = accumulate(v.begin(), v.end(), true, [first = v[0]](bool acc, int b) {
    return acc && (b == first);
  });

который также является порядком N.