你会如何在代码中代表魔方?

时间:2009-02-01 04:44:29

标签: data-structures rubiks-cube

如果您正在开发解决Rubik立方体的软件,您将如何表示立方体?

11 个答案:

答案 0 :(得分:22)

这个ACM Paper描述了它用于表示rubik多维数据集的几种替代方法,并将它们与彼此进行比较。可悲的是,我没有帐户来获取全文,但描述说明了:

  

提出并比较了Rubik's Cube的七个替代表示:一个3乘3乘3的3位整数数组;一个6乘3乘3的文字阵列;一个5乘12的字面矩阵;一个ll-by-ll稀疏文字矩阵;一个54元素的向量;一个四维数组;和一个3乘3乘3的嵌套数组。 APL功能用于定向移动和四分之一圈以及几个用于解决立方体的有用工具。

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

答案 1 :(得分:11)

一种方法是专注于视觉外观。

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

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

然后每次移动都是一种置换一组特定彩色方块的方法。

答案 2 :(得分:11)

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

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

使用的内存量非常小,处理量非常小,以至于完全没有必要进行优化,尤其是当您牺牲代码可用性时。

答案 3 :(得分:7)

软件“Cube Explorer”使用了一种表示多维数据集的有趣方法。使用大量聪明的数学方法,该方法只能使用5个整数来表示多维数据集。作者在website上解释了他的计划背后的数学。根据作者的说法,该表示适用于实现快速求解器。

答案 4 :(得分:4)

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

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

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

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

我会选择3 x 18阵列。

答案 5 :(得分:4)

有20个立方体很重要。所以一种方法是作为一个包含20个字符串的数组。字符串将包含2或3个字符,表示颜色。任何一次移动都会影响7个立方体。所以你只需要为六个方面都重新制作。

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

顺便说一句,我帮助某人做了一次软件Rubik's cube,可能是15年前,但我不记得我们是如何代表它的。

答案 6 :(得分:1)

您可以将多维数据集想象为三个垂直的圆形链接列表,它们与三个水平链接列表相交。

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

看起来像这样:

struct cubeLinkedListNode {
    cubedLinkedListNode* nextVertical;
    cubedLinkedListNode* lastVertical;
    cubedLinkedListNode* nextHorizontal;
    cubedLinkedListNode* lastHorizontal;
    enum color;
}

你可能实际上并不需要2个'最后'指针。

[我用C做过这个,但是它可以用Java或C#来完成,只需使用一个简单的cubeLinkedListNode类,每个类都保存对其他节点的引用。 ]

请记住,有六个互锁的循环链表。 3纵3横。

对于每次旋转,您只需循环通过相应的圆形链表,顺序移动旋转圆的链接以及连接圆。

类似的东西,至少......

答案 7 :(得分:0)

其他人很好地描述了物理立方体,但是关于立方体的状态......我会尝试使用矢量变换数组来描述立方体的变化。这样,您可以在更改时保留rubiks多维数据集的历史记录。我想知道你是否可以将矢量乘以变换矩阵以找到最简单的解决方案?

答案 8 :(得分:0)

作为可以移动的48个面的排列。基本轮换也是排列,排列可以组成,它们组成一个组。

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

答案 9 :(得分:0)

我实现了Rubik的Cube程序,该程序使用OpenGL渲染立方体,并且它具有内置的求解器。求解器必须生成数百万个移动,并数百万次比较每个多维数据集状态,因此基础结构必须快速。我尝试了以下结构:

  • 三维字符数组,为6x3x3。脸部的颜色像cube [SIDE] [ROW] [COL]一样被索引。这很直观,但是很慢。
  • 一个由54个字符组成的数组。这是速度的提高,并且手动计算行和步幅(平凡)。
  • 6个64位整数。这种方法本质上是一个微调,从长远来看是最快的。

立方体的每一面都由9个贴纸组成,但中心是固定的,因此只需要存储8个。并且有6种颜色,因此每种颜色都适合一个字节。给定这些颜色定义:

enum class COLOR : uchar {WHITE, GREEN, RED, BLUE, ORANGE, YELLOW};

一张脸可能看起来像这样,以一个64位整数存储:

00000000 00000001 00000010 00000011 00000100 00000101 00000000 00000001

哪个解码为:

WGR
G B
WYO

使用此结构的一个优点是rolqrorq的按位运算符可用于移动面。滚动16位可实现90度旋转;滚动32位可获得180度的旋转。相邻的部分需要手动维护-即旋转顶面之后,也需要移动正面,左侧,背面和右侧的顶层。以这种方式转过脸真的非常快。例如,滚动

00000000 00000001 00000010 00000011 00000100 00000101 00000000 00000001

按16位收益率

00000000 00000001 00000000 00000001 00000010 00000011 00000100 00000101

经过解码,看起来像这样:

WGW
Y G
OBR

另一个优点是,在某些情况下,可以使用一些聪明的位掩码和标准整数比较来完成对多维数据集状态的比较。对于求解器而言,这可以大大提高速度。

无论如何,我的实现在github上:https://github.com/benbotto/rubiks-cube-cracker/tree/master/Model参见RubiksCubeModel.{h,cpp}

如前所述,该程序还呈现多维数据集。我对该部分使用了不同的结构。我定义了一个“立方体”类,该类是一个分别具有1个,2个或3个彩色面的正方形,分别用于中心,边和角部分。魔方就是由26个立方体组成的。使用四元数旋转面。 Cubies和多维数据集的代码在这里:https://github.com/benbotto/rubiks-cube-cracker/tree/master/Model/WorldObject

侧面说明:我的求解器不是最快的。它与Thistlethwaite算法相似,但我的目标是以类似于人的方式解决多维数据集,而不是以最少的可能动作来解决。我的答案是关于底层数据结构,而不是解决多维数据集。

答案 10 :(得分:0)

最短的表示是这样的:codepen.io/Omelyan/pen/BKmedK

多维数据集以1D数组(54个元素的向量)展开。几行旋转功能会根据立方体的对称性交换贴纸。这是C语言中完整的工作模型,我在2007年还是学生时就创建了该模型:

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);
}