有谁知道检查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和无穷大做技巧。有一个更好的方法吗?感谢您提供的任何帮助或建议。
答案 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方法会稍微好一点。