如何用计算机程序解决Log Pile木制拼图?

时间:2010-04-03 22:41:18

标签: java algorithm puzzle

有人可以建议如何使用计算机程序解决Log Pile木制拼图吗?

点击此处查看拼图:http://www.puzzlethis.co.uk/products/madcow/the_log_pile.htm

图片仅显示部分内容。全套10件配置如下,其中1表示一个栓钉,-1表示一个孔,0表示既不是钉子也不是孔。
-1,1,0,-1,0
1,0,1,0,0
1,-1,1,0,0
-1,-1,0,0,-1
-1,1,0,1,0
0,1,0,0,1
1,0,1,0,-1
0,-1,0,1,0
0,0,1,1,-1
1,0,-1,0,0

这些碎片可以互锁成两层,每层5层,顶层与底层成90度,如上图所示。

我已经使用Java自己创建了这个问题的解决方案,但我觉得这是一个笨拙的解决方案,我有兴趣看到一些更复杂的解决方案。随意建议一般方法或以您选择的语言提供工作计划。

我的方法是使用上面的数字符号来创建“Logs”数组。然后我使用组合/置换生成器来尝试所有可能的Logs排列,直到找到所有交叉点等于零的解(即Peg to Hole,Hole to Peg或Blank to Blank)。我使用了一些加速来检测给定排列的第一个失败的交叉点,然后继续下一个排列。

我希望你觉得这和我一样有趣。

谢谢, 克雷格。

4 个答案:

答案 0 :(得分:5)

此问题似乎是Boolean satisfiability problem的一种形式。如果是这样,最着名的方法就是蛮力。

但是有一些对称性,问题的一些属性可以让你减少你需要尝试的解决方案的数量。例如 -

  • 如果将日志分成两部分 5件子集TOP和BOTTOM,# TOP中的钉需要匹配# BOTTOM的洞和#洞 TOP需要与#pegs相匹配 BOTTOM,以及TOP需要的#单位 匹配BOTTOM中的#flats。如果 然后,#pegs和#holes匹配起来 #flats也会匹配,所以你应该 不需要检查#flats。
  • 您有10 * 9 * 8 * 7 * 6种方式 可以将日志分成两部分 5件式子集。 (一旦你有 选择前5个日志作为子集 TOP,剩余的日志是子集 BOTTOM。
  • 当您测试5件式子集时,您可以使用5 * 8 * 6 * 4 * 2方式排列一层日志。也就是说,在您选择日志1之后,剩余4个日志。对于第二个日志, 你可以选择四个,而且有 它可以用两种方式定位 尊重第一个日志。
  • 一旦你有了一个基础层,你可以尝试一次一个地从另一个图层填入每个日志,直到找到一个不合适的日志。此时,您放弃当前的基础层日志安排并尝试下一个。

答案 1 :(得分:1)

Prolog很受此类问题的欢迎。我在Prolog中设置了一些simpler puzzle problems相对不错的解决方案。

使用函数和回溯在Haskell中解决这些问题有非常优雅的方法。我的一个朋友解决了一个非常棘手的物理难题 - Puzzling Pets - 仅仅200多行Haskell,包括所有碎片和所有东西的描述。学习相关技巧的好方法是阅读John Hughes的论文Why Functional Programming Matters,安装Haskell Platform,然后尝试一下。

答案 2 :(得分:1)

根据Jay Elston的建议,我将使用以下伪代码实现它:

 Read in the pieces
 Annotate each piece with its number of pegs and holes
 Generate all possible base layers (10 over 5 = 252, selecting 5 from the set), 
   so that sum(above, pegs) == sum(below, holes) and sum(below, pegs) == sum(above, holes)
   For each base layer, (5! = 120 of them, permuting the 'below' pieces)
      compute 'rotated pieces' for the base layer
      if one-to-one match between top layer and rotated base-layer pieces, 
          report successful solution

对于给定的基础层,“旋转的部分”将是您需要覆盖它的理想部分。通过计算这些碎片并将它们与顶层碎片匹配,您可以使用O(N log N)排序来匹配旋转的碎片与真正的顶层,而不是匹配所有N!可能的顶层安排。另外,在第一次不匹配时,你可以提前纾困。

编写高效算法的关键是尽早修剪您的搜索,并利用任何对称性。例如,如果您认为拼图是对称的,则可以将运行时间缩短一半,因此您可以任意地将一个块分配给底层:然后您将只有9个超过4个基础层进行探索。

答案 3 :(得分:0)

我在javascript中有一个(麻烦!)解决方案,但是发布它有困难。使用的基本算法是: 从总共10个日志中查找5个日志的所有迭代,并且每组5个日志创建每次迭代,并且日志被反转。 对于这些状态中的每一个,我们将知道需要将哪种日志模式置于顶部。因此,我们确定是否能够将剩余的5个日志放在最上面。

这导致了解决方案的这种表示:

(Bottom, right-> left)  
[0,0,1,-1,1],[-1,-1,0,0,-1],[0,0,1,0,1],[-1,1,0,-1,0],[1,0,-1,0,0]  
(Top, up->down)  
[0,1,0,1,-1],[0,1,0,-1,0],[-1,0,-1,0,1],[1,0,0,1,0],[-1,1,-1,0,0]  


0 - flat   
1 - dowel  
-1 - hole