使用swi-prolog解决难题

时间:2014-05-09 03:34:27

标签: prolog

我作为一个作业被赋予了使用prolog编写的解决方案 战舰单人拼图。对于那些不熟悉的人来说,这个谜题是有效 一个6乘6的网格,根据提供的方式放置一系列船只 每个行和列的约束,即第一行必须包含3个带有船的方格,第二行必须包含1个带有船的方格,第三行必须包含0个方格等其他行和列。

每个谜题都带有它自己的一组约束和显示的方块,通常是两个。这里可以看到一个例子: battleships

所以,这就是我所做的:

step([ShipCount,Rows,Cols,Tiles],[ShipCount2,Rows2,Cols2,Tiles2]):-
   ShipCount2 is ShipCount+1,
   nth1(X,Cols,X1),
   X1\==0,
   nth1(Y,Rows,Y1),
   Y1\==0,
   not(member([X,Y,_],Tiles)),
       pairs(Tiles,TilesXY),
   notdiaglist(X,Y,TilesXY),
   member(T,[1,2,3,4,5,6]),
   append([X,Y],[T],Tile),
   append([Tile],Tiles,Tiles2),
   dec_elem1(X,Cols,Cols2),dec_elem1(Y,Rows,Rows2).


dec_elem1(1,[A|Tail],[B|Tail]):- B is A-1.
dec_elem1(Count,[A|Tail],[A|Tail2]):- Count1 is Count-1,dec_elem1(Count1,Tail,Tail2).

neib(X1,Y1,X2,Y2) :- X2 is X1,(Y2 is Y1 -1;Y2 is Y1+1; Y2 is Y1).
neib(X1,Y1,X2,Y2) :- X2 is X1-1,(Y2 is Y1 -1;Y2 is Y1+1; Y2 is Y1).
neib(X1,Y1,X2,Y2) :- X2 is X1+1,(Y2 is Y1 -1;Y2 is Y1+1; Y2 is Y1).

notdiag(X1,Y1,X2,Y2) :- not(neib(X1,Y1,X2,Y2)).
notdiag(X1,Y1,X2,Y2) :- neib(X1,Y1,X2,Y2),((X1 == X2,t(Y1,Y2));(Y1 == Y2,t(X1,X2))). 
notdiaglist(X1,Y1,[]).
notdiaglist(X1,Y1,[[X2,Y2]|Tail]):-notdiag(X1,Y1,X2,Y2),notdiaglist(X1,Y1,Tail).

t(X1,X2):- X is abs(X1-X2), X==1.

pairs([],[]).
pairs([[X,Y,Z]|Tail],[[X,Y]|Tail2]):-pairs(Tail,Tail2).

我代表一个列表为[Count,Rows,Columns,Tiles]的州。最后一个状态必须是 [10,[0,0,0,0,0,0],[0,0,0,0,0,0], somelist]。拼图从初始状态开始,例如

initial([1, [1,3,1,1,1,2] , [0,2,2,0,0,5] , [[4,4,1],[2,1,0]]]).

我尝试以下列方式找到解决方案:

run:-initial(S),step(S,S1),step(S1,S2),....,step(S8,F).

现在,遇到困难:如果我使用member(T,[1])将自己限制在一种船舶部件

而不是

member(T,[1,2,3,4,5,6])

它工作正常。但是,当我使用T所需的全部可能值时 之后,查询永远不会结束,因为它运行的时间太长了。这让我很困惑,因为:

  • (a)它适用于6种类型的船舶,但只适用于8级而不是9级
  • (b)从单一类型的船舶到6种类型增加了数量 最后一步的选项是6倍,其中 不应该产生如此巨大的影响。

    那么,发生了什么?


1 个答案:

答案 0 :(得分:1)

直接回答你的问题,正在进行的是Prolog正试图筛选出巨大的可能性空间。

您正确认为更改该行会将 last 调用的搜索空间增加6倍,请注意,例如9个调用的搜索空间大小不是&n #39; t与一次通话大小的9倍成比例。 Prolog将在失败时回溯,因此它与一个呼叫提升到第九次幂的可能结果的大小成比例(实际上是上限)。

这意味着当我们允许T接受6倍的值时,我们可以预期Prolog需要搜索的空间大小最多增长6 ^ 9 = 10077696。


当然,如果我们以step开始调用initial 9次,那么解决方案并不存在(如我所知)并不存在帮助。由于最后一次通话将失败,Prolog将继续尝试,直到它最终放弃之前所有可能性(其中有很多可能性)已经用尽。


就解决方案而言,我不确定我对此问题了解得多。如果T的值是适合于网格的船舶类型(例如,单个正方形,2平方船的一半,3平方船的一部分),您应该注意到这比您提供的信息要多得多行/列上的数字。

现在,在伪代码中,您的step看起来像这样:

  • 找到其行/列上具有非零标记的(X,Y)对
  • 检查那里是否有船只
  • 检查它是否与船舶对角
  • 选择一种船舶部件。

我建议你这样做:

  • 完成任何已放置的船位以形成完整的船舶(如果我们不能:失败)
  • 直到我们完成:
    • 找到可以放置船只的地方
    • 检查行/列上的标记是否为零
    • 尝试在此处放置整个。 (而不是单个部分)

通过使用我们首先获得的最具体的信息(在这种情况下,以前放置的部分),我们可以减少Prolog必须做的工作量并使事情合理地快速返回。