7
\$\begingroup\$

I implemented an utility method that is able to view an Array[n, m] in another smaller dimension [k, l], where n%k = 0 and m%l = 0.

Example:

              -- --     -- --    
---   ---     |1 2|     |3 4|
|1 2 3 4|     |5 6|     |7 8|
|5 6 7 8|  => -- --(a0) -- --(a1) 
|4 3 2 1|     -- --     -- --        
|8 7 6 5|     |4 3|     |2 1|
---   ---     |8 7|     |6 5|
              -- --(a2) -- --(a3)
public static IEnumerable<T[,]> ViewAsNByM<T>(this T[,] values, int n, int m)
{
    var count = values.GetLength(0)*values.GetLength(1);
    for (int i = 0; i < count/(n*m); ++i)
    {
        var matrix = new T[n, m];
        for (int j = 0; j < n; ++j)
        {
            for (int k = 0; k < m; ++k)
            {
                matrix[j, k] = values[j + (i / m * n), (k + i*m)% values.GetLength(0)];
            }
        }
        yield return matrix;
    }
}

Nunit test:

[Test]
public void TestViewAsNbyM()
{
    var numbers = new[,]
    {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {4, 3, 2, 1},
        {8, 7, 6, 5}
    };

    var aux = numbers.ViewAsNByM(2, 2).ToList();
    Assert.AreEqual(4, aux.Count);

    var a0 = new[,] {{1, 2}, {5, 6}};
    var a1 = new[,] {{3, 4}, {7, 8}};
    var a2 = new[,] { { 4, 3 }, { 8, 7 } };
    var a3 = new[,] { { 2, 1 }, { 6, 5 } };

    Assert.That(aux, Is.EquivalentTo(new[] {a0,a1,a2,a3}));
}

Any thoughts about the implementation?

\$\endgroup\$
  • 1
    \$\begingroup\$ I don't have any suggestions (right now) but I'm curious what is it for? ;-P \$\endgroup\$ – t3chb0t Aug 20 '16 at 9:55
  • 1
    \$\begingroup\$ @t3chb0t I think I will be using it for a sudoku game. To view the whole board as 3,3 groups \$\endgroup\$ – Bruno Costa Aug 20 '16 at 9:56
  • 1
    \$\begingroup\$ there is no validation in method you mentioned before n%k = 0 and m%l = 0 \$\endgroup\$ – Disappointed Aug 20 '16 at 11:34
3
+50
\$\begingroup\$
  • Your solution seems to work fine fine for square matrix(where n = m). However, when presented with non-square matrix, the result becomes purely sporadic. This may or may not be problematic, depending on which kind of matrix you have to deal with.
  • Modulo % is quite expensive.
  • You are copying the array item by item. It is not the best way to it, especially when you are copying a continuous section within an array (where m > 1)
  • You should cache the result of values.GetLength(0), and more importantly values[0].GetLength(0), as they are expensive.

public static IEnumerable<T[,]> ViewAsNByM2<T>(this T[,] values, int n, int m)
{
    int height = values.GetLength(0), width = values.GetLength(1);
    int rows = height / n, columns = width / m;

    for (int row = 0; row < rows; row++)
    {
        for (int column = 0; column < columns; column++)
        {
            var matrix = new T[n, m];
            for (int i = 0; i < n; i++)
            {
                Array.ConstrainedCopy(
                    values, (row * n + i) * width + column * m,
                    matrix, i * m,
                    m);
            }
            yield return matrix;
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ Array.ConstrainedCopy is a nice tip. It was also nice that you could avoid the modulo. \$\endgroup\$ – Bruno Costa Aug 24 '16 at 20:14
0
\$\begingroup\$

(k + i*m)% values.GetLength(0)

This should have been (k + i*m)% values[0].GetLength(0)

It seems my implementation is a bit stricter than stated. In matter of fact it only allows matrices where k=sqrt(n) and l=sqrt(m).

While my previous implementation was what I strictly needed to view an array of 9 by 9 as 3 by 3, the algorithm can be adjusted in order to support any divisor of (n, m), including 1. Meanwhile I also decided that I prefer using jagged arrays.

public static IEnumerable<T[][]> ViewAsNByM<T>(this T[][] values, int m, int n)
{
    var count = values.GetLength(0)*values[0].GetLength(0);
    for (int i = 0; i < count/(n*m); ++i)
    {
        var matrix = new T[m][];
        for (int j = 0; j < m; ++j)
        {
            matrix[j] = new T[n];
            for (int k = 0; k < n; ++k)
            {
                int idxRow = j + (i/(values[0].GetLength(0)/n))*m;
                int idxCol = (k + i*n)%values[0].GetLength(0);
                matrix[j][k] = values[idxRow][idxCol];
            }
        }
        yield return matrix;
    }
}

And a test

[Test]
public void TestViewAsNbyM6By6()
{
    var numbers = new[]
    {
        new[] { 1, 2, 3, 4, 5, 6},
        new[] { 5, 6, 7, 8, 9, 1},
        new[] { 4, 3, 2, 1, 0, 9},
        new[] { 8, 7, 6, 5, 4, 3},
        new[] { 2, 1, 0, 9, 8, 7},
        new[] { 6, 5, 4, 3, 2, 1}
    };

    var aux = numbers.ViewAsNByM(3, 3).ToList();
    Assert.AreEqual(4, aux.Count);

    var a0 = new[] { new[] { 1, 2, 3 }, new[] { 5, 6, 7 }, new [] {4, 3, 2} };
    var a1 = new[] { new[] { 4, 5, 6 }, new[] { 8, 9, 1 }, new [] {1, 0, 9} };
    var a2 = new[] { new[] { 8, 7, 6 }, new[] { 2, 1, 0 }, new[] { 6, 5, 4 } };
    var a3 = new[] { new[] { 5, 4, 3 }, new[] { 9, 8, 7 }, new[] { 3, 2, 1 } };

    Assert.That(aux, Is.EquivalentTo(new[] { a0, a1, a2, a3 }));
}
\$\endgroup\$
  • \$\begingroup\$ do we need to review your question or answer ? \$\endgroup\$ – Disappointed Aug 24 '16 at 11:19
  • \$\begingroup\$ @Disappointed Due to community rules you should review my question. The code is not that different anyway. If you have other issues that you detected please give a new answer. \$\endgroup\$ – Bruno Costa Aug 24 '16 at 11:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.