C#中将矩阵转换为数组

5

如何将类似于下面的平方矩阵转换为最有效的方式:

  1 2 3 
  4 5 6
  7 8 9 

转换为

[1 2 3 4 5 6 7 8 9]

在C#中,

我正在进行的是

int[,] array2D = new int[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
int[] array1D = new int[9];
int ci=0;

 for (int i = 0; i < 3; i++)
 {
      for (int j = 0; j < 3; j++)
      {
            array1D[ci++] = array2D[i, j]);
      }
 }

2
这似乎是相同的问题,已经有答案了。 - Oleksi
1
@Olexsi 这两个问题并不完全相同;这个问题使用了一个正确的二维数组,而你链接的那个问题是关于一个二维锯齿形数组的。 - phoog
4个回答

8
LINQ使这个变得微不足道。
int[,] array2d = ...;
var array1d = array2d.Cast<int>().ToArray();

否则,你的方法是足够的,但可以泛化一下:

int[,] array2d = ...;
var rows = array2d.GetLength(0);
var cols = array2d.GetLength(1);
var array1d = new int[rows * cols];
var current = 0;
for (int i = 0; i < rows; i++)
{
    for (int j = 0; j < cols; j++)
    {
        array1d[current++] = array2d[i, j];
    }
}

甚至可以这样说:
int[,] array2d = ...;
var array1d = new int[array2d.GetLength(0) * array2d.GetLength(1)];
var current = 0;
foreach (var value in array2d)
{
    array1d[current++] = value;
}

哪一个更快且消耗资源更少? - kbvishnu
我不知道,我自己没有进行过分析。但我会猜测第三个版本是最快的。 - Jeff Mercado
2
似乎第二个版本实际上是最快的,第三个版本在大约120毫秒内非常接近。在我的测试中,第一个版本最慢,比最快的版本慢了55倍。进行一百万次迭代,将一个10x10矩阵转换所需的时间为:27349ms(75675449 ticks)491ms(1359839 ticks)614ms(1700921 ticks) - Jeff Mercado
@phoog:但是多维数组仅实现非泛型的 IEnumerable 接口。进行强制转换才能在其中使用LINQ。 - Jeff Mercado
啊哈,我不知道那个。谢谢你提供的信息。这也解释了因为装箱而导致性能较差的问题。 - phoog
显示剩余2条评论

1
正如Jeff所说,LINQ使这变得微不足道。OfType<>()通常比Cast<>快一点:
array1D = array2D.OfType<int>().ToArray();

OfType<> 的实现仍然会受到装箱/拆箱的惩罚,正如 @phoog 所提到的。

只是为了好玩,如果你想要一个快速的基于 LINQ 的解决方案(避免装箱的成本),你可以使用这个小的扩展方法:

static class LinqEx
{
    public static IEnumerable<T> Flatten<T>(this T[,] matrix)
    {
        foreach (var item in matrix) yield return item;
    }
}

或者基于Jeff的第二个解决方案:

    public static IEnumerable<T> Flatten<T>(this T[,] matrix)
    {
        var rows = matrix.GetLength(0);
        var cols = matrix.GetLength(1);
        for (var i = 0; i < rows;i++ )
        {
            for (var j = 0; j < cols; j++ )
                yield return matrix[i, j];
        }
    }

用法:

 int[,] array2D = new int[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };  
 int[] array1D = array2D.Flatten().ToArray();

我没有完全对此进行分析,但我预计这将比基于LINQ/IEnumerable的内置选项提供更好的性能。不过,在Jeff的第二个解决方案中似乎始终是最快的。


1

使用Buffer.BlockCopy的备选解决方案:

int[,] array2D = new int[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
int[] array1D = new int[ array2D.Length ];
Buffer.BlockCopy(array2D, 0, array1D, 0, array1D.Length * sizeof(int));

0

最好一次性分配完整的结果数组,然后再复制数据。

您应该像这样找到总大小;

var size = arrays.Sum(a=> a.Length);
var result = new int[size];

然后使用Array.CopyTo方法复制数组,而不是手动循环遍历。

var cursor = 0;
foreach(var a in arrays) {
   a.CopyTo(result, cursor);
   cursor += a.Length;    
}

Array.CopyTo比你自己写的循环要快,至少不会更慢。它可能会在内部使用C的memcpy函数来执行低级块复制。这是你能做到的最高效的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接