【问题标题】:What's the fastest way to get all of the indices of false elements in a boolean array?获取布尔数组中所有错误元素索引的最快方法是什么?
【发布时间】:2013-06-02 23:21:19
【问题描述】:

假设我有一个布尔数组: {false, true, false, false, true, true, ...}

获取(例如)错误元素的索引的最快方法(最优化)是什么?在这种情况下0 2 3?

【问题讨论】:

  • 数组的大小是多少?您需要索引在什么数据结构中?为什么是数组而不是 BitVector
  • 优化是一个很流行的词——就什么而言?表现最快?写得最快?最快理解?
  • 简单的for 循环可以很好地完成这项工作。
  • 如果这个列表有超过几千个元素,可能值得担心优化它......否则我认为更清晰/最明显正确会更好。无论如何,这可能只是一个明显的循环。 :)
  • 打电话给英特尔,让他们为您定制一些硬件来完成这项任务。他们可以为你做到这一点不到十亿美元。 (除非您有无限的预算,否则您不会获得“最快的”。限制是什么,您愿意花多少钱?)

标签: c# .net arrays optimization boolean


【解决方案1】:

for 循环可能是最快的方法:

List<int> indices = new List<int>();
for (int i=0;i < theArray.Length; ++i)
{
   if (theArray[i])
   {
       indices.Add(i);
   }
}

请注意,通过预分配List&lt;int&gt;,您可能会以额外内存为代价获得一点速度:

List<int> indices = new List<int>(theArray.Length);

这将避免额外的内存分配。

【讨论】:

  • 这比@renan 的位掩码快吗?
  • 哇。真的比我想象的要容易。我是大 O 表示法的新手,但这是线性的,对吧?只有一个循环(它总是和我们需要的一样大),其中只有一个 if 语句。
  • @DavidWest 是的,这是 O(N)。
【解决方案2】:

最多 32 个元素:

int mask = 0;
for (int i = 0; i < arr.Length; i++)
{
    if (!arr[i]) mask += 1 << i;
}

掩码将是一个 32 位掩码,如果该位索引处的元素为假,则每个位为 1,如果该元素为真,则为 0。它是数组的另一种表示形式,如果您愿意的话,使用四个字节而不是每个布尔值一个字节。对于最多 64 个元素,您可以改用 long 类型。但是,据我记得,使用int,您可以将enum 转换为适当的位掩码。

涉及的总字节数:四个用于掩码,一个用于数组的每个元素,四个用于循环中的索引。完成的总分配:两个(如果我们不计算数组的分配)。

【讨论】:

  • 我不完全确定使用浮点幂运算会比使用整数运算更快...
  • 2**n 是一个快速操作...但正确的写法是(1 &lt;&lt; n),而不是Math.Pow(2, n)
【解决方案3】:

这可能不是最快的方法,但它会产生一个仅包含真实索引的 IEnumerable。对我来说似乎有点乱。我想知道它是否可以简化? for 循环可能是最好的。但它的价值:

var bools = new bool[] {true, false, true, true, false, false, true, false, true};
var falseIndices = bools.Select((b, i) => new { Index = i, Value = b })
                        .Where(o => !o.Value)
                        .Select(o => o.Index);

【讨论】:

    【解决方案4】:

    在您获得经验证据之前,您永远不会知道最快的解决方案是什么。您可以使用下一个代码作为 LINQ 和for 循环方法的计算速度比较的参考:

    var r = new Random();
    bool[] vals = new bool[100000000];
    
    //initializing
    for (int i = 0; i < vals.Length; i++)
    {
        vals[i] = r.Next(2)==0;
    }
    var watch = Stopwatch.StartNew();
    
    //for loop benchmark
    List<int> indices = new List<int>(vals.Length);
    for(int i = 0; i < vals.Length; ++i)
    {
        if(!vals[i])
            indices.Add(i);
    }
    Console.WriteLine ("for loop: {0} ms", watch.ElapsedMilliseconds);
    
    watch.Restart();
    
    //LINQ benchmark
    List<int> falseIndices = vals.Where(flag => !flag)
                                 .Select((flag, index) => index)
                                 .ToList();
    
    Console.WriteLine ("LINQ: {0} ms", watch.ElapsedMilliseconds);  
    

    打印一些东西:

    for loop: 600 ms
    LINQ: 2072 ms
    

    【讨论】:

      【解决方案5】:

      最快的方法可能包括使用fixed 固定阵列。但是结果代码的可读性不是很好。

      您应该确保“找到false 元素的索引”确实是您应用程序的瓶颈,并且找到“最快的方法”确实可以改善它。可读性和可维护性也是非常重要的方面。

      正如其他人所建议的,一个简单的 for 循环具有良好的性能并且非常易读。 Linq 解决方案不会慢很多。

      如果数组包含大量true 而不是很多false,您可以考虑对每个false 使用Array.IndexOf(),直到您找不到更多的出现。它可能比每个元素的方法更快。

      【讨论】:

      • 我实际上怀疑固定数组会比 for 循环更快,因为固定的主要优点是消除了边界检查,但 JIT 无论如何都会为简单的 for 循环做到这一点......
      • 如果我理解正确,array[index] 还需要查找array 的位置,而访问固定数组上的指针则不需要。只是为了尝试,我注意到使用指针顺序更改byte[] 中的数据比使用每个元素方法快得多。在这种情况下我不知道,但无论如何我都不建议使用它:)
      • 更改数组中的数据(在大多数情况下)要慢得多,因为写入时会检查边界。在 for 循环中写入数组将消除 JIT 的边界检查优化。从数组中读取通常不会,所以它仍然会很快。
      • @ReedCopsey 对于 50000 个布尔值,其中 50% 为假,固定解决方案比常规 for 循环快 20%。我只需要尝试 :) 正如您提到的那样,差异并不像更改数据时那么大。
      【解决方案6】:

      我也做了一些计时,这表明人们所说的是正确的: 使用指针并不比简单循环快很多。

      我怀疑仅填充结果列表会占用最多的时间。

      这是我从以下测试程序中得到的时间:

      Unsafe took 00:00:06.6450271
      Safe   took 00:00:06.7691818
      

      测试代码如下...

      using System;
      using System.Collections.Generic;
      using System.Diagnostics;
      
      namespace Demo
      {
          class Program
          {
              void Run()
              {
                  int size = 10000000;
                  bool[] array = new bool[size];
      
                  for (int i = 0; i < size - 1; ++i)
                      array[i] = true;
      
                  int count = 100;
      
                  time(() => UnsafeCountSetFlags(array), "Unsafe", count);
                  time(() => SafeCountSetFlags(array),   "Safe",   count);
              }
      
              void time(Action action, string title, int count)
              {
                  var sw = Stopwatch.StartNew();
      
                  for (int i = 0; i < count; ++i)
                      action();
      
                  Console.WriteLine(title + " took " + sw.Elapsed);
              }
      
              List<int> UnsafeCountSetFlags(bool[] array)
              {
                  unsafe
                  {
                      fixed (bool* parray = array)
                      {
                          List<int> result = new List<int>();
      
                          for (bool* p = parray, end = parray + array.Length; p != end; ++p)
                              if (*p)
                                  result.Add((int)(p-parray));
      
                          return result;
                      }
                  }
              }
      
              List<int> SafeCountSetFlags(bool[] array)
              {
                  List<int> result = new List<int>();
      
                  for (int i = 0; i < array.Length; ++i)
                      if (array[i])
                          result.Add(i);
      
                  return result;
              }
      
              static void Main()
              {
                  new Program().Run();
              }
          }
      }
      

      【讨论】:

        【解决方案7】:

        尝试以下操作:

            BlockingCollection<int> indexes = new BlockingCollection<int>(x.Length);
            Parallel.For(0, x.Length, i =>
            {
                if (x[i])
                {
                    indexes.Add(i);
                }
            });
        

        【讨论】:

        • 你试过了吗?在我的机器上,100000000 bool 值需要 10 秒,而 for 循环需要 500 毫秒
        猜你喜欢
        • 2011-03-30
        • 1970-01-01
        • 2016-12-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-19
        • 2013-09-30
        • 1970-01-01
        相关资源
        最近更新 更多