web-dev-qa-db-fra.com

Vérifier si tous les éléments d'un vecteur sont égaux en C++

Si j'ai un vecteur de valeurs et que je veux vérifier qu'elles sont toutes identiques, quel est le meilleur moyen de le faire efficacement en C++? Si je programmais dans un autre langage comme R, l’une des choses qui me sautent à l’esprit est de ne restituer que les éléments uniques du conteneur, puis si la longueur des éléments uniques est supérieure à 1, je sais qu’ils ne peuvent pas être identiques. En C++, cela peut être fait comme ceci:

//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;
}

Cependant, en lisant sur Internet et sur SO, je vois d’autres réponses telles que l’utilisation d’un ensemble ou de l’algorithme find_if. Alors, quel est le moyen le plus efficace de le faire et pourquoi? J'imagine que le mien n'est pas la meilleure solution car cela implique de trier tous les éléments, puis de redimensionner le vecteur - mais je me trompe peut-être. 

Merci, Ben.

21
Ward9250

Vous n'avez pas besoin d'utiliser std::sort. Cela peut être fait d'une manière plus simple:

if ( std::adjacent_find( myvector.begin(), myvector.end(), std::not_equal_to<>() ) == myvector.end() )
{
    std::cout << "All elements are equal each other" << std::endl;
}
44
Vlad from Moscow

vous pouvez utiliser std::equal

version 1:

//assuming v has at least 1 element
if ( std::equal(v.begin() + 1, v.end(), v.begin()) )
{
    //all equal
}

Cela comparera chaque élément avec le précédent.

version 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];

Modifier:

En ce qui concerne les performances, après des tests avec des éléments de 100 m, j'ai découvert que dans Visual Studio 2015 version 1 est environ deux fois plus rapide que version 2. En effet, le dernier compilateur pour vs2015 utilise les instructions sse in c ++ std lorsque vous utilisez ints, float, etc.

si vous utilisez _mm_testc_si128 vous obtiendrez une performance similaire à std::equal

24
Raxvan

En l'absence de contrainte sur le vecteur, vous devez parcourir le vecteur au moins une fois, quelle que soit l'approche. Il suffit donc de choisir le premier élément et de vérifier que tous les autres sont égaux.

11
Luchian Grigore

Bien que la complexité asymptotique de std::unique soit linéaire, le coût réel de l'opération est probablement beaucoup plus important que nécessaire et il s'agit d'un algorithme in-situ (il modifiera les données au fur et à mesure).

L’approche la plus rapide consiste à supposer que si le vecteur contient un seul élément, il est unique par définition. Si le vecteur contient plus d'éléments, il vous suffit de vérifier si tous sont exactement égaux au premier. Pour cela, il vous suffit de rechercher le premier élément qui diffère du premier et de lancer la recherche à partir du second. S'il existe un tel élément, les éléments ne sont pas uniques.

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();

En utilisant la syntaxe C++ 14, dans une chaîne d'outils C++ 11, vous pouvez utiliser le type correct dans le lambda. En C++ 03, vous pouvez utiliser une combinaison de std::not, std::bind1st/std::bind2nd et std::equal à la place de lambda.

Le coût de cette approche est distance(start,different element) comparaisons et pas de copies. Coût linéaire attendu et dans le pire des cas en nombre de comparaisons (et pas de copies!)

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

Le tri est une tâche O(NlogN). 

Ceci est facilement soluble dans O (N), votre méthode actuelle est donc mauvaise.

Un simple O(N) serait, comme le suggère Luchian Grigore, itérer sur le vecteur, une seule fois, en comparant chaque élément au premier.

2
RichardPlunkett

Vous pouvez simplement utiliser std::count pour compter tous les éléments qui correspondent à l'élément de départ:

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;
}
1
Tas

Vous pouvez utiliser 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;
1
Jee lee

Dans votre cas particulier, itérer sur un élément vectoriel et trouver un élément différent du premier suffirait. Vous aurez peut-être même la chance de vous arrêter avant d'évaluer tous les éléments de votre vecteur. (Une boucle while pourrait être utilisée mais je m'en suis tenu à une boucle for pour des raisons de lisibilité)

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

Si vous voulez savoir combien de valeurs différentes votre vecteur contient, vous pouvez créer un ensemble et vérifier sa taille pour voir combien de valeurs différentes sont à l'intérieur:

std::set mySet;
std::copy(mySet.begin(), myvector.begin(), myvector.end());
1
Julien B.

Peut-être quelque chose comme ça. Il parcourt le vecteur une seule fois et ne plaisante pas avec le contenu du vecteur.

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();

Si les valeurs du vecteur sont différentes de celles du type de base, vous devez implémenter un opérateur d'égalité.

Après avoir pris en compte les remarques underscore_d, je change de solution possible

std::vector<int> values { 5, 5, 5, 4 };
bool equal = std::all_of(values.begin(),values.end(),[ &values ] (auto item) { return item == values[0]; });
1
Rostfrei

Une autre approche utilisant C++ 14:

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

qui est aussi l'ordre N.

0
Batato

par souci d'exhaustivité, comme ce n'est toujours pas le plus efficace, vous pouvez utiliser std :: unique de manière plus efficace pour décider si tous les membres sont identiques, mais sachez qu'après avoir utilisé std :: unique, le conteneur est inutile:

#include <algorithm>
#include <iterator>

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