简体   繁体   English

如何创建用于解决魔方的模式数据库?

[英]How to create a pattern database for solving Rubik's Cube?

I'm trying to implement Korf's algorithm for solving 3x3x3 Rubik's Cube.我正在尝试实现用于求解 3x3x3 魔方的Korf 算法 Part of the solution is to create a pattern database.部分解决方案是创建模式数据库。

This is quote from the paper that contains, literally, the whole information on how to do it:这是从论文中引用的,其中包含有关如何执行此操作的全部信息:

Using a breadth-first search from the goal state, we can enumerate these states, and record in a table the number of moves required to solve each combination of corner cubies.使用目标 state 的广度优先搜索,我们可以枚举这些状态,并在表格中记录解决每个角块组合所需的移动次数。

How do you transform this in code?你如何在代码中转换它? Since on each step, we have multiple goal states, it's not clear to me how we can just "enumerate" all states that are reachable from it.由于在每一步中,我们都有多个目标状态,我不清楚我们如何才能“枚举”所有可以从它到达的状态。

I've implemented Korf's algorithm, and you can use my code as a reference: https://github.com/benbotto/rubiks-cube-cracker/ It's a lot of code, too much to include in this post, but I can give some general algorithmic tips.我已经实现了Korf的算法,你可以参考我的代码: https://github.com/benbotto/rubiks-cube-cracker/代码很多,太多了,不能在这篇文章中包含,但我可以给出一些通用的算法技巧。

First off, Korf's paper recommends using three pattern databases, not just one.首先,Korf 的论文建议使用三个模式数据库,而不仅仅是一个。 One of the databases stores the number of moves required to solve the corner pieces of any cube.其中一个数据库存储解决任何立方体的角块所需的移动次数。 There are 8 corner cubies, and each can occupy any of the 8 positions, so there are 8. possible permutations, Each of the corner pieces can be oriented in 3 different ways--any of the three stickers can face up.有 8 个角块,每个可以占据 8 个位置中的任何一个,所以有 8 个可能的排列,每个角块可以以 3 种不同的方式定向——三个贴纸中的任何一个都可以正面朝上。 for example--but the orientations of 7 of the cubies dictate the orientation of the 8th (by the laws of the cube), Therefore.例如——但是 7 个立方体的方向决定了第 8 个立方体的方向(根据立方体的定律),因此。 there are 3^7 possible ways the corners can be orientated, Altogether then, there are 8, * 3^7 possible ways for the corners of the cube to be scrambled, and these 88.179,840 states can be iterated in a reasonable amount of time (30 minutes or so).有 3^7 种可能的方式来确定角的方向,那么总共有 8, * 3^7 种可能的方式可以对立方体的角进行加扰,并且可以以合理的数量迭代这 88.179,840 种状态时间(30 分钟左右)。 All corner states can be reached in 11 moves or fewer, so each entry in the corner pattern database can be stored in a nibble (4 bits).所有角点状态都可以在 11 次或更少的移动中达到,因此角点模式数据库中的每个条目都可以存储在一个半字节(4 位)中。 On disk, the corner pattern database occupies about 42MB.在磁盘上,角模式数据库占用大约 42MB。

A breadth-first search can be used to populate this database.广度优先搜索可用于填充此数据库。 Apply a move and use the state of the corners to create an index into the database.应用移动并使用角落的 state 在数据库中创建索引。 If the state has been seen before with fewer moves, then the search tree can be pruned: there's no reason to continue down the branch;如果之前已经看到 state 的移动次数较少,则可以修剪搜索树:没有理由继续沿着分支向下走; otherwise, add the state to the database and continue the search.否则,将 state 添加到数据库并继续搜索。 As mentioned above, iterating over all possible corner states doesn't take long on a modern computer due to the amount of pruning that can be done during the search.如上所述,在现代计算机上迭代所有可能的角状态并不需要很长时间,因为在搜索过程中可以进行大量修剪。

Korf suggests two additional databases: one for 6 of the 12 edges, and another for the other 6 edges. Korf 建议使用两个额外的数据库:一个用于 12 条边中的 6 条,另一个用于其他 6 条边。 Korf was on limited hardware (a Sun SPARC Ultra,). Korf 使用有限的硬件(Sun SPARC Ultra)。 but since I'm on a more modern machine I chose to use 7 edges in each of the edge databases.但由于我使用的是更现代的机器,因此我选择在每个边缘数据库中使用 7 个边缘。 This drastically speeds up the solver, Anyway, 7 edges can occupy 12 positions.这大大加快了求解器的速度,无论如何,7 条边可以占据 12 个位置。 so there are 12P7 (12, / (12-7).) permutations, Each corner can be oriented in 2 ways, so there are 2^7 possible orientations of 7 edges.所以有 12P7 (12, / (12-7).) 排列,每个角可以有 2 种方向,所以 7 条边有 2^7 种可能的方向。 Again, this is a small enough number of cube states to iterate over.同样,这是一个足够小的多维数据集状态来迭代。 and all states can be reached in 10 moves or fewer.并且所有状态都可以在 10 步或更少的时间内到达。 Storing each entry in a nibble, each of the 7-edge databases occupies about 244MB (12P7 * 2^7 / 2 bytes).将每个条目存储在一个半字节中,每个 7 边数据库占用大约 244MB(12P7 * 2^7 / 2 字节)。

I implemented breadth-first search using a non-recursive algorithm for efficiency reasons (and efficiency matters in this code).出于效率原因(并且此代码中的效率很重要),我使用非递归算法实现了广度优先搜索。 While this type of search worked fine for building the corner database, the memory costs were too high for indexing the edge databases.虽然这种类型的搜索可以很好地构建角落数据库,但 memory 的成本对于索引边缘数据库来说太高了。 As such I used a custom iterative-deepening depth-first search for indexing the edges.因此,我使用自定义迭代加深深度优先搜索来索引边缘。 The "custom" part is that it early-outs when it reaches a state that's already been encountered. “自定义”部分是它在到达已经遇到的 state 时提前退出。

The on-disk database sizes above of course assume that the database contains nothing but the number of moves to get to each state, each stored in a nibble.上面的磁盘数据库大小当然假设数据库只包含到达每个 state 的移动次数,每个都存储在一个半字节中。 That is, the database is a hash table, and each state is an index into the table.即数据库是一个 hash 表,每个 state 是该表的索引。 As such, you need a "perfect hash" algorithm that takes a permutation of cubes and returns an index.因此,您需要一个“完美哈希”算法,该算法采用多维数据集的排列并返回一个索引。 In his papers, and he has multiple papers on combination puzzles, Korf is rather terse about how to create a such a hash.在他的论文中,他有多篇关于组合谜题的论文,Korf 关于如何创建这样的 hash 相当简洁。 It boils down to calculating Lehmer codes .它归结为计算Lehmer 码 Korf gives a short and sweet linear algorithm in his paper, Large-Scale Parallel Breadth-First Search . Korf 在他的论文Large-Scale Parallel Breadth-First Search中给出了一个简短而实用的线性算法。

We scan the permutation from left to right, constructing a bit string of length n, indicating which elements of the permutation we've seen so far.我们从左到右扫描排列,构造一个长度为 n 的位串,指示到目前为止我们看到的排列的哪些元素。 Initially the string is all zeros.最初,字符串全为零。 As each element of the permutation is encountered, we use it as an index into the bit string and set the corresponding bit to one.当遇到排列的每个元素时,我们将其用作位串的索引并将相应的位设置为 1。 When we encounter element k in the permutation, to determine the number of elements less than k to its left, we need to know the number of ones in the first k bits of our bit string.当我们在排列中遇到元素 k 时,要确定其左侧小于 k 的元素数量,我们需要知道位串的前 k 位中 1 的数量。 We extract the first k bits by right shifting the string by n − k.我们通过将字符串右移 n-k 来提取前 k 位。 This reduces the problem to: given a bit string, count the number of one bits in it.这将问题简化为:给定一个位串,计算其中一位的数量。

We solve this problem in constant time by using the bit string as an index into a precomputed table, containing the number of ones in the binary representation of each index.我们通过使用位串作为预计算表的索引,在恒定时间内解决了这个问题,其中包含每个索引的二进制表示中的 1 的数量。

It took me a long time to digest that and transform it to code, especially because he does not talk about indexing partial permutations.我花了很长时间来消化它并将其转换为代码,特别是因为他没有谈论索引部分排列。 You'll need to index partial permutations when generating the pattern databases for the edge pieces because creating a database of all 12 edges would be incredibly large.在为边片生成模式数据库时,您需要对部分排列进行索引,因为创建包含所有 12 条边的数据库会非常大。 As such, I wrote an article about it on Medium: https://medium.com/@benjamin.botto/sequentially-indexing-permutations-a-linear-algorithm-for-computing-lexicographic-rank-a22220ffd6e3因此,我在 Medium 上写了一篇关于它的文章: https://medium.com/@benjamin.botto/sequentially-indexing-permutations-a-linear-algorithm-for-computing-lexicographic-rank-a22220ffd6e3

Lastly, I tested a number of different data structures for storing the cube.最后,我测试了许多用于存储多维数据集的不同数据结构。 In my code I have multiple solvers (Korf and Thisthlewaite), plus a graphical representation.在我的代码中,我有多个求解器(Korf 和 Thisthlewaite),以及一个图形表示。 I actually store the cube in 4 different structures.我实际上将立方体存储在 4 种不同的结构中。 With an algorithm like Korf's, the structure that you use to represent the Rubik's Cube has an enormous (extra emphatic.) impact on the speed of the solver.使用像 Korf 的算法,用于表示魔方的结构对求解器的速度有巨大(特别强调)的影响。 I wrote about different structures in another post , and option (4) is by far the fastest with the Korf algorithm in my testing.我在另一篇文章中写过不同的结构,在我的测试中,选项 (4) 是迄今为止最快的 Korf 算法。 To create a single database entry, you need the indexes and orientations of each cube (eg {0-7, 0-2} for the corners).要创建单个数据库条目,您需要每个立方体的索引和方向(例如,角的 {0-7, 0-2})。 So, when creating pattern databases, it's efficient to represent the cube as indexes and orientations so that no additional processing is required to compute them.因此,在创建模式数据库时,将立方体表示为索引和方向是有效的,因此不需要额外的处理来计算它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM