2014-07-10 6 views
8

Quindi ho chiamato un oggetto, per mancanza di una parola migliore, MatricesMatrix che è un Matrix di Matrici (tutto è memorizzato come double[,]). Voglio rimuovere tutti i valori dalle matrici interne in un'unica grande matrice. Ecco quello che ho finora:Conversione di una matrice 2D di array 2D in una singola matrice 2D

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

Si noti che ho deciso di codice il numero totale di righe e colonne della returnMatrix deve avere.

Ecco altre linee guida e casi uscita:

  • Ogni elemento del grande matrice deve essere nella stessa posizione rispetto agli altri elementi del grande matrice che provenivano dalla matrice interna MatricesMatrix che la l'elemento è venuto da.
  • Ogni "matrice" (non più in forma di matrice) all'interno della matrice grande deve essere nella stessa posizione rispetto alle altre matrici all'interno della matrice grande come era all'interno della matrice Matrici (senza sovrapposizione e 0 in eventuali spazi lasciati vuoti).

CASO 1

Dato questo ingresso: un MatricesMatrix(2,2) con [0,0] = (2x2 matrix), [0,1] = (2x3 matrix), [1,0] = (2x2 matrix), and [1,1] = (2x3 matrix). Cioè,

MatricesMatrix Input

uscita deve essere:

Output Case 2

CASO 2

Dato questo ingresso: un MatricesMatrix(2,2) con [0,0] = (1x1 matrix), [0,1] = (3x3 matrix), [1,0] = (2x2 matrix), and [1,1] = (4x4 matrix). Cioè,

Case 2 Input

uscita dovrebbe essere qualcosa di simile:

Case 2 Output

Tutta l'assistenza sarebbe molto apprezzato!

UPDATE: Ecco un test di unità per il caso 1, che deve trascorrere:

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

    } 
+0

è accettabile se si sposta 2x2 matrice superiore di due blocchi e si sposta 3x3 matrice a sinistra di 1 blocco? – Dimitri

+0

Intendi spostare il 3x3 sul blocco RIGHT 1? Sì, penso che sia una possibilità fintanto che il metodo può ancora gestire MatricesMatrix più grande rispetto all'esempio che ho dato –

+0

Non sono sicuro di quali criteri debba essere superata la soluzione. Quello che mostri è afaics non la più piccola matrice 2d che potrebbe contenere quelle più piccole senza sovrapposizioni. Quindi si potevano sistemarli allineati l'uno sotto l'altro, no? –

risposta

4

Ho iniziato con una semplice classe Matrix per contenere lo double[,] s. Niente di troppo elegante, solo un semplice array di array con un conteggio di righe e colonne e un accessorio di array.

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

tuo secondo caso sembra più difficile (e, come una migliore prova della correttezza) rispetto alla prima, così ho creato un MetaMatrix a corrispondere a quello.

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

Per comodità, ho creato una funzione Posiziona che inserisce una matrice in un'altra nella posizione specificata.

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

I numeri magici alimentati in GetLength() sono stati solo chiedendo per gli errori, così ho definite alcune costanti per loro (e ROW_DIM = 0COL_DIM = 1). Ho deciso di gestire l'imbottitura calcolando la larghezza di una colonna e l'altezza di una riga e ignorando eventuali elementi aggiuntivi dopo il Place() in sotto-matrice. Un metodo GetRowHeight() e GetColWidth() calcola i valori.

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

Una funzione Flatten() ciclo in tutti i sottomatrici, Place() li ing alla riga e colonna appropriata in una nuova matrice. Aggiorna la riga successiva e la colonna dopo ogni Place() utilizzando le funzioni GetRowHeight() e GetColWidth().

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

Un po 'di colla di provarlo ...

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

... e si ottiene un output proprio come il tuo secondo caso con tutte le matrici stranamente montaggio e 0 s nei luoghi vuoti .*

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 

* La matrice di destinazione ottiene i suoi valori inizializzato a default(double), che risulta essere 0 (il valore che si voleva). Se hai bisogno di qualcosa di diverso da default(double) per le posizioni vuote, puoi probabilmente ottenerle iterando sulla nuova matrice e scrivendo il nuovo valore predefinito ovunque prima di Flatten() nella metamatrice.

(Grazie a Jeff Mercado per aver ricordato che il metodo GetLength() array multidimensionali può essere utilizzato per trovare le loro dimensioni.)

+1

Gli array multidimensionali forniscono assolutamente dimensioni, questo è il metodo per 'GetLength (int) '. –

+0

Sembra che tu abbia ragione. Stavo cercando metodi con parole chiave come "dimensione" in loro e ho perso 'GetLength()'. Lo modificherò e vedrò se riesco a rielaborarlo per usare un 'double [,]' invece. Grazie per la correzione! – Mirinth

-1

Credo che bisogna dare elementi dell'array rowid corrispondenti e ColumnID per ottenere il rilascio di indicizzazione della matrice esterna.Supponendo che tu abbia già una conversione di matrice in oggetto Matrix;

essere non sicuro se ho comprensione passa correttamente le norme, ma qui è quello che ho realizzato finora:

ho implementato le classi Matrix e MatrixList come segue:

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

Utilizzando queste classi, ho implementato il algoritmo di seguito:

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

dove init è un metodo per riempire gli oggetti, calcolato come segue:

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

Ero su un'app fittizia di Windows Form, quindi ho utilizzato un richtextbox per testare il codice sopra.

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

e il risultato è:

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 

non ho molto tempo per dare i numeri piuttosto ad elementi di matrice per mostrare beriefly in questo momento, ma penso che si può ottenere l'idea.

+1

Grazie per la risposta, ma non sono sicuro che la tua soluzione funzioni per qualsiasi cosa eccetto il caso più semplice in cui c'è una chiara corrispondenza uno a uno tra le file di MatricesMatrix e le righe di ciascuna Matrice al suo interno. Cosa succede se una matrice all'interno di MatricesMatrix ha più di una riga? (vedi i 2 casi che ho mostrato nella domanda) –

+0

Bene, se vedi il primo ciclo per; Passa all'ultima profondità della riga, e per ciascuno degli id ​​delle righe sembra che una matrice abbia quell'id della riga. Quindi cerca qualsiasi profondità delle righe della matrice. Ti suggerisco di testare con i tuoi casi. –

+0

Inoltre, la mia classe MatricesMatrix è di tipo 'Matrix [,]', ma la tua è di tipo 'Lista '. Preferirei non dover fare più conversioni se posso aiutarlo. –

1

Penso che sarebbe utile a voi per spezzare la soluzione in quadranti che si sta cercando riempire. Questo sarà tutto presupposto che combineremo solo 4 matrici in questa configurazione 2x2. Le stesse strategie illustrate qui possono essere applicate ad altre dimensioni di matrici da combinare.

Così dato 4 matrici A, B, C e D, si cercherà di costruire una matrice risultante in questa disposizione:

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

Prima di poter iniziare, abbiamo dovrà capire le dimensioni del risultato finale. Questo dovrebbe avere un senso. Avremo una metà superiore, metà inferiore, metà sinistra e metà destra.

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 

Quindi vorremmo considerare quali regioni della matrice dei risultati vogliamo copiare ognuna delle 4 matrici. Considerando l'origine in alto a sinistra, tutto nella metà destra sarà spostato dalla dimensione della metà sinistra, tutto nella metà inferiore sarà spostato dalla dimensione della metà superiore. Le compensazioni per ciascuna delle matrici sarebbero:

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

Ora con tutte queste informazioni, si può iniziare a costruire la matrice risultato. Basta copiare i valori da ciascuna matrice al risultato, con gli offset applicati.

Quindi, in codice, farei questo:

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

Poi per dimostrare il risultato in termini di caso 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); 

Questo naturalmente può essere generalizzata a qualsiasi dimensione matrice di matrici. Il concetto è lo stesso in ogni fase del processo.

Dato m x n matrici, ci sarà una costruire una matrice risultante in questa disposizione:

+-----+-----+-----+ 
| 0,0 | ... | 0,n | 
+-----+-----+-----+ 
| ... |  | ... | 
+-----+-----+-----+ 
| m,0 | ... | m,n | 
+-----+-----+-----+ 
  1. ottenere le dimensioni di ciascuna delle fette.

    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. Ottenere gli offset per ciascuna delle matrici. Ogni porzione verticale viene spostata a sinistra dalla quantità totale di colonne nelle sezioni verticali precedenti. Ogni porzione orizzontale viene spostata verso il basso per la quantità totale di righe nelle sezioni orizzontali precedenti.

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

Così ora siamo in grado di costruire la matrice risultato.

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