3

Say you have a 3-dimensional array in c#

int space[width, height, depth];

And you would like to implement the method

public int[,] GetCrossSection(int position, int dimension)

Where 'position' is the point along the 'dimension' specified where you would like to extract your slice. It is important to not use the fact that we are only dealing with 3 dimensions, in the examples below you could fix them by adding if statements and assume the matrix will not grow beyond 3 dimensions.

My first attempt (commented problem areas):

public int[,] GetCrossSection(int position, int dimension)
{
    int[] dimensionIterationInterval = new int[] { width, height, depth };
    var dims = new List<int>(dimensionIterationInterval);
    dims.RemoveAt(dimension);
    dimensionIterationInterval = dims.ToArray();


    int[,] crossSection = new int[dimensionIterationInterval[0], dimensionIterationInterval[1]];
    int[] itr = new int[2];
    for (itr[0] = 0; itr[0] < dimensionIterationInterval[0]; itr[0]++)
    {
        for (itr[1] = 0; itr[1] < dimensionIterationInterval[1]; itr[1]++)
        {
           crossSection[itr[0], itr[1]] = space[?,?,?]; //Problem
        }
     }
}

And my second attempt, equally futile:

public int[,] GetCrossSection(int position, int dimension)
{
    int[,] dimensionIterationInterval = new int[,] { { 0, width }, { 0, height }, { 0, depth } };
    dimensionIterationInterval[dimension, 0] = position;
    dimensionIterationInterval[dimension, 1] = position + 1;

    int[,] crossSection = new int[?,?]; //Problem
    for (int x = dimensionIterationInterval[0, 0]; x < dimensionIterationInterval[0, 1]; x++)
     {
       for (int y = dimensionIterationInterval[1, 0]; y< dimensionIterationInterval[1, 1]; y++)
        {
          for (int z = dimensionIterationInterval[2, 0]; z < dimensionIterationInterval[2, 1]; z++)
          {
              crossSection[?, ?] = space[x, y, z]; // Problem
          }
         }
      }
 }

Both those attemps run into dead ends. How would you solve it? It's ok to have the fixed iteration loops for the number of dimensions of space[,,]. If the number of dimensions grow that is somewhat managable. Clever/limited if statments could work, but not excessive ifs for each dimension.

Adam
  • 2,845
  • 2
  • 32
  • 46
  • Clever language specific things appreciated, but I'm also interested in the problem in a 'pseudo-codey' sort of way, if there is some structure you can use to get around the problems i found in my two attempts. – Adam Apr 07 '17 at 07:42

3 Answers3

2

Quick draft:

    static int[,] GetSlice(int[,,] source, int dimension, int position)
    {
        int l1 = 0, l2 = 0;
        if (dimension == 0)
        {
            l1 = source.GetLength(1);
            l2 = source.GetLength(2);
        }
        else if (dimension == 1)
        {
            l1 = source.GetLength(0);
            l2 = source.GetLength(2);
        }
        else if (dimension == 2)
        {
            l1 = source.GetLength(0);
            l2 = source.GetLength(1);
        }

        var result = new int[l1, l2];

        var s0 = dimension == 0 ? position : 0;
        var s1 = dimension == 1 ? position : 0;
        var s2 = dimension == 2 ? position : 0;

        var m0 = dimension == 0 ? position + 1 : source.GetLength(0);
        var m1 = dimension == 1 ? position + 1 : source.GetLength(1);
        var m2 = dimension == 2 ? position + 1 : source.GetLength(2);

        for (var i0 = s0; i0 < m0; i0++)
        for (var i1 = s1; i1 < m1; i1++)
        for (var i2 = s2; i2 < m2; i2++)
        {
            int x = 0, y = 0;
            if (dimension == 0)
            {
                x = i1;
                y = i2;
            }
            else if (dimension == 1)
            {
                x = i0;
                y = i2;
            }
            else if (dimension == 2)
            {
                x = i0;
                y = i1;
            }

            result[x, y] = source[i0, i1, i2];
        }

        return result;
    }

It can be generalised to any number of dimensions (and it even will make code smaller and simpler).

Mykola Kovalchuk
  • 496
  • 2
  • 6
  • 16
1

Didn't debug it, but guess it should work

 private int[,,] _space = new int[width, height, depth];

    public int[,] GetCrossSection(int position, int dimension)
    {
        if (dimension < 0 || dimension > 2) return null;
        if (position > _space.GetLength(dimension) || position < 0) return null;
        var minMax = new Tuple<int, int>[3];
        var resultXLength = -1;
        var resultYLength = -1;
        for (var i = 0; i < _space.Rank; i++)
        {
            if (i == dimension)
            {
                minMax[i] = new Tuple<int, int>(position, position+1);
            }
            else
            {
                minMax[i] = new Tuple<int, int>(0,_space.GetLength(i));
                if (resultXLength == -1) resultXLength = _space.GetLength(i);
                else resultYLength = _space.GetLength(i);
            }
        }
        var result = new int[resultXLength, resultYLength];
        for (var i = minMax[0].Item1; i < minMax[0].Item2; i++)
            for (var j = minMax[1].Item1; j < minMax[1].Item2; j++)
                for (var k = minMax[2].Item1; k < minMax[2].Item2; k++)
                {
                    switch (dimension)
                    {
                        case 0:
                        {
                            result[j, k] = _space[i, j, k];
                            break;
                        }
                        case 1:
                        {
                            result[i, k] = _space[i, j, k];
                            break;
                        }
                        case 2:
                        {
                            result[i, j] = _space[i, j, k];
                            break;
                        }
                    }
                }
        return result;
    }
yolo sora
  • 434
  • 5
  • 17
1

After some weeks of doing other things and after messing around with Mykola's answer a bit I arrived at:

int[,] GetSlice(int[,,] source /*non dynamic 1*/, int dimension, int position)
{
    int dimensions = source.Rank; 
    int[] dims = new int[dimensions-1];

    for(int j = 0; j < dims.Length; j++){
        dims[j] = source.GetLength(j + (j >= dimension ? 1 :0));
    }

    var result = new int[dims[0], dims[1]]; // non dynamic 2

    int[] start = new int[dimensions];
    int[] end = new int[dimensions];
    for(int i = 0; i < dimensions; i++){
        start[i] = dimension == i ? position : 0;
        end[i] = dimension == i ? position + 1 : source.GetLength(i);
    }

    int[] counters = new int[dimensions];
    for (counters[0] = start[0]; counters[0] < end[0]; counters[0]++)
    for (counters[1] = start[1]; counters[1] < end[1]; counters[1]++)
    for (counters[2] = start[2]; counters[2] < end[2]; counters[2]++) // non dynamic 3
    {
        int[] sliceCoord = new int[dimensions-1];

        for(int i = 0; i < t.Length; i++){
            sliceCoord[i] = counters[i + (i >= dimension ? 1 :0)];
        }

        result[sliceCoord[0], sliceCoord[1]] = source[counters[0], counters[1], counters[2]]; // non dynamic 4
    }

    return result;
}

Conclusion: Arrays are not the data structure to do this in if you wan't that kind of dynamic behavior.

The above code was somewhat along the lines I was imagining of when i wrote the question. And still if you want to increase the number of dimensions you have to make changes to 4 places. Currently there is no way to do this cleanly with arrays.

Update: It seems you can generalize the code further as you can create an array of dynamic rank. Programatically Declare Array of Arbitrary Rank However this seem to come at a performance loss, which most likely is unacceptable

Similar question for reference: How to get a dimension (slice) from a multidimensional array

Community
  • 1
  • 1
Adam
  • 2,845
  • 2
  • 32
  • 46