【问题标题】:Fastest method to check if all elements of 2d array are equal检查二维数组的所有元素是否相等的最快方法
【发布时间】:2017-11-01 18:43:39
【问题描述】:

我有一个二维数组houses[5][2] = {{1,1},{1,1},{1,1},{1,1},{1,1}} 检查该数组中的所有元素是否相等的最快方法是什么? 这是我到目前为止所尝试的: ```

for(int j=0;j<5;j++){
 for(int k=0;k<6;k++){
   if(houses[j][k] == houses[j+1][k+1] && j+1 != 5 && k + 1 != 6)
     equal = true;
    else{
     equal = false;
     break;
     }
}
}

这不会比较所有元素,我知道如何比较所有元素,但它似乎是一个很长的循环......有没有更快的方法来做到这一点?

【问题讨论】:

  • 如果您希望所有元素都相等,您只需将 1 与 2 进行比较,如果它们相等,则测试 2 与 3,然后测试 3 与 4,依此类推。无需在这里花哨!
  • 您的边界检查为时已晚。您首先访问元素,然后才检查您是否在界限内。你应该要么做if (j+1 != 5 &amp;&amp; k+1 != 6 &amp;&amp; ...),要么直接调整循环边界

标签: c++ arrays algorithm loops equality


【解决方案1】:

您当前的代码将失败,因为break 只会让您跳出一个循环。您必须同时退出,这需要第二次检查,如下所示:

auto the_value = houses[0][0];
bool equal     = true;

for(int j=0;j<5;j++){
  for(int k=0;k<6;k++){
    if(houses[j][k]!=the_value){
      equal = false;
      goto done;
    }
  }
  if(!equal)
    break
}

(将第一个元素存储在变量中,然后遍历所有元素以检查它们是否等于该变量,这样可以避免通过检查相邻元素来调用混乱。)

同时跳出两个循环需要黑魔法 (goto),但如果你有纪律,可能会更易读/可维护,并且可能会稍微快一些,具体取决于你的编译器:

auto the_value = houses[0][0];
bool equal     = true;

for(int j=0;j<5;j++)
for(int k=0;k<6;k++)
  if(houses[j][k]!=the_value){
    equal = false;
    goto done; //Danger, Will Robinson!
  }

done:
//More stuff

您可能会发现平面数组更快:

auto the_value = houses[0][0];
bool equal     = true;
for(int i=0;i<5*6;i++)
  if(houses[i]!=the_value){
    equal = false;
    break;
  }

二维数组作为一维连续数组存储在内存中。使用平面数组寻址访问相同的内存位置,但明确避免强制内部算术。对于高性能代码,您不妨考虑默认使用平面数组。

由于您可能会多次使用此类函数或将其嵌入到其他复杂的代码中,因此您可能希望将其抽象化:

template<class T>
bool AllEqual(const T& arr, size_t N){
  T the_value = arr[0];
  for(int i=0;i<N;i++)
    if(arr[i]!=the_value)
      return false;
  return true;
}

AllEqual(houses, 5*6);

由于您使用 C++ 进行编码,因此您可能无论如何都不想使用原始数组。假设平面数组,让我们使用 STL 重写您的代码:

template<class T>
bool AllEqual(const std::vector<T>& arr){
  return std::all_of(arr.begin(), arr.end(), [&](const T& x){ return x==arr[0]; });
}

std::vector<int> houses = {}; //Replace with appropriate initialization
if(AllEqual(houses))
  //Do stuff

(另外:正如另一个回答者提到的,您向数组添加数据的方式似乎暗示它应该是 2x6/6x2 数组而不是 5x6/6x5。)

【讨论】:

  • @NathanOliver:关于内存布局是正确的。使用平面数组消除了双重循环,这使编译器可以更好地优化相等性检查。
  • 你可以把它放在一个函数中然后返回,而不是 goto
  • 当然,@Kevin。但我不认为有一个额外的命名函数或一个 lambda 值得伴随的可读性降低或安全性的增加。如果循环体明显更复杂,我可能会同意。我将编辑我的答案以指出这一点。
  • 我认为在一个可能很大的函数中使用 all_equal(array) 调用比使用 2 个 for 循环更具可读性。另外,如果您需要在其他地方或在不同的阵列上进行检查,您已经拥有该功能。
  • @Kevin:同意。
【解决方案2】:

首先,你了解你的数组是什么样子的吗?你有 6 次 2,但你使用了houses[5][6]。那就是5行6列。你应该得到一个错误:

main.cpp:5:55: error: excess elements in array initializer
    int houses[5][6] = {{1,1},{1,1},{1,1},{1,1},{1,1},{1,1}};
                                                      ^~~~~

你真正想要的是 6 行 2 列。


至于检查二维数组的所有元素是否相等,我会采用简单的方法;将数组的第一个元素存储到变量中,例如命名为v,并检查该值与所有其他元素。如果它不等于一个元素,那么做出决定并说并非所有元素都相等就足够了,如下例所示:

#include <iostream>

bool allEqual(int arr[][2], int rows)
{
    int v = arr[0][0];
    for(int i = 0; i < rows; ++i)
        for(int j = 0; j < 2; ++j)
            if(v != arr[i][j])
                return false;
    return true;
}

int main(void)
{
    int houses[6][2] = {{1,1},{1,1},{1,1},{1,1},{1,1},{1,1}};
    allEqual(houses, 6) ? std::cout << "All " : std::cout << "Not all ";
    std::cout << "elements are equal\n";
    return 0;
}

如果我用一维模拟二维数组,会更快吗?

我对此表示怀疑。他们的想法是内存位置是连续的,但这是在 2D 情况下最常发生的情况,因为行多于列。

这是我的实验:

Georgioss-MacBook-Pro:~ gsamaras$ g++ -Wall -std=c++0x -O3 -o 2d 2d.cpp
Georgioss-MacBook-Pro:~ gsamaras$ ./2d
2D array took 1.48e-10 seconds.
Georgioss-MacBook-Pro:~ gsamaras$ g++ -Wall -std=c++0x -O3 -o 1d 1d.cpp
Georgioss-MacBook-Pro:~ gsamaras$ ./1d
Emulating 2D array with 1D array took 1.5e-10 seconds.

和我的代码,基于我的Time measurements (C++):

#include <iostream>

#define ROWS 10000
#define COLS 20
#define REPEAT 1000

#include <iostream>
#include <ctime>
#include <ratio>
#include <chrono>

bool allEqual(int* arr, const int size)
{
    int v = arr[0];
    for(int i = 0; i < size; ++i)
        if(v != arr[i])
            return false;
    return true;
}

void fill(int* arr, const int size)
{
    for(int i = 0; i < size; ++i)
        arr[i] = 1;
}

int main(void)
{
    const int size = ROWS * COLS;
    int houses[size];
    fill(houses, size);

    bool equal;

    using namespace std::chrono;
    high_resolution_clock::time_point t1 = high_resolution_clock::now();
    for(int i = 0; i < REPEAT; ++i)
        equal = allEqual(houses, size);
    high_resolution_clock::time_point t2 = high_resolution_clock::now();
    duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
    std::cout << "Emulating 2D array with 1D array took " << time_span.count()/(double)REPEAT << " seconds.\n";

    return 0;
}

2d.cpp 是直接的方式。

使用此answer 中为 2D 数组提供的相等方法,报告的时序相似。

另外,还有std::equal,在性能上与我上面的代码相当,报告时间为:

std::equal with 2D array took 1.63e-10 seconds.

它的复杂性是:“在 first1 和 last1 之间的距离上达到线性:比较元素直到发现不匹配。”


总结

std::equal 没问题,对程序员的工作量较小,所以用它吧。

【讨论】:

  • std::equal 不是用于比较两个数组的相等性而不是单个数组的所有元素吗?
  • 另外:定义二维数组的方式确保它在内存中是连续的(作为平面数组)。如果你对行/列进行动态分配,尤其是在碎片化的环境中,速度差异会更加明显。在阵列中存储访问位置的 2D 与 1D 索引也会对性能产生很大影响。但是,对于 OP 提出的用例,我同意速度应该大致相等,正如您所发现的那样。
  • 是的@Richard 我同意,很好的评论。至于std::equal,见参考:比较[first1,last1)范围内的元素与从first2开始的范围内的元素,如果两个范围内的所有元素都匹配,则返回true..
【解决方案3】:

最快的方法:

int houses[6][2] = {{1,1},{1,1},{1,1},{1,1},{1,1},{1,2}};

int equals()
{
    int *p = (int *)houses;
    int *end = p + 6*2;
    int v = *p++;
    for(; p < end; p++)
        if (*p != v)
            return 0;
    return 1;
}

我写它是为了好玩,不要在生产中使用它。 相反,遍历它们:

int equals() {
   int v = houses[0][0];
   for(int j=0;j<5;j++)
      for(int k=0;k<6;k++)
         if (houses[i][j] != v)
            return false;
   return true;
}

【讨论】:

  • 嘿,很好的答案,我用你的代码来比较它的速度和我的,希望你不介意。 =)
  • 能否请您也尝试比较更新代码的速度?我很好奇……
【解决方案4】:

多件事:

首先,正如其他人指出的那样,行:

int houses[5][6] = {{1,1},{1,1},{1,1},{1,1},{1,1},{1,1}};

错了,左边声明了一个5行6列的数组,而右边却构成了一个6行2列的数组。

在一般情况下,比较 2d 数组(甚至 1d 数组)的所有元素在 O(n) 中,因为对于每个元素,您必须检查所有其他元素。你可以稍微优化一下,但它仍然是一个 O(n) 算法。在最一般的情况下:

A[n][m] is an array of n rows and m columns

for(int i=0; i<n*m; i++)
{
   if(A[0][0] != A[i/n][i%n])
     return false;
}
return true;

这似乎有点令人困惑,所以让我解释一下:

二维数组有 n*m 个元素,因此在一个循环中查看所有元素的简单方法是执行 [i/n](如果 i

由于我们希望所有元素都相同,因此如果第一个元素等于所有其他元素,则它们都相同,如果至少 on 不同,则它们不完全相同。

【讨论】:

    【解决方案5】:

    我们可以简单地检查该数组中的所有元素是否相等 或不。只需分配变量中的第一行和列元素。然后比较每个元素。如果不相等则返回false。

    代码片段:


    bool Equal(int **arr, int row, int col)
    {
       int v = arr[0][0];
       for(int i=0; i<row; i++)
       {
          for(int k=0; k<col; k++)
          {
             if(arr[i][k]!=v)  return false;
          }
       }
       return true;
    }
    

    【讨论】:

      猜你喜欢
      • 2012-12-16
      • 2019-07-04
      • 2023-02-10
      • 2021-02-03
      • 2018-05-13
      • 1970-01-01
      • 2015-02-12
      • 1970-01-01
      相关资源
      最近更新 更多