简体   繁体   English

将 3D 数组转换为 2D 数组

[英]Convert 3D array to 2D array

I have a rank 3 int array array[9][3][3] and I want to convert it into a rank 2 array arrayConvt[9][9] by removing the major axis (rather than the middle axis).我有一个秩为 3 的 int 数组array[9][3][3] ,我想通过删除主轴(而不是中轴)将它转换为一个秩为 2 的数组arrayConvt[9][9] To make a 9x9 array rather than 3x27, imagine array broken up into 3 equal parts, each laid out into arrayConvt before the next.要制作 9x9 数组而不是 3x27,想象将array分成 3 个相等的部分,每个部分在下一个之前布局到arrayConvt Note that the middle arrays ( array[i] ) do not remain contiguous in arrayConvt , but the innermost arrays ( array[i][j] ) do.请注意,中间数组 ( array[i] ) 在arrayConvt不保持连续,但最里面的数组 ( array[i][j] ) 保持连续。

One way to visualize it is to look at array as an array of 9 blocks.可视化它的一种方法是将array视为 9 个块的数组。 I want to recombine the blocks left-to-right, top-to-bottom:我想从左到右,从上到下重新组合块:

9 x 3 x 3 阵列到 9 x 9

How can I reshape array according to this mapping?如何根据此映射重塑array

The code sample below provides data to work with and the desired result:下面的代码示例提供了要使用的数据和所需的结果:

public static void main(String[] args) {
    int[][][] array = {
        {
            {0, 1, 2},
            {10, 11, 12},
            {20, 21, 22}
        },
        {
            {100, 101, 102},
            {110, 111, 112},
            {120, 121, 122}
        },
        {
            {200, 201, 202},
            {210, 211, 212},
            {220, 221, 222}
        },
        {
            {300, 301, 302},
            {310, 311, 312},
            {320, 321, 322}
        },
        {
            {400, 401, 402},
            {410, 411, 412},
            {420, 421, 422}
        },
        {
            {500, 501, 502},
            {510, 511, 512},
            {520, 521, 522}
        },
        {
            {600, 601, 602},
            {610, 611, 612},
            {620, 621, 622}
        },
        {
            {700, 701, 702},
            {710, 711, 712},
            {720, 721, 722}
        },
        {
            {800, 801, 802},
            {810, 811, 812},
            {820, 821, 822}
        }
    };
    
    int[][] arrayConvt;
    
    /*****
     * What should go here to fill out `arrayConvt` using entries from `array` so it's equivalent to `array2d` below?
     */
    
    int[][] array2d = {
        {  0,   1 ,  2,   100, 101, 102,   200, 201, 202},
        { 10,  11,  12,   110, 111, 112,   210, 211, 212},
        { 20,  21,  22,   120, 121, 122,   220, 221, 222},
        
        {300, 301, 302,   400, 401, 402,   500, 501, 502},
        {310, 311, 312,   410, 411, 412,   510, 511, 512},
        {320, 321, 322,   420, 421, 422,   520, 521, 522},
        
        {600, 601, 602,   700, 701, 702,   800, 801, 802},
        {610, 611, 612,   710, 711, 712,   810, 811, 812},
        {620, 621, 622,   720, 721, 722,   820, 821, 822}
    };
    
}

Try this.尝试这个。

public static void main(String[] args) {
    int[][][] array = {
        {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}},
        {{10, 11, 12}, {13, 14, 15}, {16, 17, 18}},
        {{20, 21, 22}, {23, 24, 25}, {26, 27, 28}},
        {{30, 31, 32}, {33, 34, 35}, {36, 37, 38}},
        {{40, 41, 42}, {43, 44, 45}, {46, 47, 48}},
        {{50, 51, 52}, {53, 54, 55}, {56, 57, 58}},
        {{60, 61, 62}, {63, 64, 65}, {66, 67, 68}},
        {{70, 71, 72}, {73, 74, 75}, {76, 77, 78}},
        {{80, 81, 82}, {83, 84, 85}, {86, 87, 88}},
    };
    int[][] arrayConv = new int[9][9];

    int[][] s = {
        {0, 0}, {0, 3}, {0, 6},
        {3, 0}, {3, 3}, {3, 6},
        {6, 0}, {6, 3}, {6, 6},
    };

    for (int i = 0, p = 0; i < 9; ++i, ++p)
        for (int j = 0, r = s[p][0]; j < 3; ++j, ++r)
            for (int k = 0, c = s[p][1]; k < 3; ++k, ++c)
                arrayConv[r][c] = array[i][j][k];

    for (int[] r : arrayConv)
        System.out.println(Arrays.toString(r));
}

output:输出:

[0, 1, 2, 10, 11, 12, 20, 21, 22]
[3, 4, 5, 13, 14, 15, 23, 24, 25]
[6, 7, 8, 16, 17, 18, 26, 27, 28]
[30, 31, 32, 40, 41, 42, 50, 51, 52]
[33, 34, 35, 43, 44, 45, 53, 54, 55]
[36, 37, 38, 46, 47, 48, 56, 57, 58]
[60, 61, 62, 70, 71, 72, 80, 81, 82]
[63, 64, 65, 73, 74, 75, 83, 84, 85]
[66, 67, 68, 76, 77, 78, 86, 87, 88]

First, construct some source data首先,构造一些源数据

int src[][][] = new int[9][3][3];
for (int r = 0; r < 9; r++) {
    for (int rr = 0; rr < 3; rr++) {
        for (int cc = 0; cc < 3; cc++) {
            src[r][rr][cc] = r * 9 + rr * 3 + cc + 1;
        }
    }
}
for (int[][] d : src) {
    System.out.println(Arrays.deepToString(d));
}

prints印刷

[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[[10, 11, 12], [13, 14, 15], [16, 17, 18]]
[[19, 20, 21], [22, 23, 24], [25, 26, 27]]
[[28, 29, 30], [31, 32, 33], [34, 35, 36]]
[[37, 38, 39], [40, 41, 42], [43, 44, 45]]
[[46, 47, 48], [49, 50, 51], [52, 53, 54]]
[[55, 56, 57], [58, 59, 60], [61, 62, 63]]
[[64, 65, 66], [67, 68, 69], [70, 71, 72]]
[[73, 74, 75], [76, 77, 78], [79, 80, 81]]
    

Now transform the matrix现在变换矩阵

int[][] dst = transform(src);

for (int[] row : dst) {
    System.out.println(Arrays.toString(row));
}

prints印刷

[1, 2, 3, 10, 11, 12, 19, 20, 21]
[4, 5, 6, 13, 14, 15, 22, 23, 24]
[7, 8, 9, 16, 17, 18, 25, 26, 27]
[28, 29, 30, 37, 38, 39, 46, 47, 48]
[31, 32, 33, 40, 41, 42, 49, 50, 51]
[34, 35, 36, 43, 44, 45, 52, 53, 54]
[55, 56, 57, 64, 65, 66, 73, 74, 75]
[58, 59, 60, 67, 68, 69, 76, 77, 78]
[61, 62, 63, 70, 71, 72, 79, 80, 81]
  • sr,sc - the source row and column of the inner 3x3 matrix to copy sr,sc - 要复制的内部 3x3 矩阵的源行和列
  • r - the destination row to contain the above r - 包含上述内容的目标行
  • the outer loop is used to create the destination row as well as allow外循环用于创建目标行以及允许
    intermediate increments of key variables关键变量的中间增量
public static int[][] transform(int[][][] src) {
  int sr = 0;
  int[][] dst = new int[9][9];
  for (int x = 0; x < 3; x++) {
     for (int sc = 0; sc < 3; sc++) {
        int r = x * 3 + sc;
        System.arraycopy(src[sr][sc], 0, dst[r], 0, 3);
        System.arraycopy(src[sr + 1][sc], 0, dst[r], 3, 3);
        System.arraycopy(src[sr + 2][sc], 0, dst[r], 6, 3);
     }
     sr = sr + 3;
  }
  return dst;
}

The best way of figuring out how to do this sort of thing is to play around with indices & reshaping, examining the resultant arrays.弄清楚如何做这种事情的最好方法是使用索引和重塑,检查结果数组。 Once you do this, you notice a few things that can help you come up with more formal approaches.一旦你这样做了,你就会注意到一些可以帮助你想出更正式方法的事情。

One is to examine the mapping in terms of array index expressions.一种是根据数组索引表达式检查映射。 Since you want to map array[i][j][k] to arrayConvt[u][v] , you need a way of expressing u and v in terms of i , j and k (or vice versa).由于您想将array[i][j][k] arrayConvt[u][v]arrayConvt[u][v] ,您需要一种根据ijk来表达uv的方法(反之亦然)。

Calculate Destination Indices计算目的地索引

Let's start with u .让我们从u开始。 Note each array[i][j] remains contiguous in the result.注意每个array[i][j]在结果中保持连续。 Note also that array[i][j] is followed by array[i+1][j] (rather than array[i][j+1] ) until you reach the end of arrayConvt .另请注意, array[i][j]后跟array[i+1][j] (而不是array[i][j+1] ),直到到达arrayConvt Thus, array[i..i+2][j] (where i is a multiple of 3), if flattened, becomes arrayConvt[j+x] .因此, array[i..i+2][j] (其中i是 3 的倍数),如果展平,则变为arrayConvt[j+x] The x is present because the range of j doesn't quite match between array and arrayConvt , and so an adjustment is needed. x存在是因为j的范围在arrayarrayConvt之间不太匹配,因此需要进行调整。 Once the first 3 arrays in arrayConvt are filled, j goes back to 0, but j+x can go on to the next element of arrayConvt .一旦arrayConvt中的前 3 个数组被填充, j返回到 0,但j+x可以继续到arrayConvt的下一个元素。 As for where to get x , k is similarly limited in range, but i hasn't run through its range.至于从哪里得到xk范围同样有限,但i还没有跑过它的范围。 Since j runs 0..2, it only needs to be combined with a multiple of 3, which we can get with (i / 3) * 3 .由于j运行 0..2,它只需要与 3 的倍数组合,我们可以用(i / 3) * 3 This gives j + (i / 3)*3 , which a quick mental check will show that it runs 0..8.这给出了j + (i / 3)*3 ,快速的心理检查将显示它运行 0..8。 Thus, u = j + (i / 3) * 3 .因此, u = j + (i / 3) * 3

On to v .v First note:第一个注意:

  1. the minor index ( k ) in array[i][j][k] changes just as rapidly for arrayConvt and array[i][j][k]的次要索引 ( k ) 的变化与arrayConvt
  2. the minor index for arrayConvt is independent of j arrayConvt的次要索引独立于j

Thus v = k + f(i) (where f must be determined).因此v = k + f(i) (其中f必须确定)。 Looking at how each arrayConvt[u] is layed out in terms of k and the 1D arrays from array[i][j] ( <1D> #n in the diagram), we see something similar to how u related to i .查看每个arrayConvt[u]如何根据k和来自array[i][j]的一维数组(图中的<1D> #n )进行<1D> #n ,我们看到类似于u如何与i相关的内容。

  v  | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
  k  | 0 | 1 | 2 | 0 | 1 | 2 | 0 | 1 | 2 |
     |  <1D> #0  |  <1D> #1  |  <1D> #2  |
  i  |  3a + 0   |  3a + 1   |  3a + 2   |
f(i) |     0     |     3     |     6     |

We can see that for any row of arrayConvt , i % 3 will range over 0..2.我们可以看到,对于arrayConvt任何行, i % 3范围将超过 0..2。 (i % 3) * 3 gives us f(i) . (i % 3) * 3给我们f(i) Thus v = k + (i % 3) * 3因此v = k + (i % 3) * 3

This gives you a loop over the source array indices, which are mapped to the destination array indices.这为您提供了对映射到目标数组索引的源数组索引的循环。

int[][] arrayConvt = array[array.length][array[0].length * array[0][0].length];
for (int i = 0; i < array.length; ++i) {
    for (int j = 0; j < array[i].length; ++j) {
        for (int k = 0; k < array[i][j].length; ++j) {
            arrayConvt[j + (i/3)*3][k + (i%3)*3] = array[i][j][k];
        }
    }
}

Calculate Source Indices计算源索引

You can take the above expressions for u and v and rewrite them so i , j and k are in terms of u and v .您可以将上述uv表达式重写为ijkuv You could also try starting from u and v and examine how i , j and k might come from them.您也可以尝试从uv并检查ijk可能来自它们的方式。 Let's do this, but by a different route: examine the entries in the sample array2d , since i , j and k can be read directly from it (not an accident; by encoding coordinates into the original array and figuring out where each element should get mapped to, the overall mapping is, in effect, created. You could even use this as the basis for the mapping function: loop over the elements of the coordinate array, and use the values & indices to copy from the source to the destination).让我们这样做,但通过不同的途径:检查样本array2d的条目,因为可以直接从中读取ijk (不是偶然的;通过将坐标编码到原始数组中并找出每个元素应该得到的位置映射到,整个映射实际上已创建。您甚至可以将其用作映射函数的基础:遍历坐标数组的元素,并使用值和索引从源复制到目标) .

k we see increasing left-to-right only (along v ), though it wraps around. k我们只看到从左到右增加(沿着v ),尽管它环绕。 This is simply v % 3 .这只是v % 3 j increases top-to-bottom (along u ) and also wraps around. j从上到下(沿u )增加并环绕。 Thus j = u % 3 .因此j = u % 3

i varies with both u and v . iuv It also occurs in groups of 3 along both axes before changing, so we're looking for something involving u / 3 and v / 3 .它也在改变之前沿两个轴以 3 个为一组出现,因此我们正在寻找涉及u / 3v / 3 Lastly, it varies faster along v and jumps by 3 along u , so i = (u / 3) * 3 + (v / 3) .最后,它沿v变化更快,沿u跳跃 3,因此i = (u / 3) * 3 + (v / 3)

You can loop over the destination array indices, and map the source array indices.您可以遍历目标数组索引,并映射源数组索引。

int[][] arrayConvt = array[array.length][array[0].length * array[0][0].length];
for (int u = 0; i < arrayConvt.length; ++i) {
    for (int v = 0; j < arrayConvt[i].length; ++j) {
        arrayConvt[u][v] = array[ (u / 3) * 3 + (v / 3) ][u % 3][v % 3];
    }
}

Beyond the Problem超越问题

There's another approach that, once spotted, is even easier to work with.还有另一种方法,一旦发现,就更容易使用。 Hidden in the 3D array is a hypercube: array can be viewed as a projection of a 4D array into 3 dimensions.隐藏在 3D array是一个超立方体: array可以被视为 4D 阵列到 3 维的投影。 You can realize this in various (equivalent) ways:您可以通过各种(等效)方式实现这一点:

  • envisioning array as 9x3x3, broken into three cubes, you get one fairly common visualization of a projected discrete hypercube (the same as a 3x3x3 discrete cube projected into 2D is 3 3x3 squares).array设想为 9x3x3,分成三个立方体,您会得到一个相当常见的投影离散超立方体的可视化(与投影到 2D 的 3x3x3 离散立方体是 3 个 3x3 正方形相同)。
  • An MxN array can be represented as an array of 1 lower rank of size M*N by mapping [i][j] to i*N+j (this is how multidimensional arrays are implemented in languages that use contiguous memory for such arrays).通过将[i][j]映射到i*N+j可以将 MxN 数组表示为大小为M*N的 1 个较低等级的数组(这就是在对此类数组使用连续内存的语言中实现多维数组的方式) . Under this mapping, a T[M][N] is equivalent to T[M*N] .在此映射下, T[M][N]等价于T[M*N] Looking at the indices, note that i can be refactored as i = i_0 * 3 + i_1 , where i_0 and i_1 range from 0 through 2. Thus, array[9][3][3] is equivalent to a multidimensional array array4d[3][3][3][3] .查看索引,请注意i可以重构为i = i_0 * 3 + i_1 ,其中i_0i_1范围从 0 到 2。因此, array[9][3][3]等效于多维数组array4d[3][3][3][3]

From this vantage, the problem is a simple reshaping from a rank 4 cube to a rank 2 cube;从这个角度来看,问题是从 4 阶立方体到 2 阶立方体的简单重塑; the main work is:主要工作是:

  1. figuring out the index order from the source从源中找出索引顺序
  2. identifying which indices of the destination to map to确定要映射到的目的地索引

With indices h , i , j and k , we have the index expression array[h*3+i][j][k] (note: i is the 3rd dimension, or depth, and h is the 4th, or duration).使用索引hijk ,我们有索引表达式array[h*3+i][j][k] (注意: i是第 3 维或深度, h是第 4 维或持续时间) . When mapping, the last index of array remains the last of arrayConvt (the 1st dimension).映射时, array的最后一个索引仍然是arrayConvt的最后一个(第一维)。 After completing a loop over k , we advance along the outermost array;k完成一个循环后,我们沿着最外面的数组前进; in particular, the 3rd dimension, i (the 4th is also along the outermost array but jumps from cube to cube).特别是第 3 维, i (第 4 维也沿着最外面的数组,但从一个立方体跳到另一个立方体)。 After copying a square (with dimensions width x depth), we continue with a cube, travelling along its height, or j axis.在复制一个正方形(尺寸为宽 x 深)之后,我们继续一个立方体,沿着它的高度或j轴移动。 Lastly, we check that the travel direction corresponds to the remaining axis: after finishing a cube, we go to the next cube, which is indeed along h .最后,我们检查行进方向是否对应于剩余的轴:完成一个立方体后,我们去下一个立方体,它确实沿着h This gives a an index order (minor to major) of k , i , j , h .这给出了kijh的索引顺序(次要到主要)。

The formulae for u and v come straight from the T[M][N] to T[M*N] conversion. uv的公式直接来自T[M][N]T[M*N]转换。 Each index of arrayConvt uses two indices from the hypercube, in the previously determined index order: that's h and j for u , and i and k for v . arrayConvt每个索引arrayConvt使用超立方体中的两个索引,按照先前确定的索引顺序:即uhj ,以及v ik Thus, u = h * 3 + j and v = i * 3 + k .因此, u = h * 3 + jv = i * 3 + k

int M = 3,
    N = 3,
    P = 3,
    Q = 3;
for (int h = 0; h < M; ++h) {
    for (int i = 0; i < N; ++i) {
        for (int j = 0; j < P; ++j) {
            for (int k = 0; k < Q; ++j) {
                arrayConvt[h * 3 + j][i * 3 + k] = array[h * 3 + i][j][k];
            }
        }
    }
}

Note the index expressions here can be converted to the ones used earlier.请注意,此处的索引表达式可以转换为之前使用的索引表达式。 You could use this to write a non-nested loop over 0..80 that reshapes array , but it wouldn't be as readable.您可以使用它来编写一个超过 0..80 的非嵌套循环来重塑array ,但它不会那么可读。 You can also use this approach to reshape the hypercube to any compatible shape you want.您还可以使用这种方法将超立方体重塑为您想要的任何兼容形状。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM