Prolog迷宫求解算法

3
I希望您能在Prolog中实现迷宫求解算法。因此,我搜索了一些迷宫求解算法,并找到了以下内容:http://www.cs.bu.edu/teaching/alg/maze/ 寻找路径(x,y):
if (x,y outside maze) return false
if (x,y is goal) return true
if (x,y not open) return false
mark x,y as part of solution path
if (FIND-PATH(North of x,y) == true) return true
if (FIND-PATH(East of x,y) == true) return true
if (FIND-PATH(South of x,y) == true) return true
if (FIND-PATH(West of x,y) == true) return true
unmark x,y as part of solution path
return false 

我已经在Prolog中构建了一个矩阵,表示迷宫,其中0表示开放,1表示墙壁,例如(起始位置为(2 | 1),目标位于(4 | 1)):

11111
10001
10101

进一步地,我定义了一个名为的子句,它可以给我在某个位置上矩阵的值。
到目前为止都还好。但现在我在尝试在Prolog中实现这个算法时遇到了问题。我已经尝试过“脏方法”(使用嵌套的if语句逐个翻译),但随着复杂度的增加,我认为这不是在Prolog中解决问题的方式。
所以我尝试了这个:
isNotGoal(X, Y) :- 
    X = 19, Y = 2.

notOpen(X, Y, MazeData) :-
    mazeDataAt(X, Y, MazeData, 1). 
    
findPath(X, Y, MazeData) :- 
    isNotGoal(X, Y),
    notOpen(X, Y, MazeData),
    increase(Y, Y_New),
    findPath(X, Y_New, MazeData),
    increase(X, X_New),
    findPath(X_New, Y, MazeData),
    decrease(Y, Y_New),
    findPath(X, Y_New, MazeData),
    decrease(X, X_New),
    findPath(X, Y_New, MazeData).

但是这次尝试并没有像预期的那样有效。
实际上,这是上述算法的正确Prolog实现吗?我该如何确定这种方法是否真的能在迷宫中找到路径?因此,我该如何记录路径或获取解决方案路径(通过在上述算法中标记/取消标记路径完成)?
非常感谢您的帮助!
//更新
感谢您的答案!我采用了更像Prolog的解决方案(请参见此处)来解决我的问题。所以现在我有:
d([2,1], [2,2]).
d([2,2], [1,2]).
d([2,2], [2,3]).

go(From, To, Path) :-
go(From, To, [], Path).

go(P, P, T, T).
go(P1, P2, T, NT) :-
    (d(P1, P3) ; d(P3, P2)),
    \+ member(P3, T),
    go(P3, P2, [P3|T], NT).

到目前为止,这个程序可以工作。我认为我明白了为什么Prolog的方式更好。但现在我还有一个小问题。我想让我的知识库是“动态的”。我不能为迷宫中的每个路标定义所有边缘。因此,我编写了一个叫做 is_adjacent([X1, Y1], [X2, Y2]) 的子句,当 [X1, Y1][X2, Y2] 的邻居时,它就为真。我还有一个列表 Waypoints = [[2, 1], [2, 2]| ...],其中包含迷宫中所有可能的路标。现在问题来了:我该如何使用它来使我的知识库“动态”?这样我就可以在 go 子句中使用它来查找路径了吗?感谢您的帮助! //更新2 现在,我已经将所有路标作为事实得到了。
w(2, 1).
w(2, 2).
...

我从Boris在他的一个回答中采用了解决方案:

d(X0, Y0, X , Y) :-
    w(X0, Y0),
    next_w(X0, Y0, X, Y),
    w(X, Y).

next_w(X0, Y0, X0, Y) :- Y is Y0 + 1.
next_w(X0, Y0, X0, Y) :- Y is Y0 - 1.
next_w(X0, Y0, X, Y0) :- X is X0 + 1.
next_w(X0, Y0, X, Y0) :- X is X0 - 1.

之后,我更新了go子句,使其符合要求:

go(X1, Y1, X2, Y2, Path) :-
go(X1, Y1, X2, Y2, [], Path).

go(X, Y, X, Y, T, T).
go(X1, Y1, X2, Y2, T, NT) :-
   (d(X1, Y1, X3, Y3) ; d(X3, Y3, X1, Y1)),
\+ member([X3, Y3], T),
go(X3, Y3, X2, Y2, [[X3, Y3]|T], NT).

但是,如果我尝试询问go(2, 1, 19, 2, R),Prolog会进入无限循环。如果我尝试一些更简单的东西,比如go(2, 1, 3, 8, R),它可以工作,并且我可以在R中得到解决方案路径。

我做错了什么?我忘了什么?


2
请参见:http://stackoverflow.com/questions/8672046/not-member-rule-in-prolog-doesnt-work-as-expected(使用“[prolog] maze”找到)。该方法使用形式为d(From, To)的事实表示可能的转换。 - user1812457
谢谢,但那并不符合我的需求,因为我的动机是将上面的算法翻译成Prolog,或者将其作为相应Prolog算法的尝试。 - Chris2015
1
实际上,我认为这本质上是完全相同的算法,只是用适当的“本地”Prolog编写,利用可能的转换作为事实和回溯。我建议您尝试构建您指向的算法和Prolog解决方案的解决方案树,您应该自己看到它。 - user1812457
1
你应该真正查看@Boris链接的问题,因为它展示了如何在惯用的Prolog中完成这种事情。只有当你理解语言时,才能翻译给定的算法。 - Fred Foo
好的,我明白了!谢谢。但是现在我想要使我的知识库“动态化”,请看我的更新帖子。您能帮助我吗? - Chris2015
1个回答

3
(本答案使用与此答案相同的路径查找算法。) 编辑2 实际上,如果您的输入只是矩形矩阵中不是墙的单元格,则需要将其转换为“您可以从A到达B”的规则。如果您的航点是:
w(2,1).
w(2,2).

等等,您想翻译的内容是关于IT技术方面的,那我很乐意为您提供帮助。以下是您需要翻译的内容:

如果是这样,你就可以将你最初指向的算法翻译成Prolog规则,就像这样:

% it is possible to move from (X0,Y0) to (X,Y)
d(X0,Y0,X,Y) :-
    w(X0,X0), % you can skip this check if you know for sure
              % that your starting point is a valid waypoint
              % or if you want to be able to start from inside
              % a wall :)
    next_w(X0,Y0,X,Y),
    w(X,Y).
% neighboring waypoints
next_w(X0,Y0,X0,Y) :- Y is Y0+1. % go up I guess
next_w(X0,Y0,X0,Y) :- Y is Y0-1. % go down
next_w(X0,Y0,X,Y0) :- X is X0+1. % go left
next_w(X0,Y0,X,Y0) :- X is X0-1. % go right

请注意两点:
  1. 我正在使用四个参数的规则来确定从一个方块可能移动的位置(因此请相应调整)
  2. 魔法发生在next_w中。当调用d时,它使用next_w生成四个可能的相邻方块(假设只能向上/下/左/右移动),然后检查这个方块是否确实是航路点。你不再需要反过来检查。

另外编辑:完整代码

w(0,0).
w(0,1). w(1,1). w(2,1). w(3,1). w(4,1). w(5,1).
        w(1,2).         w(3,2).         w(5,2).
        w(1,3).         w(3,3).         w(5,3).
w(0,4). w(1,4). w(2,4).         w(4,4). w(5,4).
                w(2,5). w(3,5). w(4,5).

d(X0,Y0,X,Y) :- next_w(X0,Y0,X,Y), w(X,Y).
next_w(X0,Y0,X0,Y) :- Y is Y0+1.
next_w(X0,Y0,X,Y0) :- X is X0+1.
next_w(X0,Y0,X0,Y) :- Y is Y0-1.
next_w(X0,Y0,X,Y0) :- X is X0-1.

go(X,Y,X,Y,Path,Path).
go(X0,Y0,X,Y,SoFar,Path) :-
    d(X0,Y0,X1,Y1),
    \+ memberchk( w(X1,Y1), SoFar ),
    go(X1,Y1,X,Y,[w(X1,Y1)|SoFar],Path).

您可以使用以下方式进行调用:

? go(0,0,5,4,[],Path).

我认为您的问题是分号;因为您已经明确地创建了所有可能的移动,所以它不再必要。换句话说,您应该得到两个可能的解决方案。


如果我的可能途径点列表是无序的,这个方法也适用吗?因为它只包含所有的途径点,并没有说明它们之间的关系。 - Chris2015
@Chris2015,我终于明白你在问什么了。请查看已编辑的答案。 - user1812457
谢谢你迄今为止的帮助。我今天学到了很多! :-) 我又编辑了我的原始问题,并写下了我做了什么。你能告诉我我做错了什么吗?我的解决方案是否缺少了什么或者我犯了一个错误?或者我可能误解了你的解决方案? - Chris2015
@Chris2015,是的,你误解了一些细节,但我没有表述得很明确。我添加了我的完整解决方案,它与你所拥有的有点不同,但它可以工作。 - user1812457
哇,它终于能用了!非常感谢你!我花了整整一天的时间来解决这个问题,现在终于有一个可行的解决方案了。非常感谢你!...有趣的是,在你回答之前的5分钟里,我已经删除了“或”运算符,因为它已经没有意义了。但是与你的解决方案相比,我犯了一个小错误。现在它终于能用了! :-) - Chris2015

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接