【问题标题】:Converting a 2D Array of 2D Arrays into a single 2D Array将 2D 数组的 2D 数组转换为单个 2D 数组
【发布时间】:2014-08-31 22:22:43
【问题描述】:

所以我有一个对象叫做MatricesMatrix,它是矩阵的Matrix(所有内容都存储为double[,])。我想将内部矩阵中的所有值剥离到一个大矩阵中。这是我目前所拥有的:

public Matrix ConvertToMatrix()
    {
        //Figure out how big the return matrix should be
        int totalRows = this.TotalRows();
        int totalColumns = this.TotalColumns();

        Matrix returnMatrix = new Matrix(totalRows, totalColumns);

        List<object> rowElementsList = new List<object>();

        //"outer" index means an index of the MatricesMatrix
        //"inner" index means an index of a Matrix within the Matrices Matrix

        //outer row loop
        for (int outerRowIndex = 0; outerRowIndex < NumberOfRows; outerRowIndex++)
        {
            //outer column loop
            for (int outerColumnIndex = 0; outerColumnIndex < NumberOfColumns; outerColumnIndex++)
            {
                Matrix currentMatrix = GetElement(outerRowIndex, outerColumnIndex);
                object element = null;

                //inner row loop
                for (int innerRowIndex = 0; innerRowIndex < currentMatrix.NumberOfRows; innerRowIndex++)
                {
                    //inner column loop
                    for (int innerColumnIndex = 0; innerColumnIndex < currentMatrix.NumberOfColumns; innerColumnIndex++)
                    {
                       element = currentMatrix.GetElement(innerRowIndex, innerColumnIndex);                          
                    }            
                }

                returnMatrix.SetElement(outerRowIndex, outerColumnIndex, (double)element);      





            }
        }

        return returnMatrix;
    }

请注意,我已经以编程方式确定了 returnMatrix 所需的总行数和列数。

这里有一些更多的指导方针和输出案例:

  • 大矩阵的每个元素都应相对于来自该元素所在的 MatricesMatrix 内部的矩阵的大矩阵的其他元素处于相同的位置。
  • 大矩阵内的每个“矩阵”(不再以矩阵形式)相对于大矩阵内的其他矩阵应位于与矩阵矩阵内相同的位置(没有重叠,并且 0 位于任何空格留空)。

案例 1

给定这个输入:MatricesMatrix(2,2)[0,0] = (2x2 matrix), [0,1] = (2x3 matrix), [1,0] = (2x2 matrix), and [1,1] = (2x3 matrix)。也就是说,

输出必须是:

案例 2

给定这个输入:MatricesMatrix(2,2)[0,0] = (1x1 matrix), [0,1] = (3x3 matrix), [1,0] = (2x2 matrix), and [1,1] = (4x4 matrix)。也就是说,

输出应该是这样的:

任何帮助将不胜感激!

更新: 这是一个应该通过的案例 1 的单元测试:

    [TestMethod]
    public void MatricesMatrix_ConvertToMatrixTest()
    {
        Matrix m1 = new Matrix(2);
        Matrix m2 = new Matrix(2, 3);
        Matrix m3 = new Matrix(2);
        Matrix m4 = new Matrix(2, 3);

        double[] m1Row1 = { 1, 1 };
        double[] m1Row2 = { 1, 1 };

        double[] m2Row1 = { 2, 2, 2 };
        double[] m2Row2 = { 2, 2, 2 };            

        double[] m3Row1 = { 3, 3 };
        double[] m3Row2 = { 3, 3 };

        double[] m4Row1 = { 4, 4, 4 };
        double[] m4Row2 = { 4, 4, 4 };

        m1.SetRowOfMatrix(0, m1Row1);
        m1.SetRowOfMatrix(1, m1Row2);
        m2.SetRowOfMatrix(0, m2Row1);
        m2.SetRowOfMatrix(1, m2Row2); 
        m3.SetRowOfMatrix(0, m3Row1);
        m3.SetRowOfMatrix(1, m3Row2);
        m4.SetRowOfMatrix(0, m4Row1);
        m4.SetRowOfMatrix(1, m4Row2);

        MatricesMatrix testMatricesMatrix = new MatricesMatrix(2, 2);

        testMatricesMatrix.SetElement(0, 0, m1);
        testMatricesMatrix.SetElement(0, 1, m2);
        testMatricesMatrix.SetElement(1, 0, m3);
        testMatricesMatrix.SetElement(1, 1, m4);

        Matrix expectedResult = new Matrix(4, 5);

        double[] expectedRow1 = { 1, 1, 2, 2, 2 };
        double[] expectedRow2 = { 1, 1, 2, 2, 2 };
        double[] expectedRow3 = { 3, 3, 4, 4, 4 };
        double[] expectedRow4 = { 3, 3, 4, 4, 4 };

        expectedResult.SetRowOfMatrix(0, expectedRow1);
        expectedResult.SetRowOfMatrix(1, expectedRow2);
        expectedResult.SetRowOfMatrix(2, expectedRow3);
        expectedResult.SetRowOfMatrix(3, expectedRow4);

        Matrix actualResult = testMatricesMatrix.ConvertToMatrix();

        (actualResult == expectedResult).Should().BeTrue();

    }

【问题讨论】:

  • 如果您将 2x2 矩阵向上移动两个块并将 3x3 矩阵向左移动 1 个块,这是否可以接受?
  • 你的意思是把 3x3 移到 RIGHT 1 块吗?是的,我认为这是一种可能性,只要该方法仍然可以处理比我给出的示例更大的矩阵矩阵
  • 我不确定解决方案必须通过哪些标准。您展示的是 afaics 不是最小的 2d 矩阵,它可以容纳较小的矩阵而不会重叠。所以一个人可以将它们排列在彼此下方,不是吗?
  • @PeterSchneider 对于案例 2 来说,这可能是一个更好的方法(尽管我仍然不确定如何做到这一点)。我添加了一个更简单的案例(案例 1),其中每列中的矩阵都具有相同的维度,以帮助澄清我所追求的输出
  • 你能制定规则吗?编程正在以正式的符号表示规则。没有规则,没有程序。

标签: c# arrays matrix multidimensional-array


【解决方案1】:

我从一个简单的 Matrix 类开始保存double[,]s。没什么太花哨的,只是一个简单的数组数组,带有行数和列数以及数组访问器。

class Matrix<T>
{
    public int Rows { get; private set; }
    public int Cols { get; private set; }

    private T[,] mat;

    public Matrix(int rowCount, int colCount)
    {
        Rows = rowCount;
        Cols = colCount;
        mat = new T[Rows, Cols];
    }

    public T this[int r, int c]
    {
        get { return mat[r, c]; }
        set { mat[r, c] = value; }
    }
}

你的第二个案例看起来比第一个更难(并且像是一个更好的正确性测试),所以我设置了一个元矩阵来匹配它。

public static Matrix<double[,]> BuildMetaMatrix()
{
    Matrix<double[,]> m = new Matrix<double[,]>(2, 2);

    m[0, 0] = new double[,]
    {
        { 1 }
    };

    m[0, 1] = new double[,]
    {
        { 3, 3, 3 },
        { 3, 3, 3 },
        { 3, 3, 3 }
    };

    m[1, 0] = new double[,]
    {
        { 2, 2 },
        { 2, 2 }
    };

    m[1, 1] = new double[,]
    {
        {4, 4, 4, 4},
        {4, 4, 4, 4},
        {4, 4, 4, 4},
        {4, 4, 4, 4}
    };

    return m;
}

为方便起见,我创建了一个 Place 函数,将一个矩阵放入给定位置的另一个矩阵中。

static void Place(double[,] src, double[,] dest, int destR, int destC)
{
    for (int row = 0; row < src.GetLength(ROW_DIM); row++)
    {
        for (int col = 0; col < src.GetLength(COL_DIM); col++)
        {
            dest[row + destR, col + destC] = src[row, col];
        }
    }
}

输入GetLength() 的神奇数字只是要求出错,所以我为它们定义了一些常量(ROW_DIM = 0COL_DIM = 1)。我决定通过计算列的宽度和行的高度来处理填充,并在Place()输入子矩阵之后跳过任何额外的元素。GetRowHeight()GetColWidth() 方法计算出这些值。

public static int GetRowHeight(Matrix<double[,]> m, int row)
{
    int maxSeen = 0;

    for (int col = 0; col < m.Cols; col++)
    {
        if (m[row, col].GetLength(ROW_DIM) > maxSeen)
        {
            maxSeen = m[row, col].GetLength(ROW_DIM);
        }
    }

    return maxSeen;
}

public static int GetColWidth(Matrix<double[,]> m, int col)
{
    int maxSeen = 0;

    for (int row = 0; row < m.Rows; row++)
    {
        if (m[row, col].GetLength(COL_DIM) > maxSeen)
        {
            maxSeen = m[row, col].GetLength(COL_DIM);
        }
    }

    return maxSeen;
}

Flatten() 函数循环遍历所有子矩阵,Place()在新矩阵中的适当行和列中对它们进行处理。它使用GetRowHeight()GetColWidth() 函数更新每个Place() 之后的下一行和下一列。

Matrix<double> Flatten(Matrix<Matrix<double>> src)
{
    // (7, 6) == (this.TotalRows(), this.TotalColumns())
    // from your code.
    Matrix<double> dest = new Matrix<double>(7, 6);

    int nextRow = 0;
    int nextCol = 0;

    for (int row = 0; row < src.Rows; row++)
    {
        for (int col = 0; col < src.Rows; col++)
        {
            dest.Place(src[row, col], nextRow, nextCol);
            nextCol += GetColWidth(src, col);
        }
        nextRow += GetRowHeight(src, row);
        nextCol = 0;
    }

    return dest;
}

一点胶水来测试一下...

static void Main(string[] args)
{
    Matrix<double[,]> src = BuildMetaMatrix();
    double[,] dest = Flatten(src);

    Print(dest);
    Console.ReadLine();
}

static void Print(double[,] matrix)
{
    for (int row = 0; row < matrix.GetLength(ROW_DIM); row++)
    {
        for (int col = 0; col < matrix.GetLength(COL_DIM); col++)
        {
            Console.Write(matrix[row, col] + "\t");
        }
        Console.Write("\n");
    }
}

...你得到一个输出,就像你的第二种情况一样,所有奇怪的拟合矩阵和0s 在空白的地方。*

1       0       3       3       3       0
0       0       3       3       3       0
0       0       3       3       3       0
2       2       4       4       4       4
2       2       4       4       4       4
0       0       4       4       4       4
0       0       4       4       4       4

*目标矩阵的值初始化为default(double),恰好是0(你想要的值)。如果您需要 default(double) 以外的空位,则可以通过迭代新矩阵并在 Flatten() 元矩阵之前的任何地方写入新的默认值来获得它们。

(感谢 Jeff Mercado 指出多维数组的GetLength() 方法可用于查找它们的维度。)

【讨论】:

  • 多维数组绝对提供维度,这就是GetLength(int)方法的用途。
  • 看来你是对的。我一直在寻找带有“维度”等关键字的方法,但错过了GetLength()。我将对其进行编辑,看看是否可以对其进行修改以改用double[,]。谢谢指正!
【解决方案2】:

我认为将解决方案分解为您要填充的象限对您是有益的。这一切都假设我们只会在这个 2x2 配置中组合 4 个矩阵。此处说明的相同策略可以应用于要组合的矩阵的其他维度。

因此,给定 4 个矩阵 A、B、C 和 D,我们将尝试以这种排列方式构建结果矩阵:

+---+---+
| A | B |
+---+---+
| C | D |
+---+---+

在开始之前,我们需要弄清楚最终结果的维度。这应该是有道理的。我们将有上半部分、下半部分、左半部分和右半部分。

rows_top    = max(rows_A, rows_B)
rows_bottom = max(rows_C, rows_D)
rows_result = rows_top + rows_bottom

cols_left   = max(cols_A, cols_C)
cols_right  = max(cols_B, cols_D)
cols_result = cols_left + cols_right

然后我们将要考虑要复制 4 个矩阵中的每一个的结果矩阵的哪些区域。考虑到左上角的原点,右半部分的所有内容都将移动左半部分的大小,下半部分的所有内容都将移动上半部分的大小。每个矩阵的偏移量为:

offset_A = (0, 0)
offset_B = (0, cols_left)
offset_C = (rows_top, 0)
offset_D = (rows_top, cols_left)

现在有了所有这些信息,我们就可以开始构建结果矩阵了。只需将每个矩阵中的值复制到结果中,并应用偏移即可。

所以在代码中,我会这样做:

// I'm just going to use plain 2D arrays here
public T[,] Combine<T>(T[,] a, T[,] b, T[,] c, T[,] d)
{
    // get the total rows
    var rows_top    = Math.Max(a.GetLength(0), b.GetLength(0));
    var rows_bottom = Math.Max(c.GetLength(0), d.GetLength(0));
    var rows_result = rows_top + rows_bottom;

    // get the total columns
    var cols_left   = Math.Max(a.GetLength(1), c.GetLength(1));
    var cols_right  = Math.Max(b.GetLength(1), d.GetLength(1));
    var cols_result = cols_left + cols_right;

    // get the offsets
    var offset_a = Tuple.Create(0, 0);
    var offset_b = Tuple.Create(0, cols_left);
    var offset_c = Tuple.Create(rows_top, 0);
    var offset_d = Tuple.Create(rows_top, cols_left);

    // fill 'er up
    var result = new T[rows_result, cols_result];
    Fill(result, a, offset_a);
    Fill(result, b, offset_b);
    Fill(result, c, offset_c);
    Fill(result, d, offset_d);
    return result;
}

public void Fill<T>(T[,] result, T[,] source, Tuple<int, int> offset)
{
    for (var i = 0; i < source.GetLength(0); i++)
        for (var j = 0; j < source.GetLength(1); j++)
            result[offset.Item1 + i, offset.Item2 + j] = source[i, j];
}

然后用案例2来证明结果:

const string A = "A", B = "B", C = "C", D = "D";
var a = new string[1,1]
{
    { A },
};
var b = new string[3, 3]
{
    { B, B, B },
    { B, B, B },
    { B, B, B },
};
var c = new string[2, 2]
{
    { C, C },
    { C, C },
};
var d = new string[4, 4]
{
    { D, D, D, D },
    { D, D, D, D },
    { D, D, D, D },
    { D, D, D, D },
};
var result = Combine(a, b, c, d);

这当然可以推广到任何大小的矩阵矩阵。流程的每一步都是相同的概念。

给定 m x n 个矩阵,我们将尝试以这种排列方式构建一个结果矩阵:

+-----+-----+-----+
| 0,0 | ... | 0,n |
+-----+-----+-----+
| ... |     | ... |
+-----+-----+-----+
| m,0 | ... | m,n |
+-----+-----+-----+
  1. 获取每个切片的尺寸。

    rows_0 = max(rows_0_0, ..., rows_0_n)
    ...
    rows_m = max(rows_m_0, ..., rows_m_n)
    rows_result = sum(rows_0, ..., rows_m)
    
    cols_0 = max(cols_0_0, ..., cols_m_0)
    ...
    cols_n = max(cols_0_n, ..., cols_m_n)
    cols_result = sum(cols_0, ..., cols_m)
    
  2. 获取每个矩阵的偏移量。每个垂直切片向左偏移前一个垂直切片中的列总数。每个水平切片向下偏移前一个水平切片中的总行数。

    offset_0_0 = (0, 0)
    ...
    offset_m_n = (sum(rows_0, ..., rows_m-1), sum(cols_0, ..., cols_n-1))
    

所以现在我们可以构建结果矩阵了。

public T[,] Combine<T>(T[,][,] m)
{
    // get the rows
    var rows = GetSliceRows(m);
    var rows_result = rows.Sum();

    // get the cols
    var cols = GetSliceCols(m);
    var cols_result = cols.Sum();

    // get the offsets
    var offsets = GetOffsets(rows, cols);

    // fill 'er up
    var result = new T[rows_result, cols_result];
    Fill(result, m, offsets);
    return result;
}

public int[] GetSliceRows<T>(T[,][,] m)
{
    var sliceRows = new int[m.GetLength(0)];
    var segments = m.GetLength(1);
    for (var i = 0; i < sliceRows.Length; i++)
    {
        sliceRows[i] = Enumerable.Range(0, segments)
            .Select(j => m[i, j].GetLength(0))
            .Max();
    }
    return sliceRows;
}

public int[] GetSliceCols<T>(T[,][,] m)
{
    var sliceCols = new int[m.GetLength(1)];
    var segments = m.GetLength(0);
    for (var j = 0; j < sliceCols.Length; j++)
    {
        sliceCols[j] = Enumerable.Range(0, segments)
            .Select(i => m[i, j].GetLength(1))
            .Max();
    }
    return sliceCols;
}

public Tuple<int, int>[,] GetOffsets(int[] rows, int[] cols)
{
    var offsets = new Tuple<int, int>[rows.Length, cols.Length];
    for (var i = 0; i < rows.Length; i++)
        for (var j = 0; j < cols.Length; j++)
            offsets[i, j] = Tuple.Create(
                rows.Take(i).Sum(),
                cols.Take(j).Sum()
            );
    return offsets;
}

public void Fill<T>(T[,] result, T[,][,] m, Tuple<int, int>[,] offsets)
{
    for (var i = 0; i < m.GetLength(0); i++)
        for (var j = 0; j < m.GetLength(1); j++)
            Fill(result, m[i, j], offsets[i, j]);
}

public void Fill<T>(T[,] result, T[,] source, Tuple<int, int> offset)
{
    for (var i = 0; i < source.GetLength(0); i++)
        for (var j = 0; j < source.GetLength(1); j++)
            result[offset.Item1 + i, offset.Item2 + j] = source[i, j];
}

【讨论】:

    【解决方案3】:

    我认为你必须给数组元素对应的rowid和columnid来实现外矩阵的索引问题。假设你已经有一个 Array 到 Matrix 对象的转换;

    不确定我是否正确地理解了规则,但这是我目前实施的:

    我实现了 Matrix 和 MatrixList 类如下:

        public class Matrix
        {
            public int row { get; set; }
            public int column { get; set; }
            public double value { get; set; }
        }
    
        public class MatrixList
        {
            public List<Matrix> matrixList = new List<Matrix>();
        }
    

    使用这些类,我实现了以下算法:

            List<MatrixList> matricesMatrix = new List<MatrixList>();
            init(matricesMatrix);
    
            int totalRows = 10;//as you stated, this is already known
            int totalColumns = 10;//as you stated, this is already known
    
    
            List<Matrix> ResultMatrix = new List<Matrix>();
    
            foreach (MatrixList matrixListItem in matricesMatrix)
            {
                for (int i = 0; i < totalRows; i++)
                {
                    List<Matrix> matrixItemList = matrixListItem.matrixList.FindAll(s => s.row == i);
    
                    foreach(Matrix matrixItem in matrixItemList)
    
                    for (int j = 0; j < totalColumns; j++)
                    {
                        if (matrixItem.column == j)
                            ResultMatrix.Add(new Matrix { row = i, column = j, value = matrixItem.value });
                    }
                }              
            }
    

    其中init是填充对象的方法,实现如下:

        private void init(List<MatrixList> matricesMatrix)
        {
            MatrixList ml = new MatrixList();
            for (int i = 0; i < 10; i++)
            {
                for (int j = 0; j < 10; j++)
                {
                    ml.matrixList.Add(new Matrix { row = i, column = j, value = i + j });
                }
            }
            matricesMatrix.Add(ml);
        }
    

    我在一个windows forms dummy app上,所以用一个richtextbox来测试上面的代码。

            for (int i = 0; i < totalRows; i++)
            {
                foreach (Matrix item in ResultMatrix)
                {
                    if (item.row == i)
                    {
                        for (int j = 0; j < totalColumns; j++)
                            if (item.column == j)
                                richTextBox1.Text += item.value + " ";
                    }
                }
                richTextBox1.Text += Environment.NewLine;
            }
    

    结果是:

    0 1 2 3 4 5 6 7 8 9 
    
    1 2 3 4 5 6 7 8 9 10 
    
    2 3 4 5 6 7 8 9 10 11 
    
    3 4 5 6 7 8 9 10 11 12 
    
    4 5 6 7 8 9 10 11 12 13 
    
    5 6 7 8 9 10 11 12 13 14 
    
    6 7 8 9 10 11 12 13 14 15 
    
    7 8 9 10 11 12 13 14 15 16 
    
    8 9 10 11 12 13 14 15 16 17 
    
    9 10 11 12 13 14 15 16 17 18 
    

    目前我没有太多时间给数组项目提供漂亮的数字来简单地展示,但我认为你可以理解。

    【讨论】:

    • 感谢您的回复,但我不确定您的解决方案是否适用于最简单的情况,即 MatricesMatrix 的行与行之间存在明确的一对一对应关系里面的每个矩阵。如果 MatricesMatrix 内的矩阵有多于一行怎么办? (请参阅我在问题中展示的 2 个案例)
    • 好吧,如果你看到第一个 for 循环;它循环到最后一行深度,并且对于每个行 ID,它会查看矩阵是否具有该行 ID。所以它确实搜索矩阵行的任何深度。我建议你用你的案例进行测试。
    • 另外,我的 MatricesMatrix 类的类型是 Matrix[,],但你的类型是 List&lt;Matrix&gt;。如果可以的话,我宁愿不必进行多次转换
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-11
    • 2013-08-21
    • 2020-02-27
    • 1970-01-01
    • 1970-01-01
    • 2017-04-04
    相关资源
    最近更新 更多