广度优先搜索代码不会输出BFS结果

时间:2018-11-23 15:38:03

标签: prolog breadth-first-search

这是我找到的代码,并且已在我制作的另一个post中进行了修复。

s(a, b).
s(a, c).
s(b, g).
s(b, f).
s(c, r).
s(c, e).
goal(g).

solve( Start, Solution) :-
    breadthfirst( [ [Start] ], Solution).

breadthfirst( [ [Node | Path] |_], [Node | Path] ) :-
    goal( Node).

breadthfirst( [ [N | Path] | Paths], Solution) :-
    bagof([M,N|Path],
    ( s( N, M), \+ member( M, [N | Path] ) ), NewPaths),
    %conc( Paths, NewPaths, Pathsl), !,
    append(Paths, NewPaths, Pathsl), !,
    breadthfirst( Pathsl, Solution);
    breadthfirst( Paths, Solution).

但是,我发现输出是:

?- solve(a,S).
S = [g, b, a] ;

我掌握了所有事实,并绘制了一棵树来表示它们,在树中,我们得到a给出的bc处于同一级别,即第1级因此,应该首先访问它们。然后,在g的第二级访问b

所以输出应该是:

?- solve(a,S).
S = [g, c, b, a] ;

但是它没有给我输出,我也不知道为什么。

现在,还有一个反转输出的问题,如果也解决了,我将不胜感激。

1 个答案:

答案 0 :(得分:2)

  

它没有给我输出,我也不知道为什么。

您的代码给出了正确的答案,如我先前的answer所述。

?- solve(a,S).
S = [g, b, a] ;

问题不是代码给出的答案,但我相信您将从事实中了解算法的工作方式或图形的布局。

在将问题转换为代码之前,使用纸和笔处理问题是一个明智的决定。

这是图形形式的图形,其根(a)在顶部。

        a               Level 0
     /     \
   b         c          Level 1
 /   \     /   \
g     f   r     e       Level 2

请注意,从ag的最短路径是abg,而不是a,{{1} },bc。即使g已连接到c,它也不在从aa的路径上,因此也不是解决方案的一部分。

您是正确的,因为使用广度优先搜索(BFS)从g开始,然后处理与其连接的 ALL 节点,这些节点显示在Level 0中。然后,对Level 1中的每个节点执行相同的操作,创建Level 1,然后对每个新级别再次重复。

在处理每个节点时,BFS只会查找尚未访问的与该当前节点连接的任何节点,并将其所需的任何信息记录在表或其他数据结构中。如果您的示例在每个顶点上包括权重,则到达当前正在处理的节点的成本将与当前节点一起记录在数据结构中。访问节点以处理节点与作为从一个节点到另一节点的最短路径的一部分而访问节点不同。

考虑这一点的另一种方法是BFS和深度优先搜索(DFS)应该都给出相同的答案,不同之处在于算法。如果对此使用DFS,则将得到相同的答案Level 2ab,而没有g

c

来自评论:

  

好的,我知道了,所以算法的实际工作是在我打印出包括c的代码时进行的,但是当它想打印最终路径时,它将排除c,因为它不在路径上。

我不同意。

运行带有SWI-Prolog中跟踪功能的查询

dfs_1(N,N,[N]).
dfs_1(Start,End,[Start|Rest] ) :-
    s(Start,Next),
    dfs_1(Next,End,Rest).

?- dfs_1(a,g,Path).
Path = [a, b, g] ;
false.

line   1   [trace] 133 ?- solve_1(a,S).
line   2      Call: (8) solve_1(a, _12870)
line   3      Call: (9) breadthfirst_1([[a]], _12870)
line   4      Call: (10) goal_1(a)
line   5      Fail: (10) goal_1(a)
line   6      Redo: (9) breadthfirst_1([[a]], _12870)
line   7   ^  Call: (10) bagof([_13076, a],  (s(a, _13076), \+member(_13076, [a])), _13134)
line   8      Call: (17) s(a, _13076)
line   9      Exit: (17) s(a, b)
line  10      Call: (17) lists:member(b, [a])
line  11      Fail: (17) lists:member(b, [a])
line  12      Redo: (17) s(a, _13076)
line  13      Exit: (17) s(a, c)
line  14      Call: (17) lists:member(c, [a])
line  15      Fail: (17) lists:member(c, [a])
line  16   ^  Exit: (10) bagof([_13076, a], user:(s(a, _13076), \+member(_13076, [a])), [[b, a], [c, a]])
line  17      Call: (10) lists:append([], [[b, a], [c, a]], _13216)
line  18      Exit: (10) lists:append([], [[b, a], [c, a]], [[b, a], [c, a]])
line  19      Call: (10) breadthfirst_1([[b, a], [c, a]], _12870)
line  20      Call: (11) goal_1(b)
line  21      Fail: (11) goal_1(b)
line  22      Redo: (10) breadthfirst_1([[b, a], [c, a]], _12870)
line  23   ^  Call: (11) bagof([_13198, b, a],  (s(b, _13198), \+member(_13198, [b, a])), _13256)
line  24      Call: (18) s(b, _13198)
line  25      Exit: (18) s(b, g)
line  26      Call: (18) lists:member(g, [b, a])
line  27      Fail: (18) lists:member(g, [b, a])
line  28      Redo: (18) s(b, _13198)
line  29      Exit: (18) s(b, f)
line  30      Call: (18) lists:member(f, [b, a])
line  31      Fail: (18) lists:member(f, [b, a])
line  32   ^  Exit: (11) bagof([_13198, b, a], user:(s(b, _13198), \+member(_13198, [b, a])), [[g, b, a], [f, b, a]])
line  33      Call: (11) lists:append([[c, a]], [[g, b, a], [f, b, a]], _13350)
line  34      Exit: (11) lists:append([[c, a]], [[g, b, a], [f, b, a]], [[c, a], [g, b, a], [f, b, a]])
line  35      Call: (11) breadthfirst_1([[c, a], [g, b, a], [f, b, a]], _12870)
line  36      Call: (12) goal_1(c)
line  37      Fail: (12) goal_1(c)
line  38      Redo: (11) breadthfirst_1([[c, a], [g, b, a], [f, b, a]], _12870)
line  39   ^  Call: (12) bagof([_13338, c, a],  (s(c, _13338), \+member(_13338, [c, a])), _13396)
line  40      Call: (19) s(c, _13338)
line  41      Exit: (19) s(c, r)
line  42      Call: (19) lists:member(r, [c, a])
line  43      Fail: (19) lists:member(r, [c, a])
line  44      Redo: (19) s(c, _13338)
line  45      Exit: (19) s(c, e)
line  46      Call: (19) lists:member(e, [c, a])
line  47      Fail: (19) lists:member(e, [c, a])
line  48   ^  Exit: (12) bagof([_13338, c, a], user:(s(c, _13338), \+member(_13338, [c, a])), [[r, c, a], [e, c, a]])
line  49      Call: (12) lists:append([[g, b, a], [f, b, a]], [[r, c, a], [e, c, a]], _13490)
line  50     Exit: (12) lists:append([[g, b, a], [f, b, a]], [[r, c, a], [e, c, a]], [[g, b, a], [f, b, a], [r, c, a], [e, c, a]])
line  51      Call: (12) breadthfirst_1([[g, b, a], [f, b, a], [r, c, a], [e, c, a]], _12870)
line  52      Call: (13) goal_1(g)
line  53      Exit: (13) goal_1(g)
line  54      Exit: (12) breadthfirst_1([[g, b, a], [f, b, a], [r, c, a], [e, c, a]], [g, b, a])
line  55      Exit: (11) breadthfirst_1([[c, a], [g, b, a], [f, b, a]], [g, b, a])
line  56      Exit: (10) breadthfirst_1([[b, a], [c, a]], [g, b, a])
line  57      Exit: (9) breadthfirst_1([[a]], [g, b, a])
line  58      Exit: (8) solve_1(a, [g, b, a])
line  59   S = [g, b, a] ;
line  60      Redo: (12) breadthfirst_1([[g, b, a], [f, b, a], [r, c, a], [e, c, a]], _12870)
line  61   ^  Call: (13) bagof([_13484, g, b, a],  (s(g, _13484), \+member(_13484, [g, b, a])), _13542)
line  62      Call: (20) s(g, _13484)
line  63      Fail: (20) s(g, _13484)
line  64   ^  Fail: (13) bagof([_13484, g, b, a], user:(s(g, _13484), \+member(_13484, [g, b, a])), _13548)
line  65      Redo: (12) breadthfirst_1([[g, b, a], [f, b, a], [r, c, a], [e, c, a]], _12870)
line  66      Call: (13) breadthfirst_1([[f, b, a], [r, c, a], [e, c, a]], _12870)
line  67      Call: (14) goal_1(f)
line  68      Fail: (14) goal_1(f)
line  69      Redo: (13) breadthfirst_1([[f, b, a], [r, c, a], [e, c, a]], _12870)
line  70   ^  Call: (14) bagof([_13484, f, b, a],  (s(f, _13484), \+member(_13484, [f, b, a])), _13542)
line  71      Call: (21) s(f, _13484)
line  72      Fail: (21) s(f, _13484)
line  73   ^  Fail: (14) bagof([_13484, f, b, a], user:(s(f, _13484), \+member(_13484, [f, b, a])), _13548)
line  74      Redo: (13) breadthfirst_1([[f, b, a], [r, c, a], [e, c, a]], _12870)
line  75      Call: (14) breadthfirst_1([[r, c, a], [e, c, a]], _12870)
line  76      Call: (15) goal_1(r)
line  77      Fail: (15) goal_1(r)
line  78      Redo: (14) breadthfirst_1([[r, c, a], [e, c, a]], _12870)
line  79   ^  Call: (15) bagof([_13484, r, c, a],  (s(r, _13484), \+member(_13484, [r, c, a])), _13542)
line  80      Call: (22) s(r, _13484)
line  81      Fail: (22) s(r, _13484)
line  82   ^  Fail: (15) bagof([_13484, r, c, a], user:(s(r, _13484), \+member(_13484, [r, c, a])), _13548)
line  83      Redo: (14) breadthfirst_1([[r, c, a], [e, c, a]], _12870)
line  84      Call: (15) breadthfirst_1([[e, c, a]], _12870)
line  85      Call: (16) goal_1(e)
line  86      Fail: (16) goal_1(e)
line  87      Redo: (15) breadthfirst_1([[e, c, a]], _12870)
line  88   ^  Call: (16) bagof([_13484, e, c, a],  (s(e, _13484), \+member(_13484, [e, c, a])), _13542)
line  89      Call: (23) s(e, _13484)
line  90      Fail: (23) s(e, _13484)
line  91   ^  Fail: (16) bagof([_13484, e, c, a], user:(s(e, _13484), \+member(_13484, [e, c, a])), _13548)
line  92      Redo: (15) breadthfirst_1([[e, c, a]], _12870)
line  93      Call: (16) breadthfirst_1([], _12870)
line  94      Fail: (16) breadthfirst_1([], _12870)
line  95      Fail: (15) breadthfirst_1([[e, c, a]], _12870)
line  96      Fail: (14) breadthfirst_1([[r, c, a], [e, c, a]], _12870)
line  97      Fail: (13) breadthfirst_1([[f, b, a], [r, c, a], [e, c, a]], _12870)
line  98      Fail: (12) breadthfirst_1([[g, b, a], [f, b, a], [r, c, a], [e, c, a]], _12870)
line  99      Fail: (11) breadthfirst_1([[c, a], [g, b, a], [f, b, a]], _12870)
line 100      Fail: (10) breadthfirst_1([[b, a], [c, a]], _12870)
line 101      Fail: (9) breadthfirst_1([[a]], _12870)
line 102      Fail: (8) solve_1(a, _12870)
line 103   false.

它显示了访问起始节点line 7 ^ Call: (10) bagof([_13076, a], (s(a, _13076), \+member(_13076, [a])), _13134) 的结果,起始节点a只是路径a,并且在已知路径列表中是[a]

line  16   ^  Exit: (10) bagof([_13076, a], user:(s(a, _13076), \+member(_13076, [a])), [[b, a], [c, a]])

对于路径[a],通过使用列表顶部的项目并访问其中一个未被访问的邻居,然后将该新路径添加到新列表中,来创建一个新列表。

所以 使用路径为[a]并使用列表a开头的项目访问其邻居之一s(a,b),并将新路径添加到新列表[[b,a]]中。

使用路径[a],并使用列表a开头的项目,访问其邻居之一s(a, c).,并将新路径添加到新列表{{1} }。

[[b,a],[c,a]]

对于路径line 32 ^ Exit: (11) bagof([_13198, b, a], user:(s(b, _13198), \+member(_13198, [b, a])), [[g, b, a], [f, b, a]]) ,通过使用列表顶部的项目并访问其中一个未被访问的邻居,然后将该新路径添加到新列表中,来创建一个新列表。

所以 使用路径为[b, a]并使用列表[b, a]开头的项目访问其邻居之一b,并将新路径添加到新列表s(b, g).中。

使用路径[[g, b, a]],并使用列表[b, a]开头的项目,访问其邻居之一b,并将新路径添加到新列表s(b, f).中。

请注意,答案[[g, b, a], [f, b, a]]现在在新列表中,但路径中没有[g, b, a]

c

所有路径均已创建

line  50     Exit: (12) lists:append([[g, b, a], [f, b, a]], [[r, c, a], [e, c, a]], [[g, b, a], [f, b, a], [r, c, a], [e, c, a]])

使用所有[[g, b, a], [f, b, a]], [[r, c, a], [e, c, a]], [[g, b, a], [f, b, a], [r, c, a], [e, c, a]] 事实,但答案为s/2的路径中却没有[g, b, a]


  

为什么从conc / 3更改为append / 3?

即使这不是您的问题之一,我也需要为阅读此问题的其他人回答,尤其是第一次自行学习Prolog的人。

这是我与cconc/3相关的事件的版本,如果还有其他故事可以说明的话;如果需要的话,我什至会把它作为已发布的问题来询问。

关于入门学习/教学的最好甚至最好的书之一是Ivan Bratko撰写的“人工智能的Prolog编程”。 (WorldCat)(Amazon)。

当初次学习Prolog时,人们倾向于在列表数据结构中度过一生,并获得append/3的健康饮食,但在书中,他选择让学生创建自己的append/3版本并将其命名为append/3。因此,整本书都使用conc/3,而几乎没有使用conc/3。现在,这些人已经习惯了append/3并开始使用它编写代码,发布代码等,并且它具有很高的感染力,您碰巧发现了它。因此,我为您的代码提供了补救措施。


  

输出反转的问题。

使用递归解决问题时,通常将中间结果存储在堆栈中。根据堆栈上的顺序,结果是正确的顺序,也可以是相反的顺序。

有几种方法可以使递归结果以正确的顺序返回。

对于大多数初学者来说,是获得结果,并且如果需要的话,只需反转结果即可。对于Prolog,如果结果是列表,则reverse/2有效。

conc/3

reverse / 2的谓词

?- reverse([g, b, a],R).
R = [a, b, g].

当您遇到更大的问题时,即使在不同的谓词调用之间不断颠倒结果也会开始增加时间。在函数式编程中尤其如此。

另一种方法是通过累加器。为了说明这一点,我将使用这个简单的反向版本。

?- listing(reverse/2).
lists:reverse(A, B) :-
        reverse(A, [], B, B).

true.

?- listing(reverse/4).
lists:reverse([], A, A, []).
lists:reverse([B|A], C, D, [_|E]) :-
        reverse(A, [B|C], D, E).

true.

第一个子句将累加器初始化为空列表reverse_2(L,Result) :- accumulator_reverse(L,[],Result). accumulator_reverse([],A,A). accumulator_reverse([H|T],A,Result) :- accumulator_reverse(T,[H|A],Result). ,以与[]一起使用

accumulator_reverse/3

其他两个子句仅以该子句为基本情况递归处理列表

reverse_2(L,Result) :-
    accumulator_reverse(L,[],Result).

,即输入列表为空(accumulator_reverse([],A,A). )和

此条款

[]

将列表拆开accumulator_reverse([H|T],A,Result) :- accumulator_reverse(T,[H|A],Result). (又称为AKA解构函数),并将头[H|T]附加到累加器H(又称为AKA构造函数)上。

通过

处理列表
[H|A]

原始列表变得越来越小,因为总是从列表中删除头,并且累加器不断增长,因为列表中的头总是被添加到累加器。由于解构列表的顺序(从前到后)和构建累加器的顺序(从后到前),列表变得相反。

通常,当您查看重构的Prolog代码时,如果您看到递归和类似

的基本情况,
accumulator_reverse([H|T],A,Result) :-
    accumulator_reverse(T,[H|A],Result).

其中一个参数是一个空列表或bottom,另外两个参数相同,但是一个参数在调用时绑定,另一个在调用时取消绑定,看看如果累加器传递已纳入代码中。