【问题标题】:C++: Fastest method to check if all array elements are equalC++:检查所有数组元素是否相等的最快方法
【发布时间】:2012-12-16 17:16:12
【问题描述】:

检查数组(首选整数数组)的所有元素是否相等的最快方法是什么。到目前为止,我一直在使用以下代码:

bool check(int array[], int n)
{   
    bool flag = 0;

    for(int i = 0; i < n - 1; i++)      
    {         
        if(array[i] != array[i + 1])
            flag = 1;
    }

    return flag;
}

【问题讨论】:

  • 您不需要flag,只需在循环中返回false,并在其下方返回true。否则我认为这是尽可能高效的。
  • 为什么要将10 分配给bool
  • @ChristianRau:人们很有趣,他们看到像 C++ 这样的弱类型语言就会发疯。有些人甚至会将01 分配给long,依赖于隐式转换!
  • @NoSenseEtAl 你的意思是memcmp 比较两个ints?我不这么认为。可以肯定的是,你不是那些认为他想比较两个范围的人,不是吗?
  • @ChristianRau - 是的,我的错...

标签: c++ arrays


【解决方案1】:

我认为以下内容比评分最高的答案更具可读性,而且我会打赌更有效率(但尚未进行基准测试)

bool check(int a[], int n)
{   
    if (n)
    {
        auto first = a[0];
        
        for(int i = 1; i < n; i++)      
        {         
            if(array[i] != first) return false;
        }
        
        return true;
    }

    return true;    //change to false for the OPs logic.  I prefer logical true here
}

【讨论】:

  • ...我也会打赌更有效率...绝对相反是正确的。
【解决方案2】:

快速哈希映射技术:

bool areSame(int a[],int n)
{
    unordered_map<int,int> m; //hash map to store the frequency od every

    for(int i=0;i<n;i++)
       m[a[i]]++;
      
    if(m.size()==1)
       return true;
    else
       return false;
}

【讨论】:

    【解决方案3】:

    为了程序员的效率,您可以在一行中尝试以下所有内容。

    vector<int> v{1, 1, 1, 1};
    all_of(v.cbegin(), v.cend(), [&r=v[0]](int value){ return value == r; }->bool);
    

    我没有测试运行这段代码,如果有语法错误请告诉我。

    【讨论】:

      【解决方案4】:
      bool check_identity (int a[], int b[], const int size)
      {
          int i;
          i = 0;
          while ((i < size-1) && (a[i] == b[i]))   i++;
      
          return (a[i] == b[i]);
      
      }
      

      【讨论】:

      • 问题只有 1 个数组。
      【解决方案5】:

      这是一个有效的 C++11 的可靠解决方案。 优点是您不需要手动使用索引或迭代器。这是一个最佳实践

      比手写循环更喜欢算法调用 [Herb Sutter - C++ 编码标准]

      我认为这与 Paul R 的解决方案同样有效。

      bool check(const int a[], int n)
      {
            return !std::all_of(a, a+n, [a](int x){ return x==a[0]; });
      }
      

      【讨论】:

      • 凭借我在 Core i5 上的粗略单位测量技能,此实现似乎以每个元素约 0.7 ns 的速率进行检查,这相当快但可信。您是否尝试像 Paul R 那样将 a[0] 提升到循环之外?当我这样做时,它下降到每个元素 6.4e-10 ns,我觉得这令人难以置信。我假设我犯了一个错误,并且我的粗略测量不再捕获第二种情况下的计算。其他人有测量吗?
      • Herb Sutter 真的说过“算法调用”吗?或者像“标准库调用”之类的东西?引用?
      • @EJP Herb Sutter,Andrei Alexandrescu - C++ 编码标准:101 条规则、指南和最佳实践,出版商:Addison-Wesley Professional; 1 版(2004 年 11 月 4 日),ISBN-10:9780321113580,第 162 - 164 页
      • 好吧,这在数组为空时也有效(即,当n 为零时)。
      【解决方案6】:

      理论上,我会提出这样的建议:

      bool check_single(const int a[], int n)
      {
          for (int i = 1; i < n; ++i) {
              if (a[0] != a[n]) { return false; }
          }
          return true;
      }
      

      与其他(已提议的)版本相比:

      • a[0] 将被编译器提升到循环外,这意味着循环内的单个数组访问
      • 我们从 0 循环到 n,这比加载 a[0] 然后从 a[n] 循环更好(访问方式)

      显然,它仍然检查 N 个元素,因此是 O(N)。

      【讨论】:

      • 如果 any 两个元素相等,这将返回 true。可能您希望相等性检查成为不等式检查?
      • ...你更喜欢单态布尔值?
      • @xtofl:是的!确切地! :D
      【解决方案7】:

      一旦找到不匹配的元素,就可以跳出循环:

      bool check(const int array[], int n)
      {   
          for (int i = 0; i < n - 1; i++)      
          {         
              if (array[i] != array[i + 1])
                  return true;
          }
          return false;
      }
      

      如果这对性能至关重要,那么可以将其进一步优化为:

      bool check(const int array[], int n)
      {   
          const int a0 = array[0];
      
          for (int i = 1; i < n; i++)      
          {         
              if (array[i] != a0)
                  return true;
          }
          return false;
      }
      

      【讨论】:

      • 是的,这肯定会让它更快。谢谢,下次发帖时我会记住代码格式。
      • 使用常量元素(通常是第一个)来比较所有其他元素,允许编译器将其负载提升到循环之外。但不确定是否会有很大影响。
      【解决方案8】:

      我们将它基本上是一个 O(n) 操作,所以除了在第一次失败时放弃标志和 return false; 以及在迭代后放弃 return true; 之外,你不能做得比你所拥有的更好。

      【讨论】:

      • 仅仅因为它是 O(n) 并不意味着它不能被优化。说什么无意义的话。
      • 请解释一下我说的“……毫无意义的事情……”。运行时的上限是并且将永远是 O(n),我建议在第一次不匹配时快速失败是(缺乏更多细节)一个相当好的优化!
      • 具体来说,“它是 O(n),所以你不能做得更好”是没有意义的。它表明过度教育已经腐烂了思想。例如,访问我的 hd 上的数据是 O(n)。这是否意味着我不能用 ssd 做得更好?为了论证起见,可以通过对更大的单词(使用 sse)进行操作来加速这个算法。这不是更好吗?秩序就是秩序,但系数也很重要。
      • 请正确引用我的话。我写了“......不能做得很多更好......”。也许我应该进一步澄清,写“......不能做得更好......”,但我认为像你这样受过教育的人能够推断出这一点。我同意您可以优化原始代码并引入各种特定于处理器的技巧,但事实仍然是它仍然是线性运算。
      • 你还是错了,一个 O(n) 操作可以优化几个数量级。无论是通过从 cpu 到 gpu、从 hd 到 ssd、从解释语言到编译、特殊的 cpu 指令、对算法的合理重新实现,甚至是针对特定问题的巧妙优化(例如,在这种情况下想象数组元素通常非常不同,但有时完全相同。然后,​​提前退出将带来数量级的加速。)
      【解决方案9】:

      将数组重新转换为更大的数据类型。例如,对 64 位整数进行操作,或使用 SSE 或 AVX 内在函数进行 128 位或 256 位操作。例如,SSE2 内在函数是 _mm_cmpeq_epi32,您将使用 _mm_or_si128 的结果。重复应用 _mm_srli_si128 和 _mm_cvtsi128_si32 检查结果。每隔几百次迭代检查一次结果,以便提前退出​​。

      确保对对齐的内存进行操作,将未对齐的开始和结束检查为整数,并将第一个打包元素与自身检查。

      【讨论】:

      • 也许我们可以像这样重用 glibc 的 memcmp(已经优化了汇编):1)将数组拆分为大小相等的部分。 2) 比较它们 3) 如果不同,返回 false。否则采取前半部分,忽略第二部分) 4)将前半部分分成两部分。 5) ... 这只会导致 N 次比较。
      【解决方案10】:
      bool check(int array[],int n)
      {       
        // here 1st element is checked with others. This decreases the number of iteration by 1.
        // also it returns immediately. 
        // The requirement is to check if all the elements are equal. 
        // So if 1st element is equal to others then all elements are equal. 
        // Otherwise the  elements are not equal.
        for(int i=1;i<n;i++)      
        {         
          if(array[0]!=array[i])
            return false;
        }        
        return true;
      }
      

      【讨论】:

      • 这是错误的。它只是比较数组的第一个和第二个元素并立即退出。
      • 如果你这样做,它应该是 for(int i=1;i&lt;n;i++) 并且 else 打破它,return 1 应该发生在循环之外并且 1/0 被翻转,如果不是应该返回 1相等,如果都相等则为 0
      • 还是坏了——提示:return 语句之一放错了地方。
      • 应该返回true/false而不是0/1,注意返回类型是bool
      • 它应该是但不是最好的代码,应该尝试构建无警告程序。你的编译器应该为你的返回类型给出一个警告。 :)
      【解决方案11】:

      在您的平台上找到一个支持线程或并行 for 循环的库,并将计算分开,以便不同的内核测试不同范围的数组。

      这里列出了一些可用的库:

      http://parallel-for.sourceforge.net/parallelfor.html

      或者,您可以利用许多 GPU 提供的并行性。

      【讨论】:

      • 这不是一个适合并行性的问题,因为这里的大问题是内存带宽,所以不是一个线程等待 RAM 访问,而是多个线程等待更长的时间RAM 访问。 IA32 架构意味着由于硬件内存流,单线程是最好的。
      • 是的。同样,如果数组很小,它们将保留在一个处理器的缓存中
      • 我同意这些好处取决于系统架构和所分析数据的性质。如果目标是找到最快的方法,我相信在某些情况下,并行化将比串行运行数据更快。
      【解决方案12】:
      int check(const int a[], int n)
      {   
          while(--n>0 && a[n]==a[0]);
          return n!=0;
      }
      

      【讨论】:

      • 因此,为了安全起见,为了应对他对bool 类型的错误使用,您只是将返回类型转换为int? ;)
      • 好吧,在这种情况下,它与实际问题无关,不是吗?好吧,无论如何都投了赞成票;)
      • 如果您发现使用递减索引很有吸引力,我建议不要使用a[0] 作为常量。您开始比较位于数组两端的 a[0]a[length-1],在比较开始之前会产生两次 RAM 往返(对于长度超过 64 字节的数组)。
      • 很棒的代码。我会将 n!=0 更改为 n>0,以处理 n=0 的情况
      • 我们可以比较一下 Paul R 的答案吗?
      猜你喜欢
      • 2017-11-01
      • 2015-02-12
      • 2018-05-13
      • 1970-01-01
      • 1970-01-01
      • 2019-07-04
      • 2013-12-15
      相关资源
      最近更新 更多