Prolog 在序言中,如何通过广度或深度优先搜索解析河内塔?

Prolog 在序言中,如何通过广度或深度优先搜索解析河内塔?,prolog,depth-first-search,breadth-first-search,towers-of-hanoi,Prolog,Depth First Search,Breadth First Search,Towers Of Hanoi,在我看到的大多数实现中,使用递归解决方案。但是,我不想要这个。我想用搜索算法在树中搜索,比如广度优先或深度优先 谢谢BFS可能就是这样(与SWI Prolog一起使用) %来存储可能性树 :-河内动态lst_/1。 河内博鳌亚洲论坛:- 初始化, %BFS回路 重复一遍, 下一个 完成(河内), %显示解决方案 背面(河内,河内版次), 地图列表(书面,河内版本)。 初始化:- %bdd的清洁 收回所有(河内岛), %存储初始配置列表(仅一个) 断言(河内市([[hanoi([1,2,3,4],

在我看到的大多数实现中,使用递归解决方案。但是,我不想要这个。我想用搜索算法在树中搜索,比如广度优先或深度优先


谢谢

BFS可能就是这样(与SWI Prolog一起使用)

%来存储可能性树
:-河内动态lst_/1。
河内博鳌亚洲论坛:-
初始化,
%BFS回路
重复一遍,
下一个
完成(河内),
%显示解决方案
背面(河内,河内版次),
地图列表(书面,河内版本)。
初始化:-
%bdd的清洁
收回所有(河内岛),
%存储初始配置列表(仅一个)
断言(河内市([[hanoi([1,2,3,4],][],[])]))。
%我们搜索最终的配置
%这里前两列是空的
饰面([河内([],[],A)| B]):-
%获取配置列表
河内第一大道(lst),
%试验
成员([河内([],[],A)| B],Lst)。
下一步:-
%获取实际的配置列表
收回(河内上院(河内)),
%对每个配置进行操作
地图列表(下一步,河内,河内临时),
%连接新配置列表
附加(河内临时工,河内1),
%某些配置为空,请删除它们
%SWI Prolog功能
不包括(([])、河内1号、河内2号、,
%存储新列表
断言(河内第一(河内第二)。
%计算从一个配置获得的下一个配置
下一步移动([Hanoi | T1],R):-
%只有列表的第一个元素是有用的
%计算可能的移动
移动(河内,下一个),
%创建新配置
地图列表(新河内([hanoi | T1]),下一页,R)。
%如果尚未看到新位置,则添加新位置
新河内(T,H[H|T]):-
\+成员(H,T)!。
%否则将删除配置
新河内(_T,_H,[])。
%所有移动可能性的列表
移动(河内([H | T],[],[]),[hanoi(T[H],]),河内(T,[],[H]))。
移动(河内([],[H | T],]),[河内([H],T,[]),河内([],T,[H]))。
移动(河内([],[],[H | T]),[hanoi([H],[H],T]),河内([],[H],T])。
移动(河内([H1 | T1],[H2 | T2],]),
[河内(T1[H2 | T2],[H1]),河内([H1 | T1],T2[H2]),河内([H2,H1 | T1],T2,[])):-
H1>H2!。
移动(河内([H1 | T1],[H2 | T2],]),
[河内(T1[H2 | T2],[H1]),河内([H1 | T1],T2[H2]),河内(T1[H1,H2 | T2],])。
移动(河内([H1 | T1],[H2 | T2]),
[河内(T1[H1],[H2 | T2]),河内([H1 | T1],[H2],T2),河内([H2,H1 | T1],[H2,T2]):-
H1>H2!。
移动(河内([H1 | T1],[H2 | T2]),
[河内(T1[H1],[H2 | T2]),河内([H1 | T1],[H2],T2),河内(T1,[],[H1,H2 | T2]))。
移动(河内([],[H1|T1],[H2|T2]),
[河内([H1],T1,[H2 | T2]),河内([H2],[H1 | T1],T2),河内([],[H2,H1 | T1],T2)]:-
H1>H2!。
移动(河内([],[H1|T1],[H2|T2]),
[河内([H1],T1[H2 | T2]),河内([H2],[H1 | T1],T2),河内([],T1[H1,H2 | T2]))。
移动(河内([H1|T1]、[H2|T2]、[H3|T3]),
[河内(T1、[H1、H2 | T2]、[H3 | T3]),
河内(T1、[H2 | T2]、[H1、H3 | T3]),
河内([H1 | T1],T2[H2,H3 | T3])):-
H1

此代码可能会得到改进

好的,这应该很容易。事实上,在Prolog中,一切都应该很简单——写下问题通常就是解决方案本身

那么,我们有什么?三塔,比如说
t(X,Y,Z)
。每个塔是一系列磁盘,按大小的降序排列-只有较小的磁盘可以放在较大的磁盘上,而不是相反:

move1( t([A|B],[C|D],E), t(B,[A,C|D],E) ) :- A < C, writeln([move,A,on,C]).
move1( t([A|B],[],E),    t(B,[A],E)     ) :-      writeln([move,A,on,empty]).
你看,我们只是写下我们已经知道的关于这个问题的内容。现在我们有了谓词
move/2
,它描述了从给定位置(它的第一个参数)的任何可能的移动,给了我们新的位置(它的第二个参数)

这是为我们定义搜索空间的谓词。在每一点上,我们都会采取行动,看看是否有解决方案。这是一个深度优先的搜索,因为我们采取了行动,并随着它深入,希望它能引导我们达到我们的目标

hanoi( Start, Finish):- move(Start,Finish), write('Done.'),nl.
hanoi( Start, Finish):- move(Start, P), hanoi(P,Finish).
例如,我们使用
hanoi(t([1,2],[])、t([],[1,2],])运行它。
现在发生了什么?它不停地循环,一次又一次地尝试同样的动作。但是,即使它尝试新的动作并最终达到终点状态,我们也无法说出是哪一个动作导致它达到终点——现在它只是打印出它尝试的每一个动作,即使是没有任何结果


因此,我们不需要马上写下这些动作,而是要保持动作列表,只有当我们达到完成状态时,我们才会写出成功的动作列表。我们现在还可以检查此移动列表,以避免重复我们已经进行的移动:

move1( t([A|B],[C|D],E), t(B,[A,C|D],E) ) :- A < C.
move1( t([A|B],[],E),    ...            ).

move( P, P2, M, [P2|M] ):- move(P,P2), \+memberchk(P2,M).
我们得到了

?- hanoi(t([1,2],[],[]), t([],[1,2],[]), _).
t([1, 2], [], [])
t([2], [1], [])
t([], [1], [2])
t([], [], [1, 2])
t([1], [], [2])
t([1], [2], [])
t([], [1, 2], [])
但是,这个DFS是隐式的——我们依靠Prolog来尝试这些动作。显式搜索查找每个位置的所有可能移动,并将这些移动添加到操作缓冲区,该缓冲区将包含一个位置和一个位置列表的对
move1( t([A|B],[C|D],E), t(B,[A,C|D],E) ) :- A < C.
move1( t([A|B],[],E),    ...            ).

move( P, P2, M, [P2|M] ):- move(P,P2), \+memberchk(P2,M).
hanoi(Start,Finish,N):- hanoi_dfs(Start,Finish,[Start],N).

hanoi_dfs( Start, Finish, M, N):- move(Start, Finish, M, M2), 
                                  length(M2,K), N is K-1,
                                  reverse(M2,L), maplist(writeln,L), nl.
hanoi_dfs( Start, Finish, M, N):- move(Start, P, M, M2), 
                                  hanoi_dfs(P, Finish, M2, N).
?- hanoi(t([1,2],[],[]), t([],[1,2],[]), _).
t([1, 2], [], [])
t([2], [1], [])
t([], [1], [2])
t([], [], [1, 2])
t([1], [], [2])
t([1], [2], [])
t([], [1, 2], [])