将锯齿状数组转换为2D数组C#

时间:2014-10-10 03:46:10

标签: c# arrays jagged-arrays

我正在尝试将此函数从Jagged Array转换为2D数组,但我无法转换所有内容 原始功能:

public static double[][] InvertMatrix(double[][] A)
{
    int n = A.Length;
    //e will represent each column in the identity matrix
    double[] e;
    //x will hold the inverse matrix to be returned
    double[][] x = new double[n][];
    for (int i = 0; i < n; i++)
    {
        x[i] = new double[A[i].Length];
    }
    /*
    * solve will contain the vector solution for the LUP decomposition as we solve
    * for each vector of x.  We will combine the solutions into the double[][] array x.
    * */
    double[] solve;

    //Get the LU matrix and P matrix (as an array)
    Tuple<double[][], int[]> results = LUPDecomposition(A);

    double[][] LU = results.Item1;
    int[] P = results.Item2;

    /*
    * Solve AX = e for each column ei of the identity matrix using LUP decomposition
    * */
    for (int i = 0; i < n; i++)
    {
        e = new double[A[i].Length];
        e[i] = 1;
        solve = LUPSolve(LU, P, e);
        for (int j = 0; j < solve.Length; j++)
        {
            x[j][i] = solve[j];
        }
    }
    return x;
}

到目前为止我已转换的内容:

public static double[,] InvertMatrix(double[,] A)
{
    int n = A.Length;
    //e will represent each column in the identity matrix
    double[] e;
    //x will hold the inverse matrix to be returned
    double[,] x = new double[n][];
    for (int i = 0; i < n; i++)
    {
        //how to convert this line?
        x[i] = new double[A[i].Length];
    }
    /*
    * solve will contain the vector solution for the LUP decomposition as we solve
    * for each vector of x.  We will combine the solutions into the double[][] array x.
    * */
    double[] solve;

    //Get the LU matrix and P matrix (as an array)
    Tuple<double[,], int[]> results = LUPDecomposition(A);

    double[,] LU = results.Item1;
    int[] P = results.Item2;

    /*
    * Solve AX = e for each column ei of the identity matrix using LUP decomposition
    * */
    for (int i = 0; i < n; i++)
    {
        //This one too?!
        e = new double[A[i].Length];
        e[i] = 1;
        solve = LUPSolve(LU, P, e);
        for (int j = 0; j < solve.Length; j++)
        {
            x[j,i] = solve[i,j];
        }
    }
    return x;
}

如何将x [i] = new double [A [i] .Length]转换为2D数组?

4 个答案:

答案 0 :(得分:16)

此代码段可能会有所帮助

static T[,] To2D<T>(T[][] source)
{
    try
    {
        int FirstDim = source.Length;
        int SecondDim = source.GroupBy(row => row.Length).Single().Key; // throws InvalidOperationException if source is not rectangular

        var result = new T[FirstDim, SecondDim];
        for (int i = 0; i < FirstDim; ++i)
            for (int j = 0; j < SecondDim; ++j)
                result[i, j] = source[i][j];

        return result;
    }
    catch (InvalidOperationException)
    {
        throw new InvalidOperationException("The given jagged array is not rectangular.");
    } 
}

用法:

double[][] array = { new double[] { 52, 76, 65 }, new double[] { 98, 87, 93 }, new double[] { 43, 77, 62 }, new double[] { 72, 73, 74 } };
double[,] D2 = To2D(array);

UPD:对于可接受不安全上下文的情况,有一个更快的解决方案,感谢Styp:https://stackoverflow.com/a/51450057/3909293

答案 1 :(得分:3)

注意:您的锯齿状数组应该是正交的,因此子数组长度应该都相等,否则您无法将其转换为2D数组。

部分:

double[,] x = new double[n][];
for (int i = 0; i < n; i++)
{
    //how to convert this line?
    x[i] = new double[A[i].Length];
}

仅用于初始化一个新的锯齿状数组,可以轻松替换为

double[,] x = new double[A.GetLength(0),A.GetLength(1)];

并在

   //This one too?!
    e = new double[A[i].Length];

你实际上是在i中创建一个具有相同长度的子数组A的数组,所以我们可以用

替换它
    e = new double[A.GetLength(1)]; //NOTE: second dimension

如前所述,所有子数组的长度都相等,所以我们可以使用第二维长度。

,整个方法是:

    public static double[,] InvertMatrix2D(double[,] A)
    {
        int n = A.Length;
        //e will represent each column in the identity matrix
        double[] e;
        //x will hold the inverse matrix to be returned
        double[,] x = new double[A.GetLength(0),A.GetLength(1)];

        /*
        * solve will contain the vector solution for the LUP decomposition as we solve
        * for each vector of x.  We will combine the solutions into the double[][] array x.
        * */
        double[] solve;

        //Get the LU matrix and P matrix (as an array)
        Tuple<double[,], int[]> results = LUPDecomposition(A);

        double[,] LU = results.Item1;
        int[] P = results.Item2;

        /*
        * Solve AX = e for each column ei of the identity matrix using LUP decomposition
        * */
        for (int i = 0; i < n; i++)
        {
            e = new double[A.GetLength(1)]; //NOTE: second dimension
            e[i] = 1;
            solve = LUPSolve(LU, P, e);
            for (int j = 0; j < solve.Length; j++)
            {
                x[j,i] = solve[j];
            }
        }
        return x;
    }

答案 2 :(得分:3)

如果运行时不重要,那么

Diligent Key Pressers的答案就是正确的答案。我在3D阵列上进行了大量工作,并且了解到按值复制操作的成本非常高!记住这一点!另一件事是Linq速度很慢,前提条件也在消耗时间!

我认为,如果时间很重要,此解决方案可能会有用:

using System;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

namespace ArrayConverter {
   public class Benchmark {
        [Params(10, 100, 1000, 10000)] 
        public int size;

        public double[][] data;

        [GlobalSetup]
        public void Setup() {
            var rnd = new Random();

            data = new double[size][];
            for (var i = 0; i < size; i++) {
                data[i] = new double[size];
                for (var j = 0; j < size; j++) {
                    data[i][j] = rnd.NextDouble();
                }
            }
        }

        [Benchmark]
        public void ComputeTo2D() {
            var output = To2D(data);
        }

       [Benchmark]
       public void ComputeTo2DFast() {
           var output = To2DFast(data);
       }

       public static T[,] To2DFast<T>(T[][] source) where T : unmanaged{
           var dataOut = new T[source.Length, source.Length];
           var assertLength = source[0].Length;

           unsafe {
               for (var i=0; i<source.Length; i++){
                   if (source[i].Length != assertLength) {
                       throw new InvalidOperationException("The given jagged array is not rectangular.");
                   }

                   fixed (T* pDataIn = source[i]) {
                       fixed (T* pDataOut = &dataOut[i,0]) {
                           CopyBlockHelper.SmartCopy<T>(pDataOut, pDataIn, assertLength);
                       }
                   }
               }
           }

           return dataOut;
       }

        public static T[,] To2D<T>(T[][] source) {
            try {
                var FirstDim = source.Length;
                var SecondDim =
                    source.GroupBy(row => row.Length).Single()
                        .Key; // throws InvalidOperationException if source is not rectangular

                var result = new T[FirstDim, SecondDim];
                for (var i = 0; i < FirstDim; ++i)
                for (var j = 0; j < SecondDim; ++j)
                    result[i, j] = source[i][j];

                return result;
            }
            catch (InvalidOperationException) {
                throw new InvalidOperationException("The given jagged array is not rectangular.");
            }
        }
    }

    public class Programm {
        public static void Main(string[] args) {
            BenchmarkRunner.Run<Benchmark>();
//            var rnd = new Random();
//            
//            var size = 100;
//            var data = new double[size][];
//            for (var i = 0; i < size; i++) {
//                data[i] = new double[size];
//                for (var j = 0; j < size; j++) {
//                    data[i][j] = rnd.NextDouble();
//                }
//            }
//
//            var outSafe = Benchmark.To2D(data);
//            var outFast = Benchmark.To2DFast(data);
//
//            for (var i = 0; i < outSafe.GetLength(0); i++) {
//                for (var j = 0; j < outSafe.GetLength(1); j++) {
//                    if (outSafe[i, j] != outFast[i, j]) {
//                        Console.WriteLine("Error at: {0}, {1}", i, j);
//                    }
//                }
//            }
//
//            Console.WriteLine("All Good!");

        }
    }
}

CopyBlock Helper是从这里获得的:https://gist.github.com/theraot/1bfd0deb4a1aab0a27d8

我刚刚为IL功能制作了一个包装器:

using System;
using System.Reflection.Emit;

namespace ArrayConverter {

    // Inspired by:
    // http://xoofx.com/blog/2010/10/23/high-performance-memcpy-gotchas-in-c/
    public class CopyBlockHelper {

        private const int BlockSize = 16384;

        private static readonly CopyBlockDelegate CpBlock = GenerateCopyBlock();

        private unsafe delegate void CopyBlockDelegate(void* des, void* src, uint bytes);

        private static unsafe void CopyBlock(void* dest, void* src, uint count) {
            var local = CpBlock;
            local(dest, src, count);
        }

        static CopyBlockDelegate GenerateCopyBlock() {
            // Don't ask...
            var method = new DynamicMethod("CopyBlockIL", typeof(void),
                new[] {typeof(void*), typeof(void*), typeof(uint)}, typeof(CopyBlockHelper));
            var emitter = method.GetILGenerator();
            // emit IL
            emitter.Emit(OpCodes.Ldarg_0);
            emitter.Emit(OpCodes.Ldarg_1);
            emitter.Emit(OpCodes.Ldarg_2);
            emitter.Emit(OpCodes.Cpblk);
            emitter.Emit(OpCodes.Ret);

            // compile to delegate
            return (CopyBlockDelegate) method.CreateDelegate(typeof(CopyBlockDelegate));
        }

        public static unsafe void SmartCopy<T>(T* pointerDataOutCurrent, T* pointerDataIn, int length) where T : unmanaged {
            var sizeOfType = sizeof(T);

            var numberOfBytesInBlock = Convert.ToUInt32(sizeOfType * length);

            var numOfIterations = numberOfBytesInBlock / BlockSize;
            var overheadOfLastIteration = numberOfBytesInBlock % BlockSize;

            uint offset;
            for (var idx = 0u; idx < numOfIterations; idx++) {
                offset = idx * BlockSize;
                CopyBlock(pointerDataOutCurrent + offset / sizeOfType, pointerDataIn + offset / sizeOfType, BlockSize);
            }

            offset = numOfIterations * BlockSize;
            CopyBlock(pointerDataOutCurrent + offset / sizeOfType, pointerDataIn + offset / sizeOfType, overheadOfLastIteration);
        }
    }
}

这将导致以下结果:

          Method |  size |             Mean |            Error |           StdDev |
---------------- |------ |-----------------:|-----------------:|-----------------:|
     ComputeTo2D |    10 |         972.2 ns |        18.981 ns |        17.755 ns |
 ComputeTo2DFast |    10 |         233.1 ns |         6.672 ns |         6.852 ns |
     ComputeTo2D |   100 |      21,082.5 ns |       278.679 ns |       247.042 ns |
 ComputeTo2DFast |   100 |       6,100.2 ns |        66.566 ns |        62.266 ns |
     ComputeTo2D |  1000 |   2,481,061.0 ns |    13,724.850 ns |    12,166.721 ns |
 ComputeTo2DFast |  1000 |   1,939,575.1 ns |    18,519.845 ns |    16,417.358 ns |
     ComputeTo2D | 10000 | 340,687,083.2 ns | 1,671,837.229 ns | 1,563,837.429 ns |
 ComputeTo2DFast | 10000 | 279,996,210.4 ns |   955,032.923 ns |   745,626.822 ns |

如果可能,请尝试使用ArrayCopy,BlockCopy或IL-CopyBlock来提高转换性能。逐值复制操作很慢,因此不是最佳选择!通过优化一些内容并删除if子句,可以进一步提高速度。至少应该达到2倍!

答案 3 :(得分:0)

为了确保我们的理解相同,锯齿状数组是一个数组数组。所以当你这样做时

for (int i = 0; i < n; i++)
{
    //how to convert this line?
    x[i] = new double[A[i].Length];
}

您要为第一个维度的数组的每个位置添加一个数组。

在您的情况下(在锯齿状数组中)A.Length表示数组第一维的长度,而A[i].Length表示此索引中包含的第二维数组的长度( i)第一维度。如果您使用的是2D数组,A.Length表示两个维度的长度相乘。虽然锯齿状每个第二维数组的长度可以不同,但​​2D数组​​在两个维度上的长度必须相同。

因此,在您的情况下,您必须获得n = A.GetLength(0)(表示获取第一维的长度)和m = A.GetLength(1)(表示获取第二维的长度)。然后,您将初始化“x”double[,] x = new double[n, m];,您将不再需要for循环。

您的代码应如下所示:

public static double[,] InvertMatrix(double[,] A)
{
    int n = A.Length;
    //e will represent each column in the identity matrix
    double[] e;
    //x will hold the inverse matrix to be returned
    double[,] x = new double[n, m];

    /*
    * solve will contain the vector solution for the LUP decomposition as we solve
    * for each vector of x.  We will combine the solutions into the double[][] array x.
    * */
    double[] solve;

    //Get the LU matrix and P matrix (as an array)
    Tuple<double[,], int[]> results = LUPDecomposition(A);

    double[,] LU = results.Item1;
    int[] P = results.Item2;

    /*
    * Solve AX = e for each column ei of the identity matrix using LUP decomposition
    * */
    for (int i = 0; i < n; i++)
    {
        //This one too?! /// this one would become 
        e = new double[m];
        e[i] = 1;
        solve = LUPSolve(LU, P, e);
        for (int j = 0; j < solve.Length; j++)
        {
            x[j,i] = solve[i,j];
        }
    }
    return x;
}

所以,如果我做的不错,这应该可以解决你的问题并回答你的问题。