如何获得三维阵列的所有24个旋转?

Lai*_*sny 13 arrays algorithm rotation multidimensional-array

我有一个三维数组.把它想象成一块砖头.这种砖有24种可能的旋转(保持其边缘与坐标轴平行).如何生成所有相应的三维数组?

Jam*_*at7 22

骰子(半对骰子)可以方便地观察 24 个不同的方向,并可以建议操作顺序来生成它们。您将看到六个面中的任何一个都可以位于最上面,下面的侧面可以旋转到四个不同的基本方向。让我们表示两个操作: \xe2\x80\x9c转动\xe2\x80\x9d 和 \xe2\x80\x9c滚动\xe2\x80\x9d,其中转动使骰子绕 z 轴从一个基数旋转到下一个,滚动会将骰子旋转 90\xc2\xb0 远离您,因此远离的面成为底面,近的面成为顶部。这些操作可以使用 Felipe Lopes 的答案中提到的旋转矩阵来表示,或者可以表示为简单函数,当给定 (x,y,z) 时返回 (-y,x,z) 或 (x,z,- y) 分别。

\n

无论如何,如果您将骰子的 1 放在近侧,2 放在右侧,3 放在顶部,您会发现以下步骤序列会生成 12 个不同方向,顶部有 1、2 或 3 个点:RTTTRTTTRTTT。然后,序列 RTR 在原来的 1、2、3 处暴露出 6、4、5,序列 RTTTRTTTRTTT 的重复会生成十二个方向,顶部有 4、5 或 6 个点。上述序列嵌入在以下 python 代码中。

\n
def roll(v): return (v[0],v[2],-v[1])\ndef turn(v): return (-v[1],v[0],v[2])\ndef sequence (v):\n    for cycle in range(2):\n        for step in range(3):  # Yield RTTT 3 times\n            v = roll(v)\n            yield(v)           #    Yield R\n            for i in range(3): #    Yield TTT\n                v = turn(v)\n                yield(v)\n        v = roll(turn(roll(v)))  # Do RTR\n\np = sequence(( 1, 1, 1))\nq = sequence((-1,-1, 1))\nfor i in sorted(zip(p,q)):\n    print i\n
Run Code Online (Sandbox Code Playgroud)\n

打印出变换后的点对的排序列表的基本原理是双重的:(i)任何面的方向都可以通过其两个角的位置来指定;(ii) 然后很容易检查每对的唯一性,例如通过管道输出到uniq.

\n

以下是排序输出的开始方式:

\n
((-1, -1, -1), (-1, 1, 1))\n((-1, -1, -1), (1, -1, 1))\n((-1, -1, -1), (1, 1, -1))\n((-1, -1, 1), (-1, 1, -1))\n((-1, -1, 1), (1, -1, -1))\n((-1, -1, 1), (1, 1, 1))\n((-1, 1, -1), (-1, -1, 1))\n((-1, 1, -1), (1, -1, -1))\n((-1, 1, -1), (1, 1, 1))\n
Run Code Online (Sandbox Code Playgroud)\n


Car*_*ood 7

让 X 绕 X 轴旋转 90 度,Y 绕 Y 轴旋转 90 度,那么 24 种可能的唯一组合是(给出最多 5 次旋转的所有可能组合,除了那些具有四倍相同旋转的组合(例如 XXXX、XXXXY XYYYY 等):

1.  I
2.  X
3.  Y
4.  XX = YXXY
5.  XY
6.  YX
7.  YY = XYYX
8.  XXX = XYXXY = YXXYX = YXYXY = YYXYY
9.  XXY = YXXYY = YYYXX
10. XYX = YXY
11. XYY = XXYYX = YYXXX
12. YXX = XXYYY = YYXXY
13. YYX = XXXYY = XYYXX
14. YYY = XXYXX = XYXYX = XYYXY = YXYYX
15. XXXY
16. XXYX = XYXY = YXYY
17. XXYY = YYXX
18. XYXX = YXYX = YYXY
19. XYYY
20. YXXX
21. YYYX
22. XXXYX = XXYXY = XYXYY = YXYYY
23. XYXXX = YXYXX = YYXYX = YYYXY
24. XYYYX = YXXXY
Run Code Online (Sandbox Code Playgroud)

当然,您可以使用任意两个 90 度旋转来代替 X 和 Y。例如,Y 和 Z。

或者,如果您还使用 Z,则绕 Z 轴旋转 90 度,则 4 次旋转就足够了:

1.  I
2.  X = YXZ
3.  Y = ZYX
4.  Z = XZY
5.  XX = XYXZ = YXXY = YXYZ = YXZX = YYZZ = YZXZ = ZXXZ = ZZYY
6.  XY = YZ = ZX = XZYX = YXZY = ZYXZ
7.  XZ = XXZY = YXZZ = YYYX = ZYYY
8.  YX = XZZZ = YYXZ = ZYXX = ZZZY
9.  YY = XXZZ = XYYX = YZYX = ZXYX = ZYXY = ZYYZ = ZYZX = ZZXX
10. ZY = XXXZ = XZYY = YXXX = ZZYX
11. ZZ = XXYY = XYZY = XZXY = XZYZ = XZZX = YYXX = YZZY = ZXZY
12. XXX
13. XXY = XYZ = XZX = YZZ = ZXZ
14. XXZ = ZYY
15. XYX = YXY = YYZ = YZX = ZXX
16. XYY = YZY = ZXY = ZYZ = ZZX
17. XZZ = YYX
18. YXX = ZZY
19. YYY
20. ZZZ
21. XXXY = XXYZ = XXZX = XYZZ = XZXZ = YZZZ = ZXZZ = ZYYX
22. XXYX = XYXY = XYYZ = XYZX = XZXX = YXYY = YYZY = YZXY = YZYZ = YZZX = ZXXY = ZXYZ = ZXZX = ZYZZ = ZZXZ
23. XYXX = XZZY = YXYX = YYXY = YYYZ = YYZX = YZXX = ZXXX
24. XYYY = YXXZ = YZYY = ZXYY = ZYZY = ZZXY = ZZYZ = ZZZX
Run Code Online (Sandbox Code Playgroud)

这 24 个矩阵都包含三个列向量,每个列向量包含两个零和一个负一或加一。每行也恰好有两个零。因此,它们可以很容易地生成:第一个列向量有六种可能性 ((1,0,0), (-1,0,0), (0,-1,0), (0,1,0) 、(0,0,-1) 和 (0,0,1)),这对应于将正 X 轴移动到正或负 x、y 或 z 轴。第二列向量只有四种可能性,因为它必须包含零,而第一列具有非零值。最后,第三列向量只剩下一个位置可以放置其正数或负数。这给出了 6 * 4 * 2 = 48 个矩阵,其中一半也镜像了原始矩阵(它们是镜像和可选旋转的组合)。因此只有 24 次是纯轮换。镜像运算的矩阵的行列式等于 -1,纯旋转的行列式为 1。


Fel*_*pes 6

您可以使用旋转矩阵。绕 x 轴旋转 3D 数组意味着位置处的元素(i,j,k)将映射到位置(i,-k,j)。当然,如果你的数组是 0 索引的,你可能必须替换-ksize-1-k或类似的东西。

类似地,绕 y 轴旋转映射(i,j,k)(k,j,-i)。这两个旋转可以表示为矩阵。对于x轴旋转:

|i'|   |1  0  0| |i|
|j'| = |0  0 -1|*|j|
|k'|   |0  1  0| |k|
Run Code Online (Sandbox Code Playgroud)

对于 y 轴旋转:

|i'|   |0  0  1| |i|
|j'| = |0  1  0|*|j| 
|k'|   |-1 0  0| |k|
Run Code Online (Sandbox Code Playgroud)

任何一般的旋转都可以描述为这两个旋转的序列。连续应用两次旋转只是将 3x3 矩阵相乘。因此,如果您找到它们的所有可能乘积,您将获得 24 个矩阵(包括恒等式),每个矩阵对应于数组的有效旋转。找到所有可能的乘法有点棘手,因为它们不能交换。

我认为你可以暴力破解所有形式的产品(A^p)*(B^q)*(A^r)*(B^s),其中 A 和 B 是之前的两个矩阵p,q,r,s,是它们的幂,范围从 0 到 3 (对 A 或 B 求幂到 4 会将它们带回到单位矩阵) 。

通过这种方式,您可以生成所有 24 个有效的旋转矩阵,并使用它们中的每一个旋转 3D 数组,同时注意移动负索引,以免访问越界。


Fly*_*ana 6

James Waldby的回答很鼓舞人心,我想添加一个稍微改进的版本,只有两个 for 循环。

我们知道有 24 个独特的方向。我通过想象一个骰子来计算:顶面有 6 种可能的选择,顶面的每个面有 4 种可能的旋转。

如果我们重复这个想法会怎样?我想。如果我们能找到一种方法来移动骰子的所有 6 个面,那么我们只需要观察每个面上的 4 次旋转,就完成了!

所以我抓起最近的“砖块”(在我的例子中,是一个维他奶纸盒)并开始旋转,看看什么是访问所有 6 个面的最简单的模式。如果我们引入额外的逆时针旋转,那么我们的操作是:

  • 滚动(沿固定方向,例如,面向您的脸现在向下旋转)
  • 顺时针旋转(沿着固定轴,例如,使面向您的面顺时针旋转,但仍然面向您)
  • 逆时针转动(沿与上一个相同的轴)

然后我们可以通过执行以下操作来访问所有面孔:

横滚 -> 顺时针转动 -> 横滚 -> 逆时针转动 -> 横滚 -> 顺时针转动 -> 横滚 -> 逆时针转动 -> 横滚 -> 顺时针转动 -> 横滚 -> 逆时针转动

随着最后一次翻滚和转身,我们又回到了原来的方向。正如您所看到的,它是滚动+交替的顺时针转弯和逆时针转弯的重复序列。

现在,如果我们将其扩展为包括我们访问的每个面的所有旋转,则变为:

滚动 -> 顺时针旋转 3 次 -> 滚动 -> 逆时针旋转 3 次 -> 滚动 -> 顺时针旋转 3 次 -> 滚动 -> 逆时针旋转 3 次 -> 滚动 -> 顺时针旋转 3 次 -> 滚动 -> 逆时针旋转 3 次

...我们又回到了起点!这可以转化为两个 for 循环(少一个!):

def sequence(m):
  for roll_index in range(6):
    m = roll(m)
    yield(m)
    for turn_index in range(3):
      m = turn_cw(m) if roll_index % 2 == 0 else turn_ccw(m)
      yield(m)
Run Code Online (Sandbox Code Playgroud)