简体   繁体   English

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

[英]Converting jagged array to 2D array C#

I'm trying to convert this function from Jagged Array to 2D array, and I'm not able to convert everything Original Function: 我正在尝试将此功能从锯齿状数组转换为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;
}

What I have converted until now: 到现在为止我已经转换了什么

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;
}

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

This snippet can be helpful 此代码段可能会有所帮助

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.");
    } 
}

Usage: 用法:

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: For those scenarios where unsafe context is acceptable there is a faster solution, thanks Styp: https://stackoverflow.com/a/51450057/3909293 UPD:对于那些可以接受不安全上下文的情况,有一个更快的解决方案,感谢Styp: https ://stackoverflow.com/a/51450057/3909293

NOTE: your jagged array should be orthogonal, hence sub arrays lengths should all be equal, otherwise you cannot convert it to a 2D array. 注意:锯齿状数组应正交,因此子数组的长度应全部相等,否则无法将其转换为2D数组。

the part: 那个部分:

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

is just for initializing a new jagged array which can easily replaced with 仅用于初始化一个新的锯齿状数组,可以轻松地将其替换为

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

and in 和在

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

you are essentially creating an array with same length of sub array i in A so we can replace it with 您实际上是在A创建具有相同长度的子数组i数组,因此我们可以将其替换为

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

as mentioned before, All sub arrays length are equal so we can use second dimension length instead. 如前所述,所有子数组的长度都相等,因此我们可以使用第二维长度。

and the whole method would be: 整个方法将是:

    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;
    }

Diligent Key Pressers' answer is the right one if runtime is unimportant. 如果运行时不重要,则勤奋的按键答案是正确的答案。 I work a lot with 3D Arrays and I learned that copy value-by-value operations are incredibly expensive! 我在3D阵列上进行了大量工作,并且了解到按值复制操作的成本非常高! Keep that in mind! 记住这一点! Another thing is Linq is slow and preconditions are eating up times as well! 另一件事是Linq速度很慢,前提条件也正在消耗时间!

In my opinion, if time is of importance, this solution might be useful: 我认为,如果时间很重要,则此解决方案可能会有用:

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!");

        }
    }
}

The CopyBlock Helper was obtained from here: https://gist.github.com/theraot/1bfd0deb4a1aab0a27d8 可从此处获得CopyBlock Helper: https ://gist.github.com/theraot/1bfd0deb4a1aab0a27d8

I just made a Wrapper for the IL-functions: 我刚刚为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);
        }
    }
}

This results in the following results: 结果如下:

          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 |

If possible, try to use ArrayCopy, BlockCopy or IL-CopyBlock to increase conversion performance. 如果可能,请尝试使用ArrayCopy,BlockCopy或IL-CopyBlock来提高转换性能。 The value-by-value copy operation is slow and therefore not the best option to use! 逐值复制操作很慢,因此不是最佳选择! Further speedup can be found by optimizing a few things and removing the if-clause. 通过优化一些内容并删除if子句,可以进一步提高速度。 A factor of at least 2x should be achievable! 至少应达到2倍!

Just to make sure we are at the same understanding, a jagged array is an array of arrays. 为了确保我们有相同的理解,锯齿状的数组就是数组的数组。 So when you do 所以当你这样做

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

you are adding a array for each position of the first dimension's array. 您正在为第一个维度的数组的每个位置添加一个数组。

In your case (in the jagged array) A.Length represented the length of the first dimension of the array while A[i].Length represented the length of the array of the second dimension that is contained in this index (i) of the first dimension. 在您的情况下(在锯齿状数组中) A.Length表示数组的第一维的长度,而A[i].Length表示包含在数组的索引(i)中的第二维的数组的长度。第一维。 If you are using a 2D array, A.Length represents the length of both dimensions multiplied. 如果使用2D数组,则A.Length表示两个维度的长度相乘。 While with a jagged can have different length for each second dimension arrays, a 2D array must have the same length on both dimension. 尽管带有锯齿的每个第二维数组的长度可以不同,但​​是二维数组在两个维上的长度必须相同。

So, in your case you would have to get n = A.GetLength(0) (means get the length of the first dimension) and m = A.GetLength(1) (means get the length of the second dimension). 因此,在您的情况下,您将必须获得n = A.GetLength(0) (均值获得第一维的长度)和m = A.GetLength(1) (均值获得第二维的长度)。 You would then initialize 'x' double[,] x = new double[n, m]; 然后,您将初始化' double[,] x = new double[n, m]; and you would no longer need the for loop. 并且您将不再需要for循环。

Your code should then look like this : 您的代码应如下所示:

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;
}

So, if what I did is ok, this should fix your problem and answer your question. 因此,如果我做的还可以,则可以解决您的问题并回答您的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM