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 代码中。
\ndef 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\nRun Code Online (Sandbox Code Playgroud)\n打印出变换后的点对的排序列表的基本原理是双重的:(i)任何面的方向都可以通过其两个角的位置来指定;(ii) 然后很容易检查每对的唯一性,例如通过管道输出到uniq.
以下是排序输出的开始方式:
\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))\nRun Code Online (Sandbox Code Playgroud)\n
让 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。
您可以使用旋转矩阵。绕 x 轴旋转 3D 数组意味着位置处的元素(i,j,k)将映射到位置(i,-k,j)。当然,如果你的数组是 0 索引的,你可能必须替换-k为size-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 数组,同时注意移动负索引,以免访问越界。
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)