在 C++ 中检查向量的所有元素是不是相等

Posted

技术标签:

【中文标题】在 C++ 中检查向量的所有元素是不是相等【英文标题】:Checking if all elements of a vector are equal in C++在 C++ 中检查向量的所有元素是否相等 【发布时间】:2013-11-29 13:37:21 【问题描述】:

如果我有一个值向量并想检查它们是否都相同,那么在 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;

但是在互联网上阅读时,我看到了其他答案,例如使用 setfind_if 算法。那么最有效的方法是什么,为什么?我想我的方法不是最好的,因为它涉及对每个元素进行排序,然后调整向量的大小 - 但也许我错了。

【问题讨论】:

之前有人问过这个问题:***.com/questions/15531258/… 那里的答案指出,重要的是,O(n) compare-all-to-the-first 方法的效率可以通过确保你获得一旦找到第一个不相等的元素,就中断循环。 数组版本:***.com/questions/14120346/… 是data() 的子集。 空向量的首选行为是什么? std::equal 和 std::adjacent_find 答案返回 false,std::find_if 和 std::all_of 返回 true。 【参考方案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;

【讨论】:

我即将使用adjacent_find 发布解决方案,但我的解决方案会包含一个 lambda 谓词,这就是我最后没有发布它的原因。 not_equal_to 是使它成为完美解决方案的缺失部分。 奇怪的是没有人提到std::all_of.:) 您的评论表明使用 all_of 有更好的解决方案。如果是这样,您(或某人)可以编辑您的答案以显示它吗?【参考方案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];

编辑:

关于性能,在使用 100m 个元素进行测试后,我发现 在 Visual Studio 2015 中 version 1 的速度大约是 version 2 的两倍。这是因为当您使用 ints、float 等时,vs2015 的最新编译器在 c++ std 实现中使用 sse instructions。

如果您使用_mm_testc_si128,您将获得与std::equal 相似的性能

【讨论】:

在内部增加 2 个迭代器时,效率低于仅遍历数组。 @Luchian Grigore 是的,但写起来更短:) 是的,但是“那么最有效的方法是什么?为什么?” @Luchian Grigore 好的,我会编辑添加一些有效的东西。 请注意,虽然他在底部要求“最有效”,但在顶部他要求“最佳方式”,但前提是 C++ 和高效。 “最佳方式”允许人们考虑样式、可读性等,同时平衡可能的 2 减慢因素。【参考方案3】:

鉴于对向量没有任何限制,无论采用何种方法,您都必须至少迭代一次向量。所以只需选择第一个元素并检查所有其他元素是否与它相等。

【讨论】:

发现不等于第一个值后记得短接!【参考方案4】:

使用 std::all_of 和 C++11 lambda

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

【讨论】:

也许也可以使用 begin()+1? @Mikhail,如果你能保证values 不为空,begin()+1 确实会跳过一个不必要的评估。但是如果空虚的可能性,那么上面的答案提供了安全性,因为它在这种情况下只返回true。【参考方案5】:

虽然std::unique 的渐近复杂度是线性的,但操作的实际成本可能比您需要的要大得多,而且它是一种就地算法(它会随时修改数据)。

最快的方法是假设如果向量包含单个元素,则根据定义它是唯一的。如果向量包含更多元素,那么您只需要检查它们是否都与第一个完全相等。为此,您只需要找到与第一个不同的第一个元素,从第二个开始搜索。如果存在这样的元素,则元素不是唯一的。

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 工具链中,您可以在 lambda 中使用正确的类型。在 C++03 中,您可以使用 std::notstd::bind1st/std::bind2ndstd::equal 的组合来代替 lambda。

这种方法的成本是distance(start,different element) 比较并且没有副本。比较次数的预期和最坏情况线性成本(并且没有副本!)

【讨论】:

开场白具有误导性。是的,Unique 是线性的,但它紧随其后,这绝对不是线性的。 @RichardPlunkett:如果您唯一的期望是检测是否存在唯一值,则不需要排序。请注意,这并不是试图解决删除重复或查找有多少唯一值的一般问题,而是要查找是否至少存在一个非重复值。也许我应该在答案中更明确地说......虽然这只是对问题方法的评论,而不是我自己的方法。【参考方案6】:

排序是一个 O(NlogN) 的任务。

这在 O(N) 中很容易解决,因此您当前的方法很差。

一个简单的 O(N) 就像 Luchian Grigore 所建议的那样,只迭代一次向量,将每个元素与第一个元素进行比较。

【讨论】:

【参考方案7】:
if(std::all_of(myvector.begin()+1, myvector.end(), std::bind(std::equal_to<int>(),
                                      std::placeholders::_1, myvector.front())) 
  // all members are equal

【讨论】:

【参考方案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】:

也许是这样的。它只遍历vector一次,不会弄乱vector的内容。

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

【讨论】:

这也是浪费时间,因为count 必须在找到第一个不同元素后继续运行,但您只需要知道是否有一个,所以这些都是浪费的周期。我不明白为什么 4 年后我们需要一个低效的替代方案。【参考方案10】:

在您的特定情况下,迭代向量元素并找到与第一个元素不同的元素就足够了。您甚至可能很幸运地在评估向量中的所有元素之前停下来。 (可以使用 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());

【讨论】:

为什么这比遍历向量更有效? 它不是,而且通常和排序一样糟糕。 不是,我还没有时间详细说明我的答案。但是,我认为 Ward9250 知道如果他寻求的不仅仅是一个独特的价值,那么构建一个集合是可能的。【参考方案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;

【讨论】:

我不认为这比 4 年前发布的所有其他方式更“简单”。事实上,这是浪费时间,因为 count 即使在找到第一个不同的元素后也必须继续运行,但您只需要知道是否有一个,所以这些纯粹是浪费循环。 你提出了一个很好的观点,当时我没有考虑过它会遍历所有元素;但是,许多其他解决方案也是如此,我并不认为这是最好的方法,但我遇到了这个问题,发现这是最易读的解决方案。如果vector 非常大,那么绝对值得使用不会遍历所有元素的解决方案。【参考方案12】:

LLVM 提供了一些独立可用的头文件+库:

#include <llvm/ADT/STLExtras.h>
if (llvm::is_splat(myvector))
  std::cout << "All elements are the same!" << std::endl;

https://godbolt.org/z/fQX-jc

【讨论】:

【参考方案13】:

为了完整起见,因为它仍然不是最有效的,您可以使用 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

【讨论】:

【参考方案14】:

使用C++ 14的另一种方法:

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

也是N阶

【讨论】:

【参考方案15】:

这是一个可读的 C++17 解决方案,它可能会让学生想起 std::vector 的其他构造函数:

if (v==std::vector(v.size(),v[0])) 
  // you guys are all the same

...在 C++17 之前,std::vector 右值需要明确提供其类型:

if (v==std::vector<typename decltype(v)::value_type>(v.size(),v[0])) 
  // you guys are all the same

【讨论】:

以上是关于在 C++ 中检查向量的所有元素是不是相等的主要内容,如果未能解决你的问题,请参考以下文章

C ++如何检查数组中的元素是不是相等?

如何检查数组中的所有值是不是彼此相等。 C++

检查数组中所有元素是不是相等的最快方法

使用 pandas GroupBy 检查组中的所有元素是不是相等

如何检查向量的所有元素是不是在 Eigen c++ 中的另一个向量中?

Ruby:检查数组中所有对象的属性是不是相等