有没有更好的方法来计算F#中立方体的所有可能方向?

时间:2010-08-05 11:31:13

标签: f#

我有一个名为cube的类型,它代表一个物理多维数据集。我编写了一些采用立方体的代码,并生成了多维数据集所有可能方向的列表。

我使用了以下术语,假设立方体在我眼前坐在我面前。

对于立方体的面孔:

  1. 顶部朝向天花板。
  2. 底部朝向桌子。
  3. 正面朝我远方。
  4. 背面朝我。
  5. 左边是我左边的墙。
  6. 右边是我右边的墙。
  7. 对于轴,立方体可以旋转:

    1. 法线轴从桌子延伸到天花板。
    2. 纵轴从我身前伸向我面前的墙壁。
    3. 横轴从左壁延伸到右壁。
    4. 当6个面中的每一个面朝下时,立方体可以以不同的方式(0°,90°,180°和270°)围绕其法线轴旋转。这导致24种可能的方向。

      我已经开始使用立方体类型(请原谅S / O的语法着色):

      type 'a cube(top:'a, bottom:'a, left:'a, right:'a, front:'a, back:'a) =
          member this.Top = top
          member this.Bottom = bottom
          member this.Left = left
          member this.Right = right
          member this.Front = front
          member this.Back = back
          override this.ToString() =
              sprintf "Top: %O, Bottom: %O, Left: %O, Right: %O Front: %O, Back: %O" top bottom left right front back
      

      然后我继续编写一个Cube模块,它提供了函数getOrientations。

      module Cube =
          let rotateNormalRight (c:'a cube) =
              cube(c.Top, c.Bottom, c.Back, c.Front, c.Left, c.Right)
          let rotateLongitudinalRight (c:'a cube) =
              cube(c.Left, c.Right, c.Bottom, c.Top, c.Front, c.Back)
          let rotateLongitudinalLeft (c:'a cube) =
              cube(c.Right, c.Left, c.Top, c.Bottom, c.Front, c.Back)
          let private operations =
              [ rotateNormalRight; rotateNormalRight; rotateNormalRight; rotateLongitudinalRight
                rotateNormalRight; rotateNormalRight; rotateNormalRight; rotateLongitudinalRight
                rotateNormalRight; rotateNormalRight; rotateNormalRight; rotateLongitudinalLeft
                rotateNormalRight; rotateNormalRight; rotateNormalRight; rotateLongitudinalLeft
                rotateNormalRight; rotateNormalRight; rotateNormalRight; rotateLongitudinalRight
                rotateNormalRight; rotateNormalRight; rotateNormalRight ]
          let getOrientations startCube =
              let rec getCubeInner (ops:('a cube -> 'a cube) list) (cl:'a cube list) =
                  match ops with
                  | [] -> cl
                  | op :: rest -> getCubeInner rest ((cl |> List.hd |> op) :: cl)
              getCubeInner operations [startCube]
      

      这个模块只提供三种可能的90度旋转,一个旋转列表,每个可能的方向都有一个立方体,以及一个产生给定一个立方体的所有方向的函数。

      如果我这样做:

      cube(1, 2, 3, 4, 5, 6)
      |> Cube.getOrientations
      |> List.iter (printfn "%O")
      

      我明白了:

      Top: 3, Bottom: 4, Left: 1, Right: 2 Front: 6, Back: 5
      Top: 3, Bottom: 4, Left: 6, Right: 5 Front: 2, Back: 1
      Top: 3, Bottom: 4, Left: 2, Right: 1 Front: 5, Back: 6
      Top: 3, Bottom: 4, Left: 5, Right: 6 Front: 1, Back: 2
      Top: 6, Bottom: 5, Left: 3, Right: 4 Front: 1, Back: 2
      Top: 6, Bottom: 5, Left: 1, Right: 2 Front: 4, Back: 3
      Top: 6, Bottom: 5, Left: 4, Right: 3 Front: 2, Back: 1
      Top: 6, Bottom: 5, Left: 2, Right: 1 Front: 3, Back: 4
      Top: 2, Bottom: 1, Left: 5, Right: 6 Front: 3, Back: 4
      Top: 2, Bottom: 1, Left: 3, Right: 4 Front: 6, Back: 5
      Top: 2, Bottom: 1, Left: 6, Right: 5 Front: 4, Back: 3
      Top: 2, Bottom: 1, Left: 4, Right: 3 Front: 5, Back: 6
      Top: 4, Bottom: 3, Left: 1, Right: 2 Front: 5, Back: 6
      Top: 4, Bottom: 3, Left: 5, Right: 6 Front: 2, Back: 1
      Top: 4, Bottom: 3, Left: 2, Right: 1 Front: 6, Back: 5
      Top: 4, Bottom: 3, Left: 6, Right: 5 Front: 1, Back: 2
      Top: 5, Bottom: 6, Left: 4, Right: 3 Front: 1, Back: 2
      Top: 5, Bottom: 6, Left: 1, Right: 2 Front: 3, Back: 4
      Top: 5, Bottom: 6, Left: 3, Right: 4 Front: 2, Back: 1
      Top: 5, Bottom: 6, Left: 2, Right: 1 Front: 4, Back: 3
      Top: 1, Bottom: 2, Left: 5, Right: 6 Front: 4, Back: 3
      Top: 1, Bottom: 2, Left: 4, Right: 3 Front: 6, Back: 5
      Top: 1, Bottom: 2, Left: 6, Right: 5 Front: 3, Back: 4
      Top: 1, Bottom: 2, Left: 3, Right: 4 Front: 5, Back: 6
      

      这就是我想要的。但是Cube模块被大量的操作所占用。

      是否有更好的方法可以通过更少的操作或完全不同的方法来实现这一目标?

2 个答案:

答案 0 :(得分:2)

首先要考虑的是:如果你纵向旋转立方体,那么做其他操作,操作序列更加规则。 (它变成[长,正常,正常,正常]时间6)。你可以想象将它浓缩成4个操作的列表,特别是在一秒钟内。而且,在此列表的3次迭代之后,您最终得到了与之相同的多维数据集。

现在,考虑到你在旋转时看到的每个方向,也有一个“相反”的方向。 (IE:对于每个方向(U,D,L,R,F,B),都有相应的方向(D,U,L,R,B,F)。(想象一下你和外太空的朋友,每个上部 - 相对于另一个,以及你们每个人在立方体的两侧。)在前12次操作中的每一次([长 - 左,正常,正常,正常]时间3)之后,最终的三个方面你的“顶级”永远不会出现在“底层” - 也就是说,你看到的和你朋友看到的东西之间没有重叠。如果你的朋友也注意到了他看到的东西(如果你添加你的观点,请阅读你可以将转数减半。

所以(在伪代码中,因为我不知道F#):

ops = [rotateLongLeft, rotateNormalRight, rotateNormalRight, rotateNormalRight]
for each operation in [ops times 3]:
    do the operation
    add the orientation
    add its opposite

最后,你应该拥有所有24个方向。

答案 1 :(得分:2)

这可能稍微清洁一点:

let rec expand = function
| [] -> []
| (0,o)::l -> expand l
| (n,o)::l -> o::(expand ((n-1,o)::l))

let getOrientations startCube =
  expand [  
    3,rotateNormalRight; 1,rotateLongitudinalRight
    3,rotateNormalRight; 1,rotateLongitudinalRight
    3,rotateNormalRight; 1,rotateLongitudinalLeft
    3,rotateNormalRight; 1,rotateLongitudinalLeft
    3,rotateNormalRight; 1,rotateLongitudinalRight
    3,rotateNormalRight]
  |> List.scan (|>) startCube

expand函数获取重复/元素对的列表,并将其扩展为元素列表,这使得操作列表看起来更清晰(至少对我来说)。然后,我们可以使用内置的List.scan函数来遍历该列表,并将每个函数连续应用于上一个结果。