C#检查2D阵列中的任何元素是NaN还是Infinity的最佳方法

时间:2013-11-05 23:14:07

标签: c# multidimensional-array

有谁知道检查2D阵列中没有元素是NaN还是Infinity的最佳方法是什么?我的数组可能很大,所以我不想循环它。我正在考虑使用

double[,] myArray;
if ( !double.IsNaN(myArray.Cast<double>().Min() ) && !double.IsInfinity( myArray.Cast<double>().Min() )
{
    // have finite values as elements
}

用于NaN和无穷大检查。我只需要知道数组中是否存在NaN或无穷大的元素,所以我只想检查数组中的最小元素应该为NaN和无穷大做技巧。有一个更好的方法吗?感谢您提供的任何帮助或建议。

4 个答案:

答案 0 :(得分:4)

我个人会用:

if (!myArray.Cast<double>().Any(d => double.IsNaN(d) || double.IsInfinity(d)))
{
     // All have correct values...

除了缩短之外,这应该接近手动编写的循环的速度,因为它使它保持单次通过值,并且如果达到“坏”值则立即退出(从Any()开始将停止评估失败)。


  

由于我的数组可能会变大,我想我会编写循环以获得更好的性能/

如果您正在处理非常大的数组,那么可以选择并行执行此操作:

if (!myArray.Cast<double>().AsParallel()
            .Any(d => double.IsNaN(d) || double.IsInfinity(d)))
{
     // All have correct values...

对于一个非常大的数组,这通常会执行直接循环,因为多个内核可以通过PLINQ处理分区数据。

答案 1 :(得分:2)

手动编码循环比建议的Linq替代方案快得多。见下面的更新2。

无论如何,您将循环遍历数组的每个元素以回答您的问题,因为没有为数组指定排序。

您的代码就是这样,隐藏了循环的实现。使用Linq进行循环将至少比编写自己的for循环慢一点,因为它可以完成更多工作。 (实际上,由于你没有缓存myArray.Cast<double>().Min()的结果,所以你现在循环遍历数组两次。

对于大多数处理器,您应该遍历row major order中的循环,因为它遍历相邻的内存地址(在我自己的测试中,以错误的顺序遍历可能会慢30%)。

如果实际上对数组中的值有一些顺序,则可以进行更有效的搜索。

<强>更新

我怀疑手动编码循环将比使用Linq快得多,因为循环体是微不足道的。如果每个最后一点性能都很重要,我建议你实现两个选项和基准。

Reed建议并行运行也值得进行基准测试。您可以使用Parallel.For非常轻松地并行化传统迭代。

更新2

测量Reed解决方案的性能和手动编码循环。

Linq:1448.6353 ms

For循环:125.2208 ms

使用的代码:

class Program
{
    const int SIZE = 3000;
    static double[,] data = new double[SIZE,SIZE];


    static void Main(string[] args)
    {
        if (args.Length >= 1 && args[0] == "/for")
        {
            Benchmark(ForLoop);
        }
        else
        {
            Benchmark(LinqLoop);
        }
    }

    static void ForLoop()
    {
        for (int i = 0; i < SIZE; i++)
        {
            for (int j = 0; j < SIZE; j++)
            {
                if (double.IsNaN(data[i, j]) || double.IsInfinity(data[i, j])) Console.WriteLine("FOUND!");
            }
        }
    }

    static void LinqLoop()
    {
        if (!data.Cast<double>().Any(d => double.IsNaN(d) || double.IsInfinity(d))) Console.WriteLine("FOUND!");
    }

    static void Benchmark(Action a)
    {
        Stopwatch watch = Stopwatch.StartNew();
        a();

        TimeSpan span = watch.Elapsed;
        Console.WriteLine("Milliseconds: " + span.TotalMilliseconds + " ms");
        Console.ReadKey();
    }
}

答案 2 :(得分:1)

  

“我的阵列可能很大,所以我不想循环使用它。”

循环访问它是访问所有元素的唯一方法。需要以这种或那种方式循环。

如果您担心性能,则不应使用LINQ,因为它会产生迭代器并委托调用开销。使用unsafe代码通过将其视为一维数组来运行该数组的所有元素。

JIT无法优化多维数组的边界检查。

像这样:

fixed (double* arrayPtr = array) {
 var count = width * height;
 for (int i = 0; i < count; i++) {
  if (double.IsNaN(arrayPtr[i]) || double.IsInfinity(arrayPtr[i]))
   return true;
 }
}
return false;

可能比LINQ解决方案快10倍。 LINQ非常间接重,这对现代CPU的性能有害。基于直线循环的代码通常要快得多。

答案 3 :(得分:0)

您建议的解决方案会对您的阵列进行两次传递。你可以这样做(Linqy):

static bool HasBadData( double[,] doubles )
{
  bool hasBadData = doubles.Cast<double>()
                           .Any( x => double.IsInfinity(x) || double.IsNaN(x) )
                           ;
  return hasBadData ;
}

或者更简单,非Linq方式:

static bool HasBadData( double[,] doubles )
{
  bool hasBadData = false ;
  for ( int i = 0 ; !hasBadData && i < doubles.GetLength(0) ; ++i )
  {
    for ( int j = 0 ; !hasBadData && j < doubles.GetLength( 1 ) ; ++j )
    {
      double d = doubles[i,j] ;
      hasBadData = double.IsInfinity(d) || double.IsNaN(d) ;
    }
  }
  return hasBadData ;
}

这是另外六个中的六个,实际上,虽然我怀疑非Linq方法会稍微好一点。