如何使用具有二维数组的LINQ

我有一个二维字节数组,看起来像这样:

0 0 0 0 1

1 1 1 1 0

0 0 1 1 1

1 0 1 0 1

数组中的每个值只能是0或1.上面的简化示例显示了4行,每行有5列。 我试图找出如何使用LINQ将索引返回到具有最大数量的1s的行,在上面的示例中应该返回1。

以下非LINQ C#代码解决了这个问题:

static int GetMaxIndex(byte[,] TwoDArray) { // This method finds the row with the greatest number of 1s set. // int NumRows = TwoDArray.GetLength(0); int NumCols = TwoDArray.GetLength(1); int RowCount, MaxRowCount = 0, MaxRowIndex = 0; // for (int LoopR = 0; LoopR < NumRows; LoopR++) { RowCount = 0; for (int LoopC = 0; LoopC  MaxRowCount) { MaxRowCount = RowCount; MaxRowIndex = LoopR; } } return MaxRowIndex; } static void Main() { byte[,] Array2D = new byte[4, 5] { { 0, 0, 0, 0, 1 }, { 1, 1, 1, 1, 0 }, { 0, 0, 1, 1, 1 }, { 1, 0, 1, 0, 1 } }; int MaxInd = GetMaxIndex(Array2D); Console.WriteLine("MaxInd = {0}", MaxInd); } 

所以,我的问题是:

  1. 如何使用LINQ来解决这个问题,并且在这里使用LINQ会比使用上面的非LINQ代码效率低吗?
  2. PLINQ可以解决这个问题吗? 或者,将任务并行库(TPL)直接用于上述代码并将每行中1的数量计数拆分为单独的线程会更高效,假设每行至少有1,000列?

使用LINQ的多维数组很难处理,但是你可以这样做:

 var arr = new [,] { { 0, 0, 0, 0, 1 }, { 1, 1, 1, 1, 0 }, { 0, 0, 1, 1, 1 }, { 1, 0, 1, 0, 1 } }; var data = Enumerable.Range(0, 4) .Select( row => new { index = row, count = Enumerable.Range(0, 5).Select(col => arr[row, col]).Count(x => x == 1) }) .OrderByDescending(x => x.count) .Select(x => x.index) .First(); 

我就是这样做的。 它或多或少都和其他人一样,但是没有任何Enumerable.Range (并不是说它们有任何问题(我一直使用它们)……它只是让代码在这种情况下更加缩进)。

这个还包括PLINQ的东西。 TPL(async / await)不适用于此,因为它受计算限制,TPL更适合I / O绑定操作。 如果您使用async / await而不是PLINQ,您的代码将最终按顺序执行。 这是因为async / await在线程被释放之前不会并行(并且它可以启动下一个任务……然后可以并行执行),并且纯粹的同步函数(例如CPU内容)不会每次实际等待。 ..他们会一路奔跑。 基本上,它会在你开始下一件事之前完成列表中的第一件事,使它按顺序执行。 PLINQ显式启动并行任务,没有此问题。

 //arry is your 2d byte array (byte[,] arry) var maxIndex = arry .Cast() //cast the entire array into bytes .AsParallel() //make the transition to PLINQ (remove this to not use it) .Select((b, i) => new // create indexes { value = b, index = i }) .GroupBy(g => g.index / arry.GetLength(1)) // group it by rows .Select((g, i) => new { sum = g.Select(g2 => (int)g2.value).Sum(), //sum each row index = i }) .OrderByDescending(g => g.sum) //max by sum .Select(g => g.index) //grab the index .First(); //this should be the highest index 

在效率方面,你的for循环可能会得到更好的结果。 我要问的问题是,哪个更具可读性和清晰度?

1)你可以用LINQ这样做…

 private static int GetMaxIndex(byte[,] TwoDArray) { return Enumerable.Range(0, TwoDArray.GetLength(0)) .Select( x => new { Index = x, Count = Enumerable.Range(0, TwoDArray.GetLength(1)).Count(y => TwoDArray[x, y] == 1) }) .OrderByDescending(x => x.Count) .First() .Index; } 

…你必须测试它以查看LINQ是更快还是更慢。

2)可以使用PLINQ。 只需将ParallelEnumerable.Range用于行索引生成器即可

 private static int GetMaxIndex2(byte[,] TwoDArray) { return ParallelEnumerable.Range(0, TwoDArray.GetLength(0)) .Select( x => new { Index = x, Count = Enumerable.Range(0, TwoDArray.GetLength(1)).Count(y => TwoDArray[x, y] == 1) }) .OrderByDescending(x => x.Count) .First() .Index; } 
 // This code is extracted from // http://www.codeproject.com/Articles/170662/Using-LINQ-and-Extension-Methods-in-C-to-Sort-Vect private static IEnumerable ConvertToSingleDimension(T[,] source) { T[] arRow; for (int row = 0; row < source.GetLength(0); ++row) { arRow = new T[source.GetLength(1)]; for (int col = 0; col < source.GetLength(1); ++col) arRow[col] = source[row, col]; yield return arRow; } } // Convert byte[,] to anonymous type {int index, IEnumerable} for linq operation var result = (from item in ConvertToSingleDimension(Array2D).Select((i, index) => new {Values = i, Index = index}) orderby item.Values.Sum(i => i) descending, item.Index select item.Index).FirstOrDefault();