J’ai un tableau d’octets en 2 dimensions qui ressemble à ceci:
0 0 0 0 1
1 1 1 1 0
0 0 1 1 1
- Comment dessiner un anneau en utilisant GDI +?
- Référencement du contrôle nested
- Le fichier journal n’est pas écrit depuis un HttpHandler
- Entity Framework List Contient dans lambda
- Comment enregistrer BitmapImage WinRT
1 0 1 0 1
Chaque valeur du tableau ne peut être que 0 ou 1. L’exemple simplifié ci-dessus montre 4 lignes, chaque ligne contenant 5 colonnes. J’essaie de comprendre comment utiliser LINQ pour renvoyer l’index à la ligne contenant le plus grand nombre de 1, ce qui dans l’exemple ci-dessus devrait renvoyer 1.
Le code non LINQ C # suivant résout le problème:
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); }
Donc, mes questions sont:
Il est difficile de travailler avec des tableaux multidimensionnels avec LINQ, mais voici comment procéder:
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();
Voici comment je le ferais. C’est la même chose que les autres plus ou moins, mais sans aucun Enumerable.Range
(pas qu’il n’y a rien qui cloche avec ceux-là (je les utilise tout le temps) … cela rend simplement le code plus indenté dans ce cas).
Celui-ci comprend également des éléments PLINQ. La TPL (asynchrone / wait) ne conviendrait pas à cela, car elle est liée au calcul et que la TPL convient mieux aux opérations liées aux E / S. Votre code finirait par s’exécuter de manière séquentielle si vous utilisiez async / wait plutôt que PLINQ. Cela est dû au fait que async / wait ne sera pas parallèle tant que le thread ne sera pas libéré (et il pourra démarrer la tâche suivante … qui pourrait ensuite devenir parallèle) et des fonctions purement synchrones (telles que les commandes de processeur) ne seront pas toutes réellement attendues. ..ils vont juste courir tout au long. Fondamentalement, il terminerait la première chose de votre liste avant même de commencer la chose suivante, la rendant exécutée séquentiellement. PLINQ démarre explicitement les tâches parallèles et n’a pas ce problème.
//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
En termes d’efficacité, vous obtiendrez probablement de meilleurs résultats avec votre boucle. La question que je poserais est, qui est plus lisible et plus clair?
1) Vous pouvez le faire avec LINQ de cette façon …
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; }
… vous devriez le tester pour voir si LINQ est plus rapide ou plus lent.
2) Il est possible d’utiliser PLINQ. Il suffit d’utiliser ParallelEnumerable.Range
pour le générateur d’index de ligne
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();