熄灭游戏算法

时间:2013-11-05 18:20:42

标签: algorithm backtracking

这是一个功课。我必须使用回溯描述来设计和点亮游戏。

游戏由一个5乘5的灯组成;当游戏开始时,一组这些灯(随机的,或一组存储的拼图模式中的一个)被打开。按下其中一个灯将打开和关闭它旁边的四个灯。 (对角线邻居不受影响。)游戏提供了一个难题:给定一些初始配置,其中一些灯亮,一些灯关闭,目标是关闭所有灯,最好是 尽可能少按按钮。

我的方法是1到25,检查所有灯是否都关闭。如果没有,那么我将检查1到24,依此类推,直到我达到1或找到解决方案。不,如果没有解决方案,那么我将从2到24开始并按照上述过程直到我达到2或找到解决方案。

但是通过这个我没有得到结果?例如,在(0,0)(1,1)(2,2)(3,3)(4,4)处的光是ON?

如果有人需要代码我可以发布。

任何人都能告诉我使用回溯来解决这个游戏的正确方法吗?

感谢。

5 个答案:

答案 0 :(得分:8)

有一种解决这个问题的标准算法,它基于GF(2)上的高斯消元法。我们的想法是设置一个表示按钮的矩阵按下代表灯光的列向量,然后使用标准矩阵简化技术来确定要按哪些按钮。它以多项式时间运行,不需要任何回溯。

我有一个implementation这个算法,其中包括我个人网站上可用的数学描述。我希望你觉得它很有用!

修改:如果您被迫使用回溯,您可以使用以下事实来执行此操作:

  • 任何解决方案都不会再按两次相同的按钮,因为这样做会取消之前的动作。
  • 任何解决方案要么按下第一个按钮,要么不按。

考虑到这种方法,您可以使用回溯来解决这个问题,使用简单的递归算法来跟踪电路板的当前状态以及您已经做出决定的按钮:

  • 如果您已决定每个按钮,请返回是否已解决该板。
  • 否则:
    • 尝试按下下一个按钮,看看该板是否可以从那里递归解决。
    • 如果是,请返回成功。
    • 否则,请尝试按下下一个按钮,看看该板是否可以从那里递归解决。
    • 如果是,请返回成功。如果没有,则返回失败。

这将探索大小为2 25 的搜索空间,大约为3200万。这很重要,但并非不可逾越。

希望这有帮助!

答案 1 :(得分:1)

回溯意味着:

Incrementally build a solution, throwing away impossible solutions.

这是使用输入和输出的位置这一事实的一种方法(按下按钮会影响它周围的方形)。

problem = GIVEN
solutions = [[0],[1]] // array of binary matrix answers (two entries, a zero and a one)
for (problemSize = 1; problemSize <= 5; problemSize++) {
    newSolutions = [];
    foreach (solutions as oldSolution) {
        candidateSolutions = arrayOfNByNMatriciesWithMatrixAtTopLeft(min(5,problemSize+1), oldSolution);
        // size of candidateSolutions is 2^((problemSize+1)^2 - problemSize^2)
        // except last round candidateSolutions == solutions
        foreach (candidateSolutions as candidateSolution) {
            candidateProblem = boardFromPressingButtonsInSolution(candidateSolution);
            if (compareMatrix(problem, candidateProblem, 0, 0, problemSize, problemSize)==0)
                newSolutions[] = candidateSolution;
        }
    }
    solutions = newSolutions;
}
return solutions;

答案 2 :(得分:0)

如前所述,您应首先形成一组联立方程式。

首先要注意的是,一个特定的灯光按钮最多应按一次,因为将灯光组切换两次是没有意义的。

Let Aij = Light ij Toggled { Aij = 0 or 1 }

应该有25个这样的变数。

现在,对于每个灯光,您可以形成一个类似

的等式
summation (Amn) = 0. { Amn = 5 light buttons that toggle the light mn }

所以你将有25个变量和25个未知数。你可以同时解决这些方程式。

如果您需要使用回溯或递归来解决它,您可以通过这种方式求解方程式。只需假设变量的初始值,看它们是否满足所有方程式。如果没有,那就回去追踪。

答案 3 :(得分:0)

天真的解决方案

首先,您需要一种方法来表示电路板的状态和堆栈来存储所有状态。在每个步骤中,制作电路板的副本,更改为新状态。将该状态与您迄今为止遇到的所有董事会状态进行比较。如果您还没有看到它,请将该状态推到堆栈顶部并继续下一步。如果您已经看过它,请尝试下一步行动。在从堆栈弹出状态(回溯)之前,每个级别都必须尝试所有可能的64个移动。您将需要使用递归来管理下一步要检查的状态。

最多有2个 64 可能的电路板配置,这意味着您可能会进入一个非常长的独特状态链并且仍然会耗尽内存。 (作为参考,1 GB是2 30 字节,您需要至少8个字节来存储电路板配置)此算法不太可能在已知Universe的生命周期内终止。

你需要做一些聪明的事情来减少搜索空间......

贪婪的第一次搜索

您可以通过首先搜索最接近已解决配置的状态来做得更好。在每个步骤中,按照从大多数灯关闭到最少灯关闭的顺序对可能的移动进行排序。按顺序迭代。这应该可以很好地工作,但不能保证获得最佳解决方案。

并非所有熄灯拼图都可以解决

无论您使用何种算法,都可能没有解决方案,这意味着您可能会在没有找到解决方案的情况下永远搜索(或至少几万亿年)。

在浪费任何时间尝试寻找解决方案之前,您需要检查电路板的可解决性(事实证明这是一个更快的算法)。

答案 4 :(得分:0)

我使用答案集编程制作了一个简洁的优化求解器。请参阅:https://github.com/jachymb/lights-out