当前位置: 首页 > 知识库问答 >
问题:

迷宫求解器DFS算法

蔺弘
2023-03-14

我在一门课上学习序言。我有一个练习,我需要读取一个文件,从中生成一个迷宫,获得从源到目标的路径,然后将其写入一个文件。

我读了文件,对我拥有的每个单元格断言square(CoordX,CoordY),对连接的每两个单元格断言connect(X,Y)

TargetXTargetYSourceXSourceY都是整数坐标,所以我知道起点和终点。

我的工作原理是,如果当前节点连接到目标,完成并返回。否则,找到当前节点所连接的节点,并用新节点调用递归

solveFirst(TargetX, TargetY, SourceX, SourceY, T):-
    connects(square(SourceX, SourceY), NewSquare),
    solve(TargetX, TargetY, SourceX, SourceY, NewSquare, T2),
    T = T2.

solve(TargetX, TargetY, SourceX, SourceY, NewSquare ,[Tail]):-
    getFirst(Tail, HeadOfTail),
    (
    connects(NewSquare, square(TargetX, TargetY)), 
    addFirst(NewSquare, Tail, T2),
    Tail = T2 ;
    connects(NewSquare, E),
    solve(TargetX, TargetY, SourceX, SourceY, E, Tail)
    ).

共有2个答案

沈实
2023-03-14

你的理由似乎是有效的。你唯一要做的就是正确地实施它。

首先,对您的代码进行一般性评论:

当你看到这样一个目标:

T = T2

问问自己:你为什么要引入T2?你可以简单地使用T,因为如果这个目标成功,这两个词是相同的。

这种模式在程序中出现两次。

另一个问题:永远不要放 在一行的末尾,它看起来太像

因此,在这些更改之后,您的solve/6如下所示:


solve(TargetX, TargetY, SourceX, SourceY, NewSquare, [Tail]):-
    getFirst(Tail, HeadOfTail),
    (   connects(NewSquare, square(TargetX, TargetY)),
        addFirst(NewSquare, Tail, Tail)
    ;   connects(NewSquare, E),
        solve(TargetX, TargetY, SourceX, SourceY, E, Tail)
    ).

现在问问自己:这有意义吗?特别是,addFirst(NewSquare,Tail,Tail)能成功吗?这对我们来说有点难说,因为你省略了它的定义。

此外,当你编译这个时,你会得到一个警告:


Singleton variables: [HeadOfTail]

那么你为什么要引入这个变量呢?

从概念上讲,以下模式可能会帮助您:


path(Current, Target, Path) :-
    (   connects(Current, Target),
        Path = [Current]
    ;   connects(Current, Next),
        Path = [Current|Ps],
        path(Next, Target, Ps)
    ).

陶刚豪
2023-03-14

我用这种方式修改了你的规则

solve(TargetX, TargetY, _, _, NewSquare, [NewSquare]) :-
  connects(NewSquare, square(TargetX, TargetY)).

solve(TargetX, TargetY, SourceX, SourceY, NewSquare, [E | Tail]) :-
  connects(NewSquare, E),
  solve(TargetX, TargetY, SourceX, SourceY, E, Tail).

solveFirst(TargetX, TargetY, SourceX, SourceY, [NewSquare | T]):-
  connects(square(SourceX, SourceY), NewSquare),
  solve(TargetX, TargetY, SourceX, SourceY, NewSquare, T).

根据以下事实

square(1, 1).  square(1, 2).  square(1, 3).
square(2, 1).  square(2, 2).  square(2, 3).
square(3, 1).  square(3, 2).  square(3, 3).

connects(square(1, 1), square(1, 2)).
connects(square(1, 1), square(2, 1)).
connects(square(1, 2), square(1, 3)).
connects(square(1, 2), square(2, 2)).
connects(square(2, 1), square(2, 2)).
connects(square(2, 2), square(3, 2)).
connects(square(3, 2), square(3, 3)).

调用solveFirst(3,3,1,1, L),我得到(在L中)以下路径

[square(1,2),square(2,2),square(3,2),square(3,2)]
[square(2,1),square(2,2),square(3,2),square(3,2)]

但这是可行的,因为没有循环。如果添加以下连接

connects(square(2, 2), square(1, 2)).

所以你可以循环((1,2)-

为了避免这个问题,您可以记住访问过的方块,并避免再次使用它们。

我已经写了下面的例子,但是请考虑一下

(1) 我已经切换了开始和目标(先开始,后目标)

(2) 我在生成的路径中添加了start和target

(3)我使用gprolog,所以我没有not/1;我使用了\成员......代替

getPath(Tx, Ty, Tx, Ty, _, [square(Tx, Ty)]).

getPath(Sx, Sy, Tx, Ty, Visited, [square(Sx, Sy) | Path]) :-
  connects(square(Sx, Sy), square(Nx, Ny)),
  \+ member(square(Nx, Ny), Visited), % or not(member(square(Nx, Ny), Visited)
  getPath(Nx, Ny, Tx, Ty, [square(Nx, Ny) | Visited], Path).

getPath(Sx, Sy, Tx, Ty, Path) :-
  getPath(Sx, Sy, Tx, Ty, [square(Sx, Sy)], Path).

使用以下事实

square(1, 1).  square(1, 2).  square(1, 3).
square(2, 1).  square(2, 2).  square(2, 3).
square(3, 1).  square(3, 2).  square(3, 3).

connects(square(1, 1), square(1, 2)).
connects(square(1, 1), square(2, 1)).
connects(square(1, 2), square(1, 3)).
connects(square(1, 2), square(2, 2)).
connects(square(2, 2), square(1, 2)).
connects(square(2, 1), square(2, 2)).
connects(square(2, 2), square(3, 2)).
connects(square(3, 2), square(3, 3)).

getPath(1,1,3,3, L)我得到以下路径

[square(1,1),square(1,2),square(2,2),square(3,2),square(3,3)]
[square(1,1),square(2,1),square(2,2),square(3,2),square(3,3)]

---编辑---

正如Mat在评论中所建议的(谢谢!),您可以编写(如果您的prolog环境支持maplist/2),而不是\member(square(Nx,Ny),Visited)(或not(member(square(Nx,Ny,Visited)

maplist(dif(square(Nx,Ny)), Visited)

要强制执行square(Nx,Ny)不在已访问的列表中。

这个解决方案更一般,因为(如果我理解正确的话)统一是双向的。

 类似资料:
  • 下面是DFS算法的伪代码http://www.mazeworks.com/mazegen/mazetut/index.htm 创建一个CellStack(后进先出)来保存单元格位置列表 设置TotalCells=网格中的单元格数 随机选择一个单元格并将其命名为CurrentCell 设置VisitedCells=1 在探访牢房时 别的 从CellStack中弹出最近的单元格条目 使其成为Curre

  • 我是C的新手,我目前正在一个项目中创建一个使用DFS算法生成的迷宫。 我已经成功地生成了一条路径,例如 如上所述, Source是初始单元,1是我根据随机邻居创建的路径,D是“死胡同”。所以,如果可能的话,我想回到S,从另一个方向开始。我该如何处理队列和堆栈?有人能解释一下吗?非常感谢你?

  • 问题内容: 为了编写一个解决C程序的蛮力迷宫,我首先编写了这个Java程序来测试一个想法。我对C还是很陌生,打算在Java中正确处理后将其转换。结果,我试图远离arraylist,fancy库等,以便更轻松地转换为C。该程序需要生成最短步骤的单个宽度路径来解决迷宫问题。我认为我的问题可能在于将通过每个递归传递的路径存储数组碎片化。感谢您的关注。-乔 代码中解释了数字符号 问题答案: 这本来不是要作

  • 问题内容: 我正在尝试使用递归编写一个迷宫求解器,似乎它尝试每个方向一次,然后停止,我不知道为什么。如果您发现问题,请告诉我。钥匙0是一个开放空间1是墙壁2是路径的一部分3是迷宫的末端 问题答案: 在过去五个小时中,您已经问过有关此迷宫递归难题的四个问题,这证明它有多复杂。这整个概念的1/0迷宫电网已吸引了我,我想出了一个类,使它成为一个 整体 变得简单许多。如果需要进行递归,那么它将对您没有用,

  • 我得到了一些构建迷宫的代码,以及任何其他需要的东西,抽象迷宫类包含一个抽象方法“makeMove(int row,int col)”。这是我试图编写的解决迷宫的方法,向左、向右、向上、向下移动。 我刚刚开始做这件事,下面是我到目前为止的全部资料。 好的,我让代码运行到不再出现错误的地方。 感谢所有的帮助。

  • 给定一个填充了0和1的二维字符数组,其中0代表一堵墙,1代表一条有效路径,我开发了一个名为findPath(int r,int c)的递归方法,用于在标有“x”的迷宫中找到出口。该方法接收迷宫的当前行和列,并通过N、E、S、W方向,直到找到有效路径,并用“”标记该有效路径。假设所有方向都被一堵墙挡住,那么该方法假设是回溯,直到情况不再如此,然后用“F”标记该路径,以表示坏路径。 现在我不明白为什么