如何在 C# 中获取三维数组的横截面

3

假设你在C#中有一个三维数组

int space[width, height, depth];

您希望实现该方法

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

'position'是指您要提取切片的 'dimension' 沿着的点。重要的是不要只使用我们处理3个维度的事实,在下面的示例中,可以通过添加if语句并假定矩阵不会超过3个维度来修复它们。

我的第一次尝试(注释的问题区域):

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

我的第二次尝试同样徒劳无功:

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

这两种尝试都走进了死胡同。你怎么解决呢?可以为空间的维度[,,]设置固定的迭代循环次数,如果维度数量增加,这是可以管理的。聪明/有限的if语句可能有效,但不要为每个维度使用过多的if语句。


欣赏聪明的语言特定技巧,但我也对以“伪代码”方式描述问题感兴趣,如果有一些结构可以解决我在两次尝试中遇到的问题。 - Adam
3个回答

2

快速草稿:


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

它可以推广到任意维度(并且它甚至会使代码更小更简单)。


1

经过几周的其他事情和对Mykola的答案进行一些尝试后,我得出了以下结论:

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

结论:如果您想要这种动态行为,数组不是执行此操作的数据结构。
上面的代码有些类似于我写问题时的想法。但是,如果您想增加维数,则必须更改4个位置。目前没有一种干净的方法可以使用数组来完成此操作。
更新:似乎您可以进一步将代码泛化,因为您可以创建一个动态秩数组。Programatically Declare Array of Arbitrary Rank 然而,这似乎会导致性能损失,这很可能是不可接受的。
参考类似问题:How to get a dimension (slice) from a multidimensional array

1

没调试过,但猜测应该可以工作

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接