[英]How to "flatten" or "index" 3D-array in 1D array?
我正在尝试将 3D 数组展平为我游戏中“块”系统的一维数组。 这是一个 3D 块游戏,基本上我希望块系统与 Minecraft 的系统几乎相同(但是,这不是 Minecraft 的克隆)。 在我之前的 2D 游戏中,我使用以下算法访问了展平数组:
Tiles[x + y * WIDTH]
但是,这显然不适用于 3D,因为它缺少 Z 轴。 我不知道如何在 3D 空间中实现这种算法。 宽度、高度和深度都是常量(宽度与高度一样大)。
只是x + y*WIDTH + Z*DEPTH
吗? 我的数学很差,而且我才刚刚开始 3D 编程,所以我很迷茫:|
附言。 这样做的原因是我循环并通过索引从中获取很多东西。 我知道一维 arrays 比多维 arrays 快(原因我不记得了:P)。 尽管这可能不是必需的,但我希望性能尽可能好:)
这是 Java 中的解决方案,可为您提供:
下面是我选择遍历 3D 矩阵的路径的图形说明,单元格按其遍历顺序编号:
转换函数:
public int to1D( int x, int y, int z ) {
return (z * xMax * yMax) + (y * xMax) + x;
}
public int[] to3D( int idx ) {
final int z = idx / (xMax * yMax);
idx -= (z * xMax * yMax);
final int y = idx / xMax;
final int x = idx % xMax;
return new int[]{ x, y, z };
}
算法大致相同。 如果你有一个 3D 数组Original[HEIGHT, WIDTH, DEPTH]
那么你可以把它变成Flat[HEIGHT * WIDTH * DEPTH]
Flat[x + WIDTH * (y + DEPTH * z)] = Original[x, y, z]
顺便说一句,在 .NET 中,您应该更喜欢数组数组而不是多维数组。 性能差异显着
我认为以上需要一点修正。 假设你的 HEIGHT 为 10,WIDTH 为 90,一维数组将是 900。根据上面的逻辑,如果你在数组 9 + 89*89 的最后一个元素,显然这大于 900。正确的算法是:
Flat[x + HEIGHT* (y + WIDTH* z)] = Original[x, y, z], assuming Original[HEIGHT,WIDTH,DEPTH]
具有讽刺意味的是,如果您的高度>宽度,您将不会遇到溢出,只需完成疯狂的结果;)
x + y*WIDTH + Z*WIDTH*DEPTH
。 将其可视化为一个长方体:首先沿着x
遍历,然后每个y
是一个“线” width
步长,每个z
是一个“平面” WIDTH*DEPTH
步长区域。
您快到了。 您需要将 Z 乘以WIDTH
和DEPTH
:
Tiles[x + y*WIDTH + Z*WIDTH*DEPTH] = elements[x][y][z]; // or elements[x,y,z]
TL; 博士
正确答案可以有多种书写方式,但我最喜欢以易于理解和可视化的方式书写。 这是确切的答案:
(width * height * z) + (width * y) + x
TS;DR
可视化它:
someNumberToRepresentZ + someNumberToRepresentY + someNumberToRepresentX
someNumberToRepresentZ
表示我们在哪个矩阵上( depth
)。 要知道我们在哪个矩阵上,我们必须知道每个矩阵有多大。 矩阵是 2d 大小为width * height
,简单。 要问的问题是“我所在的矩阵之前有多少个矩阵?” 答案是z
:
someNumberToRepresentZ = width * height * z
someNumberToRepresentY
表示我们在哪一行( height
)。 要知道我们在哪一行,我们必须知道每行有多大:每行是 1d,大小为width
。 要问的问题是“我所在的行之前有多少行?”。 答案是y
:
someNumberToRepresentY = width * y
someNumberToRepresentX
表示我们在哪一列( width
)。 要知道我们在哪一列,我们只需使用x
:
someNumberToRepresentX = x
我们的可视化然后
someNumberToRepresentZ + someNumberToRepresentY + someNumberToRepresentX
成为
(width * height * z) + (width * y) + x
上面 Samuel Kerrien 的正向和反向变换几乎是正确的。 下面包含一个更简洁的(基于 R 的)转换映射和示例(“a %% b”是模运算符,表示 a 除以 b 的余数):
dx=5; dy=6; dz=7 # dimensions
x1=1; y1=2; z1=3 # 3D point example
I = dx*dy*z1+dx*y1+x1; I # corresponding 2D index
# [1] 101
x= I %% dx; x # inverse transform recovering the x index
# [1] 1
y = ((I - x)/dx) %% dy; y # inverse transform recovering the y index
# [1] 2
z= (I-x -dx*y)/(dx*dy); z # inverse transform recovering the z index
# [1] 3
注意除法 (/) 和模块 (%%) 运算符。
为了更好地理解一维数组中 3D 数组的描述将是(我猜最佳答案中的深度是指 Y 大小)
IndexArray = x + y * InSizeX + z * InSizeX * InSizeY;
IndexArray = x + InSizeX * (y + z * InSizeY);
正确的算法是:
Flat[ x * height * depth + y * depth + z ] = elements[x][y][z]
where [WIDTH][HEIGHT][DEPTH]
m[x][y][z] = 数据[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
如果有人有兴趣将 nD (2D, 3D, 4D, ...) 数组展平为 1D,我编写了以下代码。 例如,如果将不同维度的数组大小存储在sizes
数组中:
# pseudo code
sizes = {size_x, size_y, size_z,...};
这个递归 function 给你系列{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 数组在 memory 中是连续的,首先是 x,然后是 y, z, ...。 所以你可能称你的数组为arr[z,y,x]
。 但是,如果你用另一种方式称呼它们,arr[x,y,z] 那么 z 是最快的索引,我们喜欢计算iz + iy*size_z + ix* size_z*size_y
。 在这种情况下,下面的 function 为我们提供了系列{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);
}
除了使用 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;
}
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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.