8

So I have an object called, for lack of a better word, MatricesMatrix which is a Matrix of Matrices (everything is stored as a double[,]). I want to strip all of the values from the inner matrices into one big Matrix. Here is what I have so far:

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

Note that I have determined programmatically the total number of rows and columns the returnMatrix needs to have.

Here are some more guidelines and output cases:

  • Each element of the big matrix should be in the same position relative to the other elements of the big matrix that came from the Matrix inside of MatricesMatrix that the element came from.
  • Each "matrix" (no longer in matrix form) inside of the big matrix should be in the same position relative to the other matrices inside of the big matrix as it was inside of the MatricesMatrix (with no overlapping, and 0's in any spaces left empty).

CASE 1

Given this input: a MatricesMatrix(2,2) with [0,0] = (2x2 matrix), [0,1] = (2x3 matrix), [1,0] = (2x2 matrix), and [1,1] = (2x3 matrix). That is,

MatricesMatrix Input

Output must be:

Output Case 2

CASE 2

Given this input: a MatricesMatrix(2,2) with [0,0] = (1x1 matrix), [0,1] = (3x3 matrix), [1,0] = (2x2 matrix), and [1,1] = (4x4 matrix). That is,

Case 2 Input

Output should be something like:

Case 2 Output

Any assistance would be greatly appreciated!

UPDATE: Here is a unit test for Case 1 that should pass:

    [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();

    }
7
  • is this acceptable if you move 2x2 matrix upper by two blocks and move 3x3 matrix to the left by 1 block ? Commented Jul 10, 2014 at 17:56
  • Do you mean shift the 3x3 to the RIGHT 1 block? Yes, I think that is a possibility as long as the method can still handle a larger MatricesMatrix than the example I gave Commented Jul 10, 2014 at 18:18
  • I am not sure which criteria the solution must pass. What you show is afaics not the smallest 2d matrix which could hold the littler ones without overlap. So one could just arrange them left aligned below each other, couldn't one? Commented Jul 10, 2014 at 18:38
  • @PeterSchneider that might be a better way to go about it for Case 2 (although I'm still not sure how to do that). I added a simpler case (Case 1) where the matrices in each column all have the same dimensions to help clarify what output I'm after Commented Jul 10, 2014 at 19:58
  • Are you able to formulate a rule? Programming is putting rules in formal notation. No rules, no program. Commented Jul 10, 2014 at 20:01

3 Answers 3

4

I started with a simple Matrix class to hold the double[,]s. Nothing too fancy, just a simple array-of-arrays with a row and column count and array accessor.

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

Your second case looks more difficult (and like a better test of correctness) than the first, so I set up a metamatrix to match that.

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

For convenience, I made a Place function that puts one matrix into another one at the given location.

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

The magic numbers fed into GetLength() were just asking for mistakes, so I defined some constants for them (ROW_DIM = 0 and COL_DIM = 1). I decided to handle the padding by figuring out how wide a column is and how tall a row is and skipping any extra elements after Place()ing the sub-matrix in. A GetRowHeight() and GetColWidth() method figure out the values.

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

A Flatten() function loops through all the sub-matrices, Place()ing them at the appropriate row and column in a new matrix. It updates the next row and column after each Place() using the GetRowHeight() and GetColWidth() functions.

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

A little glue to test it out...

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

...and you get an output just like your second case with all the oddly fitting matrices and 0s in the empty places.*

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

*The destination matrix gets its values initialized to default(double), which happens to be 0 (the value you wanted). If you need something other than default(double) for the empty places, you can probably get them by iterating over the new matrix and writing the new default value everywhere before Flatten()ing the metamatrix.

(Thanks to Jeff Mercado for pointing out that multidimensional arrays' GetLength() method can be used to find their dimensions.)

Sign up to request clarification or add additional context in comments.

2 Comments

Multi-dimensional arrays absolutely provides dimensions, that's what the GetLength(int) method is for.
It looks like you're right. I was looking for methods with keywords like "dimension" in them and missed GetLength(). I'll edit that in and see if I can rework it to use a double[,] instead. Thanks for the correction!
1

I think it would be beneficial to you to break up the solution into the quadrants you are trying to fill. This will all be under the assumption that we will only be combining 4 matrices in this 2x2 configuration. The same strategies explined here can be applied to other dimensions of matrices to be combined.

So given 4 matrices A, B, C and D, we will try to build a resulting matrix in this arrangement:

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

Before we can start, we will need to figure out the dimensions of the final result. This should hopefully make sense. We'll have a top half, bottom half, left half and right half.

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

Then we will want to consider which regions of the result matrix we want to copy each of the 4 matrices. Considering the origin at the top-left, everything on the right half will be shifted over by the size of the left half, everything on the bottom half will be shifted over by the size of the top half. The offsets for each of the matrices would be:

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

Now with all this information, we can start building up the result matrix. Just copy over the values from each matrix to the result, with the offsets applied.

So in code, I would do this:

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

Then to demonstrate the result in terms of case 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);

This of course can be generalized to any size matrix of matrices. The concept is the same in every step of the process.

Given m x n matrices, we will try to build a resulting matrix in this arrangement:

+-----+-----+-----+
| 0,0 | ... | 0,n |
+-----+-----+-----+
| ... |     | ... |
+-----+-----+-----+
| m,0 | ... | m,n |
+-----+-----+-----+
  1. Get the dimensions of each of the slices.

    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. Get the offsets for each of the matrices. Each vertical slice is offset to the left by the total amount of columns in the previous vertical slices. Each horizontal slice is offset to the down by the total amount of rows in the previous horizontal slices.

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

So now we can build up the result matrix.

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

Comments

-1

I think you have to give array elements corresponding rowid and columnid to achieve the indexing issue of the outer matrix. Assuming you already have a Array to Matrix object conversion;

Being not sure if I undestood the rules correctly, but here is what I implemented so far:

I implemented Matrix and MatrixList classes as follows:

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

Using those classes, I implemented the algorithm below:

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

where init is a method to fill the objects, implemented as follows:

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

I was on a windows forms dummy app, so used a richtextbox to test the code above.

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

and the result is:

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 

I don't have much time to give pretty numbers to array items to show beriefly at the moment, but I think you can get the idea.

3 Comments

Thank you for your response, but I'm not sure your solution works for anything but the simplest case in which there is a clear one-to-one correspondence between the rows of the MatricesMatrix and the rows of each Matrix inside of it. What if a matrix inside of the MatricesMatrix has more than one row? (see the 2 Cases I showed in the question)
Well, if you see the first for loop; It loops to the very last row depth, and for each of the row ids it looks whether a matrix has that row id. So it does search for any depth of matrix rows. I suggest you to test with your cases.
Also, my MatricesMatrix Class is of type Matrix[,], but yours is of type List<Matrix>. I would rather not have to do multiple conversions if I can help it

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.