Есть ли лучший способ вычислить все возможные ориентации куба в F #? - PullRequest
5 голосов
/ 05 августа 2010

У меня есть тип с именем куб, который представляет физический куб. Я написал некоторый код, который берет куб и генерирует список всех возможных ориентаций куба.

Я использовал следующую терминологию, предполагая, что куб находится передо мной на уровне глаз.

Для граней куба:

  1. Верхняя часть обращена к потолку.
  2. Дно обращено к столу.
  3. Передняя часть от меня.
  4. Спина обращена ко мне.
  5. Левый лицом к стене слева от меня.
  6. Право обращено к стене справа от меня.

Для осей куб можно вращать вокруг:

  1. Нормальная ось тянется от стола к потолку.
  2. Продольная ось тянется от меня к стене передо мной.
  3. Боковая ось вытянута от левой стены к правой стене.

В то время как каждая из 6 граней остается обращенной вниз, куб можно вращать вокруг своей нормальной оси 4 различными способами (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 ]

2 голосов
/ 05 августа 2010

Это может быть немного чище:

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, чтобы просмотреть этот список и последовательно применить каждую функцию к предыдущему результату.

2 голосов
/ 05 августа 2010

С одной стороны: учтите, что если вы вращаете куб в продольном направлении, а затем выполняете другие операции, последовательность операций становится более регулярной. (Превращается в [длительное, нормальное, нормальное, нормальное] время 6). Вы можете представить это в виде 4 операций, особенно за секунду. Более того, после трех итераций этого списка вы получите тот же куб, с которого начинали.

Теперь учтите, что для каждой ориентации, которую вы видите во время вращения, есть также "противоположная" ориентация. (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 ориентации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...