Data structures 你会如何表现一个魔方';代码中的立方体是什么?

Data structures 你会如何表现一个魔方';代码中的立方体是什么?,data-structures,rubiks-cube,Data Structures,Rubiks Cube,如果你正在开发软件来解魔方,你会如何表示魔方?有很多方法可以做到这一点。有些方法比其他方法更有效地利用内存 我见过人们使用3 x 3 x 3长方体对象数组,其中长方体对象需要存储颜色信息(是的,从未使用过中心对象)。我见过人们使用6个数组,每个数组都是3 x 3的长方体数组。我看到了一个3 x 18的长方体阵列。有很多可能性 可能更大的问题是如何表示各种转换。旋转物理立方体的单个面(所有立方体移动本质上都是单个面的旋转)必须通过围绕大量长方体对象交换来表示 您的选择应该对您正在编写的任何应用程序

如果你正在开发软件来解魔方,你会如何表示魔方?

有很多方法可以做到这一点。有些方法比其他方法更有效地利用内存

我见过人们使用3 x 3 x 3长方体对象数组,其中长方体对象需要存储颜色信息(是的,从未使用过中心对象)。我见过人们使用6个数组,每个数组都是3 x 3的长方体数组。我看到了一个3 x 18的长方体阵列。有很多可能性

可能更大的问题是如何表示各种转换。旋转物理立方体的单个面(所有立方体移动本质上都是单个面的旋转)必须通过围绕大量长方体对象交换来表示

您的选择应该对您正在编写的任何应用程序都有意义。可能您只是在渲染多维数据集。可能是没有用户界面。你可能正在解立方体


我会选择3 x 18阵列。

一种方法是关注视觉外观

立方体有六个面,每个面是三乘三的正方形阵列。所以

Color[][][] rubik = new Color[6][3][3];

然后,每次移动都是一种方法,可以排列一组特定的彩色正方形。

您可以将立方体想象为三个垂直圆形链表,它们与三个水平链表相交

每当立方体的某一行旋转时,您只需旋转相应的指针

它看起来是这样的:

struct cubeLinkedListNode {
    cubedLinkedListNode* nextVertical;
    cubedLinkedListNode* lastVertical;
    cubedLinkedListNode* nextHorizontal;
    cubedLinkedListNode* lastHorizontal;
    enum color;
}
WGW
Y G
OBR
实际上,您可能不需要2个“最后”指针

[我用C实现了这一点,但也可以用Java或C实现#只需为cubeLinkedListNode使用一个简单的类,每个类都包含对其他节点的引用。]

记住,有六个互锁的循环链表。3垂直3水平

对于每个旋转,您只需在相应的循环链接列表中循环,顺序移动旋转圆以及连接圆的链接


像这样的东西,至少…

有20个立方很重要。一种方法是将其作为20个字符串的数组。字符串将包含指示颜色的2或3个字符。任何一次移动都会影响7个立方体。因此,您只需要为六个面中的每一面重新映射

注意:此解决方案无法记住位于白色中心的徽标标签的方向

顺便说一句,大概15年前,我曾帮助某人制作过一个软件魔方,但我不记得我们是如何表示它的。

本文介绍了几种用于表示魔方的替代方法,并将它们相互比较。遗憾的是,我没有获得全文的账户,但描述中指出:

提出并比较了七种不同的魔方表示法:3位整数的3×3×3数组;6×3×3的文字数组;5×12的文字矩阵;逐ll稀疏文字矩阵;54个元素的向量;四维数组;和一个3乘3乘3的嵌套数组。APL函数用于方向移动和四分之一转弯,以及一些用于求解立方体的有用工具


此外,该文件包含一个非常干净的表示,以及用于旋转节的相关代码(如果您正在寻找实际代码)。它使用了一个单元格和面数组。

其他的描述了物理立方体,但是关于立方体的状态。。。我会尝试使用向量变换数组来描述立方体的变化。这样,您可以在进行更改时保留rubiks多维数据集的历史记录。我想知道你能不能把向量乘成一个变换矩阵,找到最简单的解?

作为48个可以移动的面的排列。基本旋转也是置换,置换可以组合,它们形成一个群


在一个程序中,这种排列将由包含数字0到47的48个元素的数组表示。与数字对应的颜色是固定的,因此可以从排列中计算视觉表示,反之亦然。

软件“多维数据集浏览器”使用了一种有趣的表示多维数据集的方法。使用许多聪明的数学,这种方法只能用5个整数来表示立方体。作者在他的电脑上解释了他的程序背后的数学原理。根据作者的说法,该表示法适用于实现快速求解器。

避免优化;让它成为面向对象的。我使用的伪代码类大纲是:

class Square
    + name : string
    + accronym : string

class Row
    + left_square : square
    + center_square : square
    + right_square : square

class Face
    + top_row : list of 3 square
    + center_row : list of 3 square
    + bottom_row : list of 3 square

    + rotate(counter_clockwise : boolean) : nothing

class Cube
    + back_face : face
    + left_face : face
    + top_face : face
    + right_face : face
    + front_face : face
    + bottom_face : face

    - rotate_face(cube_face : face, counter_clockwise : boolean) : nothing

使用的内存量非常小,处理量非常小,因此完全不需要进行优化,尤其是在牺牲代码可用性的情况下。

简单的回答是,这取决于如何解决多维数据集。如果您的解算器将使用人工方法,如逐层方法或Fridrich方法,那么底层数据结构将不会有太大的区别。计算机可以用人类的方法在很短的时间内(不到一秒钟)解出一个立方体,即使是用最慢的编程语言。但是,如果你打算使用计算量更大的方法来求解立方体,比如Thistlethwaite的52-move算法、Reid的29-move算法或Korf的20-move算法,那么数据结构和编程语言是至关重要的

我实现了一个使用OpenGL渲染立方体的魔方程序,它内置了两种不同类型的解算器(Thistlethwaite和Korf)。解算器必须生成数十亿次移动,并对每个立方体状态进行数十亿次比较,因此底层结构必须快速。我尝试了以下结构:

  • 三维字符数组,6x3x3。面颜色的索引类似于立方体[侧面][行][列]。这是直观的,但很慢
  • 由54个字符组成的单个数组。这比(1)快,并且行和步幅是手动计算的(微不足道)
  • 6个64位整数。
    00000000 00000001 00000010 00000011 00000100 00000101 00000000 00000001
    
    WGR
    G B
    WYO
    
    00000000 00000001 00000010 00000011 00000100 00000101 00000000 00000001
    
    00000000 00000001 00000000 00000001 00000010 00000011 00000100 00000101
    
    WGW
    Y G
    OBR
    
    0  1  2  3  4  5  6  7  8  9  10 11 // Index.
    UB UR UF UL FR FL BL BR DF DL DB DR // Position (up-back, ..., down-right).
    RY RG RW RB WG WB YB YG OW OB OY OG // Colors (red-yellow, ..., orange-green).
    
    0   1   2   3   4   5   6   7
    ULB URB URF ULF DLF DLB DRB DRF
    RBY RGY RGW RBW OBW OBY OGY OGW
    
    const byte // symmetry
      M[] = {2,4,3,5},
      I[] = {2,0,4,6};
    
    byte cube[55]; // 0,0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,1, ... need to be filled first
    
    #define m9(f, m) (m6(f, m)*9)
    
    byte m6(byte f, byte m) {return ((f&~1)+M[m+(f&1)*(3-2*m)])%6;}
    
    void swap(byte a, byte b, byte n) {
      while (n--) {byte t=cube[a+n]; cube[a+n]=cube[b+n]; cube[b+n]=t;}
    }
    
    void rotate(byte f, byte a) { // where f is face, and a is number of 90 degree turns
      int c=m9(f, 3), i;
      swap(c, c+8, 1);
      while (a--%4) for (i=2; i>=0; --i)
        swap(m9(f, i) + I[i], m9(f, i+1) + I[i+1], 3),
        swap(f*9+i*2, f*9+i*2+2, 2);
      swap(c, c+8, 1);
    }