如何将3D数组“压平”或“索引”为1D数组?

44

我正在尝试将3D数组压缩为1D数组,以便在我的游戏的“区块”系统中使用。这是一个3D方块游戏,基本上我希望该区块系统几乎与Minecraft的系统相同(但无论如何都不是Minecraft的克隆)。在我之前的2D游戏中,我使用以下算法访问了已压平的数组:

Tiles[x + y * WIDTH]

然而,这显然无法在三维空间中使用,因为缺少Z轴。我不知道如何在三维空间中实现这种算法。宽度、高度和深度都是常数(而且宽度与高度相同)。

是不是只需要x + y * WIDTH + Z * DEPTH?我对数学很差,而且我刚开始学习3D编程,所以我很迷惑 :|

PS. 这是因为我经常通过索引循环和获取数组中的内容。我知道一维数组比多维数组更快(出于某些原因我记不清了 :P)。即使这可能不是必要的,我仍希望尽可能获得良好的性能 :)


我说的正确吗?你想把一个3D数组放入一个1D数组中吗? - Dominic K
1
为什么不使用三维数组呢? - svick
@DMan 是的,没错。 - user925777
14个回答

1

m[x][y][z] = data[xYZ + yZ + z]

x-picture:
0-YZ
.
.
x-YZ

y-picture 

0-Z
.
.
.
y-Z


summing up, it should be : targetX*YZ + targetY*Z + targetZ  

0

Samuel Kerrien对Python的回答:

def to1D(crds,dims):
  x,y,z=crds
  xMax,yMax,zMax=dims
  return (z * xMax * yMax) + (y * xMax) + x

def to3D(idx,dims):
    xMax,yMax,zMax=dims
    z = idx // (xMax * yMax)
    idx -= (z * xMax * yMax)
    y = idx // xMax
    x = idx % xMax
    return x, y, z 

0

如果有人想要将nD(2D,3D,4D等)数组展平为1D,我编写了以下代码。例如,如果在不同维度中存储了数组的大小,则可以使用sizes数组:

#  pseudo code
sizes = {size_x, size_y, size_z,...};

这个递归函数可以给你一系列的{1,size_x,size_x*size_y,size_x*size_y*size_z,...}

// i: number of the term
public int GetCoeff(int i){
        if (i==0)
            return 1;
        return sizes[i-1]*GetCoeff(i-1);
}

因此,我们必须将nD索引乘以它们对应的系列项并将它们相加,以获得{ix + iy*size_x + iz*size_x*size_y, ...}

// indexNd: {ix, iy, iz, ...}
public int GetIndex1d(int[] indexNd){
    int sum =0;
    for (var i=0; i<indexNd.Length;i++)
        sum += indexNd[i]*GetCoeff(i);
    return sum;
    
}

在这段代码中,我假设nD数组在内存中是沿着x、y、z等方向连续的。因此,你可能会将类似数组命名为arr[z,y,x]。但是,如果你按照另一种方式命名它们,即arr[x,y,z],那么z就是最快的索引,我们喜欢计算iz + iy*size_z + ix* size_z*size_y。在这种情况下,下面的函数给出了系列{1, size_z, size_z*size_y, ...}
// Dims is dimension of array, like 3 for 3D
public int GetReverseCoeff(int i){
       if (i==0)
            return 1;
        return sizes[Dims-i]*GetReverseCoeff(i-1);
}

系数按正确顺序存储:

public void SetCoeffs(){
    for (int i=0;i<Dims;i++)
            coeffs[Dims-i-1] = GetReverseCoeff(i);
}

1D 索引的计算方式与以前相同,只是使用了 coeffs 数组:

// indexNd: {ix, iy, iz, ...}
public int GetIndex1d(int[] indexNd){
    int sum =0;
    for (var i=0; i<indexNd.Length;i++)
        sum += indexNd[i]*coeffs[i];
    return sum;
    
}

0
这是一个通用排名矩阵的C#实现。数据存储在一维数组中,函数int GetIndex(int,int,int...)用于找到对应于第n个排名张量索引的一维数组中的索引。反向操作称为int[] GetIndexes(int)
该代码还支持列主序(默认)和行主序。
public enum IndexOrdering
{
    ColumnMajor,
    RowMajor,
}
public class Matrix<T>
{
    readonly T[] _data;
    readonly int[] _shape;

    public Matrix(IndexOrdering ordering, int[] shape)
    {
        Ordering = ordering;
        Rank = shape.Length;
        _shape = shape;
        Size = shape.Aggregate(1, (s, l) => s * l);
        _data = new T[Size];
    }
    public Matrix(params int[] shape)
        : this(IndexOrdering.ColumnMajor, shape) { }
    public Matrix(IndexOrdering ordering, int[] shape, T[] data)
        : this(ordering, shape)
    {
        Array.Copy(data, _data, Size);
    }

    public int Rank { get; }
    public int Size { get; }
    public IReadOnlyList<int> Shape { get => _shape; }
    internal T[] Data { get => _data; }
    public IndexOrdering Ordering { get; set; }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public int GetIndex(params int[] indexes)
    {
        switch (Ordering)
        {
            case IndexOrdering.ColumnMajor:
                {
                    int index = 0;
                    for (int i = 0; i < Rank; i++)
                    {
                        index = _shape[i] * index + indexes[i];
                    }
                    return index;
                }
            case IndexOrdering.RowMajor:
                {
                    int index = 0;
                    for (int i = Rank - 1; i >= 0; i--)
                    {
                        index = _shape[i] * index + indexes[i];
                    }
                    return index;
                }
            default:
                throw new NotSupportedException();
        }
    }
    public int[] GetIndexes(int index)
    {
        int[] indexes = new int[Rank];
        switch (Ordering)
        {
            case IndexOrdering.ColumnMajor:
                {
                    for (int i = Rank - 1; i >= 0; i--)
                    {
                        // div = index/shape[i]
                        // indexes[i] = index - shape[i]*div
                        // index = div
                        index = Math.DivRem(index, _shape[i], out indexes[i]);
                    }
                    return indexes;

                }
            case IndexOrdering.RowMajor:
                {
                    for (int i = 0; i < Rank; i++)
                    {
                        // div = index/shape[i]
                        // indexes[i] = index - shape[i]*div
                        // index = div
                        index = Math.DivRem(index, _shape[i], out indexes[i]);
                    }
                    return indexes;
                }
            default:
                throw new NotSupportedException();
        }
    }

    public T this[params int[] indexes]
    {
        get => _data[GetIndex(indexes)];
        set => _data[GetIndex(indexes)] = value;
    }

    public override string ToString()
    {
        return $"[{string.Join(",", _data)}]";
    }
}

为了测试具有rank=3的矩阵(3D矩阵)的代码,我使用了以下内容:
static void Main(string[] args)
{
    const int L0 = 4;
    const int L1 = 3;
    const int L2 = 2;

    var A = new Matrix<int>(L0, L1, L2);

    Console.WriteLine($"rank(A)={A.Rank}");
    Console.WriteLine($"size(A)={A.Size}");
    Console.WriteLine($"shape(A)=[{string.Join(",", A.Shape)}]");

    int index = 0;
    for (int i = 0; i < L0; i++)
    {
        for (int j = 0; j < L1; j++)
        {
            for (int k = 0; k < L2; k++)
            {
                A[i, j, k] = index++;
            }
        }
    }
    Console.WriteLine($"A=");

    for (int i = 0; i < L0; i++)
    {
        for (int j = 0; j < L1; j++)
        {
            Console.Write($"[{i},{j},..] = ");
            for (int k = 0; k < L2; k++)
            {
                Console.Write($"{A[i, j, k]} ");
            }
            Console.WriteLine();
        }
        Console.WriteLine();
    }

    for (int idx = 0; idx < A.Size; idx++)
    {
        var ixs = A.GetIndexes(idx);
        Console.WriteLine($"A[{string.Join(",", ixs)}] = {A.Data[idx]}");
    }

}

输出

rank(A)=3
size(A)=24
shape(A)=[4,3,2]
A=
[0,0,..] = 0 1
[0,1,..] = 2 3
[0,2,..] = 4 5

[1,0,..] = 6 7
[1,1,..] = 8 9
[1,2,..] = 10 11

[2,0,..] = 12 13
[2,1,..] = 14 15
[2,2,..] = 16 17

[3,0,..] = 18 19
[3,1,..] = 20 21
[3,2,..] = 22 23

A[0,0,0] = 0
A[0,0,1] = 1
A[0,1,0] = 2
A[0,1,1] = 3
A[0,2,0] = 4
A[0,2,1] = 5
A[1,0,0] = 6
A[1,0,1] = 7
...
A[3,1,1] = 21
A[3,2,0] = 22
A[3,2,1] = 23

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