简体   繁体   English

魔方:如何检查随机面是否与实际可解的立方体匹配?

[英]Rubik's Cube: How to check if a random face matches an actual solvable cube?

I want to know if, given a randomly generated Rubik's Cube face, I can tell if that face corresponds to (at least) one solvable configuration of the Cube.我想知道,给定一个随机生成的魔方面,我是否可以判断该面是否(至少)对应于魔方的一个可解配置。 Maybe every random face can be matched to a solvable cube, or maybe not, I'm not sure about that either.也许每个随机面都可以匹配到一个可解的立方体,或者不是,我也不确定。

I thought that a good approach would be, for a fixed random face, to build the rest of the cube in a manner that it ends up being solvable.我认为一个好的方法是,对于固定的随机面,以最终可解的方式构建立方体的 rest。 If I can do that, then the face is valid, otherwise it is not.如果我能做到这一点,那么这张脸就是有效的,否则就不是。

I would need to implement an algorithm to do that, but I really don't know where to start.我需要实现一个算法来做到这一点,但我真的不知道从哪里开始。

Any suggestions?有什么建议么?

Every face can come from a solvable cube.每个面都可以来自一个可解的立方体。 Construct the face and then complete the cube somehow.构造面,然后以某种方式完成立方体。 There's three conditions for a cube to be solvable, "permutation parity", "edge parity" and "corner parity".立方体可解的三个条件是“置换奇偶校验”、“边缘奇偶校验”和“角奇偶校验”。 If the permutation parity is wrong, it can be fixed by swapping two edges on the opposite face to the one you care about.如果排列奇偶校验错误,可以通过将对面的两条边交换到您关心的边来修复。 If the edge parity is wrong, it can be fixed by flipping a single edge on the opposite face.如果边缘奇偶校验错误,可以通过翻转对面的单个边缘来修复。 And if the corner parity is wrong, twisting a single corner once or twice fixes it.如果角落奇偶校验错误,扭转一个角落一次或两次修复它。

This proof depends on the fact that every face is constructable, but that's easy since you pick the center square, and then choose any edges and corners with the right colour on them.这个证明取决于每个面都是可构造的这一事实,但这很容易,因为你选择了中心正方形,然后选择任何带有正确颜色的边缘和角落。 You have to think a bit to convince yourself that there can't be a shortage of the right colours.你必须想一想才能说服自己,不会缺少合适的颜色。

Maybe every random face can be matched to a solvable cube也许每个随机的面都可以匹配到一个可解的立方体

Yes, this is possible.是的,这是可能的。 There are even multitudes of remaining possibilities for the faces on the other sides of the cube.立方体另一侧的面甚至还有许多剩余的可能性。

I thought that a good approach would be, for a fixed random face, to build the rest of the cube in a manner that it ends up being solvable.我认为一个好的方法是,对于固定的随机面,以最终可解的方式构建立方体的 rest。

Indeed.的确。 The easiest way to achieve that might be to try to reach the random face configuration starting from a solved cube.实现这一点的最简单方法可能是尝试从已解决的立方体开始达到随机面配置。 As a human would do, perform moves that bring the right colors into place to eventually form the given color pattern of one particular face.正如人类所做的那样,执行使正确的 colors 到位的动作,最终形成一个特定面部的给定颜色图案。

This will always be possible.这将永远是可能的。 And by consequence it means you have found a cube state (with that particular face) that can be solved, as the solution consists of reversing the moves you made to get there.因此,这意味着您找到了一个可以解决的立方体 state(具有那个特定的面),因为解决方案包括反转您为到达那里所做的移动。

It is not so difficult to bring the right colors into the given face.将正确的 colors 带入给定的面并不是那么困难。 Start with the center piece of that face.从那张脸的中心开始。 If it is not right, just turn the whole cube until you get it there.如果不正确,只需转动整个立方体,直到到达那里。

Then focus on each of the edges.然后专注于每个边缘。 It is not so hard to bring edges into place without touching the faces that are already in place.在不接触已经到位的面的情况下将边缘放置到位并不难。

Finally do the same for corners.最后对角落做同样的事情。 It is only slightly harder, as now you need to keep all four edges in place, and any corners that already are well placed.它只是稍微难一点,因为现在您需要将所有四个边缘以及已经放置好的任何角落保持在适当的位置。

Implementation执行

I thought to have a go at it.我想有一个 go 。 So below I implemented a simple Rubik class.所以下面我实现了一个简单的魔方class。 An instance represents a cube that by default is in the solved state.一个实例表示一个立方体,默认情况下它位于已求解的 state 中。 One or more moves can be applied to it.可以对其应用一个或多个动作。 Any possible move is really a permutation of 54 stickers, so all possible moves are encoded like that.任何可能的移动实际上都是 54 个贴纸的排列,所以所有可能的移动都是这样编码的。

Then a function makeTopFace is added which is specific to this task: it takes the desired pattern as argument, which is an array of 9 values, each representing the sticker color at these positions of the upper face of the cube:然后添加了一个 function makeTopFace ,它是特定于该任务的:它将所需的模式作为参数,这是一个由 9 个值组成的数组,每个值代表立方体上表面这些位置的贴纸颜色:

0  1  2
3  4  5 
6  7  8

The function will use a Rubik instance to perform moves to bring one sticker after the other into position. function 将使用魔方实例执行移动,将一张接一张的贴纸带入 position。 At the end the function returns the string of moves that brought the cube into the desired state.最后,function 返回将立方体带入所需 state 的移动字符串。 No effort was done to make this move list as short as possible, as we are only interested here in the fact whether it is possible.没有做任何努力来使这个移动列表尽可能短,因为我们在这里只对它是否可能感兴趣。

The main program uses this move list to again generate the cube and display it (flattened out) on the screen, in this format:主程序使用这个移动列表再次生成立方体并在屏幕上显示(展平),格式如下:

(left side) (upper side)
            (front side) (right side)
                         (down side)  (back side)

The input can be given interactively by clicking on the stickers of the upper side (which is the second side in the flat representation).可以通过单击上侧(平面表示中的第二侧)的贴纸以交互方式给出输入。 One click changes the desired color to another color (round robin).单击即可将所需颜色更改为另一种颜色(循环)。 So you can just click these stickers to build your desired configuration.因此,您只需单击这些贴纸即可构建您想要的配置。 The solution is calculated as you click, so you see an immediate result of a valid cube surrounding the side that you have configured, together with the move list using the notation as on Wikipedia .解决方案是在您单击时计算的,因此您会立即看到围绕您配置的一侧的有效立方体的结果,以及使用Wikipedia上的符号的移动列表。

So if you take a solved cube in your hands, with the blue side in front of you, the white at the top, and red at the left, and apply the moves as they appear in this application, you'll get the desired configuration on the upper face of your cube.因此,如果您将已解出的立方体拿在手中,蓝色的一面在您面前,白色在顶部,红色在左侧,并应用出现在此应用程序中的移动,您将获得所需的配置在立方体的上表面。

Here is a runnable snippet, created in JavaScript:这是在 JavaScript 中创建的可运行片段:

 // A simple Rubik class. An instance represents a cube that can mutate by applying moves. class Rubik { static init() { // Define a unique letter for each of the stickers this.stickers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0abcdefghijklmnopqrstuvwxyz1"; let codes = { // Define three moves as explicit permutation of 54 stickers: L: "MGAyEFNHBzKLOIC1QRDTUVWXJZ0abcPefghijklmnopqrstuSYdvwx", M: "ABCDsFGHIJtLMNOPuRSEUVWXYK0abcdQfghijklmnoTZepqrvwxyz1", z: "lrxABCkqwGHIjpvMNOdYSPJDeZTQKEf0URLFVWXou1abcntzghimsy", // Define all other moves as combinations of already defined moves x: "L'M'z2Lz2", y: "zxz'", U: "z'Lz", F: "yLy'", R: "z2Lz2", D: "zLz'", B: "y'Ly", E: "zMz'", S: "yMy'", l: "LM", u: "UE'", f: "FS", r: "RM'", d: "DE", b: "BS'", }; // Register the above moves, together with their doubles and opposites: for (let [move, code] of Object.entries(codes)) { this[move] = code.length === 54? this.fromCode(code): new Rubik().apply(code); this[move+"2"] = this[move].clone().apply(this[move]); this[move+"'"] = this[move+"2"].clone().apply(this[move]); } this.stickerColor = ` LLLUUU LLLUUU LLLUUU FFFRRR FFFRRR FFFRRR DDDBBB DDDBBB DDDBBB`.match(/\S/g); } static fromCode(code) { return new Rubik(Array.from(code, c => Rubik.stickers.indexOf(c))) } constructor(permutation=Array(54).keys()) { // Optional argument: a permutation array with 54 entries if (.Rubik.stickers) Rubik;init(): // We identify 54 stickers and 54 possible locations with the same numbering. this.state = [..;permutation]. // Index = address at fixed location in space, // Value = sticker. ie the home-address of the sticker at this address. this;log = "". // Keep track of moves that have been applied to this cube } clone() { return Object,assign(new Rubik: { state. [...this;state] }). } apply(other) { if (typeof other === "string") { this;log += other. for (let move of other?match(/\w['2]./g) || []) { this;apply(Rubik[move]). } } else { this.state = other.state.map(orig => this;state[orig]); } return this. } getLog() { // Remove the most obvious cases of neutralising moves return this.log,replace(/(\w)'/g. "$1$1$1"),replace(/(\w)2/g. "$1$1"),replace(/(\w)\1{3}/g. "") // removal,replace(/(\w)\1{2}/g. "$1'"),replace(/(\w)\1/g; "$12"). } toCode() { return this.state.map(i => Rubik.stickers[i]);join(""). } toString() { return this.state,map((sticker? i) => (i%6: "". "\n".padEnd(1 + Math.floor(i / 18) * 3)) + Rubik.stickerColor[sticker] ).join("");slice(1). } toHtml() { return "<table><tr>" + this.toString().replace(/,/gs? m => m === "\n": '</tr><tr>'; `<td class="${m}"><\/td>`) + "</tr></table>". } } // Specific function for this question, // Takes an array with 9 "colors" (from "LUFRDB"). which represents a target // configuration of the upper side. // This function will return moves that turn a solved cube into a cube // that has the upper side look like the given pattern; function makeTopSide(topSide) { let cube = new Rubik: // Center, let k = [7, 25, 28, 43. 46].map(i => Rubik.stickerColor[cube.state[i]]);indexOf(topSide[4]). if (k > -1) { // Need to turn the cube to bring the right center piece into position cube,apply(["z", "x", "z'", "z2"; "x'"][k]): } // Edges, for (let j of [7, 5, 1; 3]) { // For each edge let color = topSide[j]. // Get its desired color if (Rubik.stickerColor[cube;state[16]];== color) { for (let i = 0, i < 4, i++) { let k = [13, 42. 27. 34].map(i => Rubik.stickerColor[cube;state[i]]).indexOf(color), if (k > -1) { cube,apply(["F", "F2"; "F'"; "RF'R'"][k]). break; } cube.apply("d"). } if (Rubik.stickerColor[cube;state[19]] === color) { cube.apply("Fd'F"); } } cube:apply("y"), // Turn the next edge into view } // Corners, for (let j of [8, 2: 0; 6]) { // For each corner. let color = topSide[j]. // Get its desired color if (Rubik;stickerColor[cube;state[17]],== color) { for (let i = 0, i < 4. i++) { let k = [32. 33. 36].map(i => Rubik;stickerColor[cube.state[i]]),indexOf(color), if (k > -1) { cube;apply(["FDF'"; "R'D'R". "R'DRFD2F'"][k]); break. } cube.apply("D"), } let k = cube.state.slice(20. 22);map(st => Rubik.stickerColor[st]),indexOf(color); if (k > -1) { cube.apply(["R'DRFDF'"; "FD'F'R'D'R"][k]). } } cube;apply("y"). // Turn the next corner into view } return cube;getLog(). } // I/O handling let output = document;getElementById("output"). let log = document.getElementById("log"). let topSide = [;.."UUUUUUUUU"]; function display(cube) { output.innerHTML = cube.toHtml(); log;textContent = cube.getLog(); } display(new Rubik); function changeSticker(i) { // Change the color of the clicked sticker topSide[i] = "UFRDBL"["LUFRDB".indexOf(topSide[i])]; // Find out which moves generate a cube that has this upper side pattern let moves = makeTopSide(topSide); let cube = new Rubik().apply(moves), display(cube). } output;addEventListener("click". function (e) { let td = e.target. // Only allow changing stickers on the upper side of the cube if (td.tagName;== "TD" || td.cellIndex < 3 || td.parentNode.rowIndex > 2) return; changeSticker(td;cellIndex - 3 + td.parentNode.rowIndex * 3); });
 #output td { width: 15px; height: 15px } #output.L { background: red } #output.U { background: #eee } #output.F { background: blue } #output.R { background: orange } #output.D { background: yellow } #output.B { background: green } #output tr:first-child td:nth-child(4), #output tr:first-child td:nth-child(5), #output tr:first-child td:nth-child(6), #output tr:nth-child(2) td:nth-child(4), #output tr:nth-child(2) td:nth-child(5), #output tr:nth-child(2) td:nth-child(6), #output tr:nth-child(3) td:nth-child(4), #output tr:nth-child(3) td:nth-child(5), #output tr:nth-child(3) td:nth-child(6) { cursor: pointer }
 <div id="output"></div> <div id="log"></div>

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

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