从Prolog中的相邻元素列表中查找所有可能的路径?

时间:2017-10-11 18:54:07

标签: list recursion path prolog

我提前为这个尴尬的标题道歉,因为有点难以清楚地说几句话。

目标是找到所有可能的路径以及从一个房间使用的总能量"另一个基于输入房间。所以列表[r1,r2,3]意味着你可以从1号房间到2号房间,从2号房间到1号房间,无论哪种方式都需要3个能量。您 允许前往前往的房间。

以下是表示可以旅行的房间的清单列表。

adjacent([[r1,r2,8],[r1,r3,2],[r1,r4,4],[r2,r3,7],[r3,r4,1],[r2,r5,2],[r4,r6,5],[r6,r3,9],[r3,r5,3]]). 

这是我的代码,它正确地找到了一个路径,但是所有未来可能的路径只是重复以前的房间,因为我不确定如何实现该功能。我想我可以简单地使用not member(PosPath,Paths),因为Paths保存了以前所有传递给元素的列表,但似乎事先将PosPath添加到Paths所以它总是失败。

trip(Start,End,[Start,End],Energy):- adjacent(List), member([Start,End,Energy],List).

trip(Start,End,[Start|Paths],TotalE) :- 
                    adjacent(List),
                    member([Start,PosPath,E], List),
                    % not member(PosPath, Paths),
                    trip(PosPath,End,Paths,PathE).
                    % TotalE is E+PathE.

输出:

?- trip(r1, r6, Path, TotalE).
Path = [r1, r2, r3, r4, r6]
Total = Total
Yes (0.00s cpu, solution 1, maybe more)
Path = [r1, r2, r3, r4, r6, r3, r4, r6]
Total = Total
Yes (0.00s cpu, solution 2, maybe more)
Path = [r1, r2, r3, r4, r6, r3, r4, r6, r3, r4, r6]
TotalE = TotalE
Yes (0.00s cpu, solution 3, maybe more)

1 个答案:

答案 0 :(得分:1)

由于[r1,r2,3]中的房间代表双向路径,我建议使用描述这种对称性的谓词,让我们将其称为from_to_cost / 3:

from_to_cost(X,Y,C) :-
   adjacent(L),
   member([X,Y,C],L).
from_to_cost(X,Y,C) :-
   adjacent(L),
   member([Y,X,C],L).

对于调用谓词,我会建议一个更具描述性的名称,比如start_end_path_cost / 4,它对应于你的谓词trip / 4。对于描述实际关系的谓词,需要两个额外的参数:累加器来总结路径的成本,从0开始,以及以第一个房间作为单个元素开始的访问房间列表{ {1}}:

[S]

实际关系必须描述两种情况:

1)如果起始房间和终端房间相等,则找到路径。然后成本和累加器也是相等的,路径是空的。

2)否则,有一个尚未访问过的中间房间,可以从start_end_path_cost(S,E,P,C) :- s_e_p_c_(S,E,P,C,0,[S]). 到达:

S

现在,您的示例查询找到所有解决方案并终止:

s_e_p_c_(E,E,[],C,C,_Visited).
s_e_p_c_(S,E,[X|Path],C,C0,Visited) :-
   maplist(dif(X),Visited),
   from_to_cost(S,X,SXC),                
   C1 is C0+SXC,
   s_e_p_c_(X,E,Path,C,C1,[X|Visited]).

最常见的查询会查找给定连接的所有137个解决方案并终止:

?- start_end_path_cost(r1, r6, Path, TotalE).
Path = [r2, r3, r4, r6],
TotalE = 21 ;
Path = [r2, r3, r6],
TotalE = 24 ;
Path = [r2, r5, r3, r4, r6],
TotalE = 19 ;
Path = [r2, r5, r3, r6],
TotalE = 22 ;
Path = [r3, r4, r6],
TotalE = 8 ;
Path = [r3, r6],
TotalE = 11 ;
Path = [r4, r6],
TotalE = 9 ;
Path = [r4, r3, r6],
TotalE = 14 ;
false.

编辑:

关于你在评论中的问题:是的,这是可能的。您可以定义一个谓词,该谓词描述第一个参数不是列表中第二个参数的元素,让我们将其称为非成员/ 2:

?- start_end_path_cost(S, E, Path, TotalE).
S = E,
Path = [],
TotalE = 0 ;
S = r1,
E = r2,
Path = [r2],
TotalE = 8 ;
S = r1,
E = r3,
Path = [r2, r3],
TotalE = 15 ;
.
.
.
S = r5,
E = r1,
Path = [r3, r6, r4, r1],
TotalE = 21 ;
S = r5,
E = r2,
Path = [r3, r6, r4, r1, r2],
TotalE = 29 ;
false.

然后你可以用非成员/ 2替换s_e_p_c_ / 6中的地图列表目标,如下所示:

nonmember(_A,[]).
nonmember(A,[H|T]):-
   dif(A,H),
   nonmember(A,T).

通过此更改,查询会产生相同的结果。