Answers:
一个普通的旧大小数组[6X][X]
怎么了?您不需要了解内部迷你立方体,因为您看不到它们。它们不是多维数据集状态的一部分。将两个丑陋的方法隐藏在一个漂亮且易于使用的界面后面,对其进行单元测试,然后瞧,您完成了!
As long as you know how the six surfaces are "threaded" together
这正是更强大的数据结构将为您提供的。我认为我们正在为同一件事争论。一个数组边,一个边是块的数组,但是,关于边和块有很多有趣的属性,它们有助于弄清楚“线程” 并不是很喜欢该术语,因为它可能与多线程混淆; )
应当指出,我是一个狂热的速度立方体,但我从未尝试以编程方式在算法或数据结构中表示Rubik立方体。
我可能会创建单独的数据结构来捕获多维数据集中每个块的独特方面。
多维数据集上有3种不同类型的块:
角块-它具有三个彩色面和三个相邻的块,可以随时与它们共享一个侧面。
边缘块-它具有两个彩色面,并具有4个相邻的块,可以随时与该块共享一侧。在3x3块中,它始终具有2个中心块和2个角块。
中心块-在3x3立方体中,该块不可移动,但可以旋转。它将始终具有4个相邻的边块。在较大的立方体中,有多个中心块可以与另一个中心块或边缘块共享。中心块永远不会与角块相邻。
知道了这一点,一个块可以具有其所接触的其他块的引用列表。我将保留另一个列表列表,这将是代表单个多维数据集面的块列表和保留对每个多维数据集面的引用的列表。
每个立方体面都将表示为唯一面。
使用这些数据结构,编写一种算法将非常容易,该算法将在每个面上执行旋转转换,将适当的块移入或移出适当的列表。
编辑:重要说明,这些列表当然必须排序,但我忘了提了。 例如,如果我翻转右侧,则左上角右侧块将移动到右侧的右角并顺时针旋转。
list of lists
。最好只包含可以查询的无序块列表。并且在执行转换时只需更新相邻的块引用。如果要获取一个面中所有块的列表,则可以查询列表中与中心块相邻的所有块,对吗?
当我想到这个问题时,我想到的是一个静态立方体,其颜色以已知模式在其上移动。所以....
多维数据集对象包含6个侧面对象,这些对象保持固定索引0-5。每侧包含9个位置对象,这些对象保持固定索引0-8。每个位置包含一种颜色。
为简单起见,以四分之一圈为增量处理每个动作。共有3个旋转轴,每个旋转轴有2个可能的方向,用于在多维数据集上总共进行6个可能的动作。有了这些信息,在多维数据集上映射6个可能的动作就变得相当简单。
因此,取决于所采取的措施,第6位置3的绿色可能会移动到第1位置3或第2位置7。我还没有进行足够的探索来找到任何数学翻译,但是可能会出现一些可以在代码中利用的模式。
使用数据结构,我如何知道处于特定状态的某个多维数据集是否可解?我本人一直在努力解决这个问题,还没有找到答案。
为此,请不要以随机立方体状态开始。而是从已解决状态开始,然后以编程方式执行n个操作以使多维数据集进入随机的开始状态。由于您仅采取法律措施才能达到当前状态,因此多维数据集必须是可解的。
我发现xyz坐标系是解决Rubik立方体的一种简单方法,而旋转矩阵是一种实现旋转的简单,通用的方法。
我创建了一个包含位置向量的Piece类(x, y, z)
。可以通过将旋转矩阵应用于其位置(矩阵-矢量乘法)来旋转片段。该部件还将其颜色记录在一个元组中(cx, cy, cz)
,使颜色沿每个轴指向。逻辑少量确保这些颜色旋转期间适当地更新:在XY平面内旋转90度意味着我们将交换的值cx
和cy
。
因为所有旋转逻辑都封装在Piece类中,所以Cube可以存储无序的Pieces列表,并且可以以通用方式进行旋转。要旋转左侧,请选择x坐标为-1的所有片段,并将适当的旋转矩阵应用于每个片段。要旋转整个立方体,请对每个零件应用相同的旋转矩阵。
这个实现很简单,有很多好处:
(-1, 1, 1)
)中不包含零,边的正好具有一个零((1, 0, -1)
),而中心片具有两个零((-1, 0, 0)
)。缺点:
您可以使用一个简单的数组(每个元素都有1到1的映射到面上的正方形的元素),并以一定的排列模拟每次旋转
您仅需3个基本排列就可以逃脱:通过轴旋转一个切面穿过正面,围绕垂直轴旋转多维数据集,并通过左右表面在水平轴上方旋转多维数据集。其他所有动作都可以通过这三个动作的串联来表示。
如果您最终得到2个已互换位置的边,单个翻转边,单个翻转角或2个交换角,您有一个不可解的多维数据集
the most straightforward way of know whether a cube is solvable is to solve it
。好吧,使用该模型,您建议我认为是正确的。但是,如果您使用更接近@maple_shaft的模型并跟踪旋转,则可以通过验证边缘翻转mod 2的总和为0以及拐角旋转mod 3的总和为0,来快速测试3x3x3立方体是否可求解。计算边缘掉期和边角掉期(需要重新求解),它们的总和mod 2必须为0(总奇偶校验)。这些是证明立方体可解决的必要和充分的测试。
可解决的第一个条件是存在每个零件,并且可以使用每个零件上的颜色来组装“已保存”的立方体。这是一个相对琐碎的条件,可以通过一个简单的清单确定其真相。定义了“标准”立方体上的配色方案,但是即使您不处理标准立方体,也只有6种!求解面的可能组合。
一旦所有部件和颜色正确,那么确定是否可以解决任何给定的物理配置就成为问题。并非所有人。最简单的检查方法是运行多维数据集求解算法,看看它是否以已解决的多维数据集终止。我不知道是否有花哨的组合技术来确定可溶性,而无需实际尝试求解立方体。
至于什么数据结构...几乎没有关系。棘手的部分是正确进行转换,并能够以一种使您能够巧妙地使用文献中可用算法的方式表示多维数据集状态。如枫木轴所示,有三种类型的零件。关于魔方解题的文献总是按类型来指代棋子。转换也以常见方式表示(查找Singmaster表示法)。另外,我见过的所有解决方案都始终将一个零件作为参考点(通常将白色的中间零件放在底部)。
由于您已经收到了不错的答案,所以让我添加一个细节。
不管您的具体表示方式如何,都请注意,镜头是“放大”立方体各个部分的绝佳工具。例如,看一下函数cycleLeft
在这个Haskell代码。这是一个通用函数,可循环排列任何长度为4的列表。执行L移动的代码如下所示:
moveL :: Aut (RubiksCube a)
moveL =
cong cube $ cong leftCols cycleLeft
. cong leftSide rotateSideCW
因此cycleLeft
操作上由下式给出的视图 leftCols
。类似地,,rotateSideCW
它是将其旋转版本的一侧支撑的通用函数,在给出的视图上运行leftSide
。其他举动可以类似的方式实现。
Haskell库的目标是创建漂亮的图片。我认为它成功了:
我喜欢@maple_shaft的想法,以不同的方式表示不同的块(微型立方体):中心块,边块和角块分别带有1、2或3种颜色。
我将它们之间的关系表示为(双向)图,其边缘连接相邻的零件。每块都将有一个用于边缘(连接)的插槽阵列:中央块有4个插槽,边缘块有4个插槽,角块有3个插槽。替代地,中心件可以分别具有到边缘件的4个连接和角件的4个连接,和/或边缘件可以分别具有2个到中心件的连接和2个与角件的连接。
这些数组是有序的,以便在图边缘上进行迭代始终表示“相同”旋转,以立方体的旋转为模。也就是说,例如对于一个中心块,如果旋转多维数据集以使其表面在顶部,则连接顺序始终是顺时针方向。对于边角件也是如此。该属性在面部旋转后保持不变(或者现在看来如此)。
如果希望很容易的话,也可以很容易地检测出明显无法解决的条件(交换/翻转的边角,调换的角),因为找到特定类型和方向的零件很简单。
节点和指针怎么样?
假设总有6个面,并且1个节点代表1个面的1个正方形:
r , g , b
r , g , b
r , g , b
| | |
r , g , b - r , g , b
r , g , b - r , g , b
r , g , b - r , g , b
节点旁边有一个指向每个节点的指针。在这种情况下,圆旋转仅将指针(节点数/面数)-1个节点移到1个节点上。由于所有旋转都是圆旋转,因此只需构建一个rotate
函数。它是递归的,将每个节点移动一个空间,并检查是否已经足够移动它们,因为它将收集到节点的数量,并且始终有四个面。如果不是,请增加移动值的次数并再次调用旋转。
不要忘记它是双重链接的,所以也要更新新指向的节点。总会移动高度*宽度的节点数,每个节点更新一个指针,因此应该更新高度*宽度* 2的指针数。
由于所有节点都指向彼此,因此只需走一圈就可以更新每个节点。
这适用于任何大小的多维数据集,而没有边缘情况或复杂的逻辑。只是指针的走动/更新。
从个人经验来看,使用一套工具来跟踪立方体的每个旋转部分效果很好。每个子立方体分为三组,没有魔方立方体的大小。因此,要在魔方的某个位置找到子立方体,只需将三个集合相交即可(结果是一个子立方体)。要进行移动,请从移动中涉及的子集中移除受影响的子幼崽,然后将它们放回作为移动结果的子集中。
4×4立方体将有12套。围绕立方体的6个面6组,六个带6组。每个面都有16个子立方体,每个带都有12个子立方体。共有56个子立方体。每个子多维数据集都保存有关颜色和颜色方向的信息。魔方立方体本身是4×4×4的阵列,每个元素的信息都由3个集合组成,这些3个集合定义了该位置的子立方体。
与其他11个答案不同,此数据结构让您使用集合的交集定义多维数据集中每个子块的位置。这样省去了在进行更改时必须更新Near子块的工作。