Проверка наличия всех элементов вектора в С++

Если у меня есть вектор значений и вы хотите проверить, что они все одинаковые, что лучший способ сделать это на С++ эффективно? Если бы я программировал на каком-то другом языке, например, R, один мой ум перескакивает - это возвращать только уникальные элементы контейнера, а если длина уникальных элементов больше 1, я знаю, что элементы не могут быть одинаковыми. В С++ это можно сделать следующим образом:

//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, я вижу другие ответы, например, с помощью набора или алгоритма find_if. Итак, каков наиболее эффективный способ сделать это и почему? Я считаю, что мой не лучший способ, поскольку он включает в себя сортировку каждого элемента, а затем изменение размера вектора - но, возможно, я ошибаюсь.

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

Ответы

Ответ 1

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

Ответ 2

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

Редактировать:

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

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

Ответ 3

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

Ответ 4

Хотя асимптотическая сложность 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();

Используя синтаксис С++ 14, в инструментальной цепочке С++ 11 вы можете использовать правильный тип в лямбда. В С++ 03 вместо лямбда вы можете использовать комбинацию std::not, std::bind1st/std::bind2nd и std::equal.

Стоимость этого подхода - distance(start,different element) сравнения и без копий. Ожидаемая и наихудшая линейная стоимость в количестве сравнений (и без копий!)

Ответ 5

if(std::all_of(myvector.begin()+1, myvector.end(), std::bind(std::equal_to<int>(),
                                      std::placeholders::_1, myvector.front())) {
  // all members are equal
}

Ответ 6

Сортировка - это задача O (NlogN).

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

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

Ответ 7

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

Ответ 8

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

Ответ 9

Может быть как то так Он пересекает вектор только один раз и не связывается с векторным содержимым.

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

Ответ 10

для полноты, поскольку он по-прежнему не самый эффективный, вы можете использовать std:: unique более эффективным способом решить, все ли члены одинаковы, но будьте осторожны, после использования std:: unique this путь контейнер бесполезен:

#include <algorithm>
#include <iterator>

if (std::distance(cntnr.begin(), std::unique(cntnr.begin(), cntnr.end()) == 1)
{
  // all members were the same, but
}

Ответ 11

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

Ответ 12

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

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

который также порядок N.

Ответ 13

используя std::all_of и С++ 11 lambda

if (all_of(values.begin(), values.end(), [&] (int i) {return i == values[0];}){
    //all are the same
}