在人工智能领域,经常使用到搜索技术。常见的搜索方式有深度优先搜索与广度优先搜索两种。这里用到了一些较为专业的名词,如果你不太明白请你仔细阅读下面的例子。
问题:树的搜索
树在计算机科学领域是一种数据结构的概念。
树中的字母表示树的节点,节点a叫做树的根,节点b、c、d叫做节点a的子节点。b、c、d又分别有它们的子节点。树的搜索的意思就是要找到一条连接两个节点的路径,例如连接节点a与g的路径是a-c-g。
上面的例子是显而易见的,不过要想让计算机也能够找到这条路径,就必须编程解决。下面介绍它的Prolog程序的编法。
树的表达
首先我们需要把上面的树翻译为Prolog的语言,这不难办到,只要使用事实就可以轻易地搞定:
sub(a,b).
sub(a,c).
sub(a,d).
sub(b,e).
sub(c,f).
sub(c,g).
sub(d,i).
sub(d,h).
上面的每一条事实对应树中的一条树枝。
树的搜索
先给出程序:
route(X,X,[]).
route(X,Y,L):-
sub(X,Z),%首先找到X的一个子节点Z,
route(Z,Y,L1),%在寻找从Z到目的节点Y的路由L1,
L=[Z|L1].%最终的路由表为Z加上L1.
?-route(a,f,L).
L=[c,f];
no
我们可以看出,上面的route/3是使用递归的方法书写的。前两个参数为要考虑的节点,第三个参数储存找出来的路由表。route的第一个子句为边界条件;第二个子句递归调用route/3本身。
在这个程序中,当找到X的一个子节点后,系统就开始寻找这个子节点的子节点,一直到找到目标,搜索就成功地结束,而如果某个节点没有子节点,就会引起回溯,去寻找它的父节点的另一个子节点。这种搜索方式就叫做深度优先搜索。由于Prolog内建的自动回溯功能,使得我们可以非常容易地实现深度搜索。所以在一般情况下,通常使用深度搜索,而只在特殊的情况下才使用广度搜索(我们在以后的章节介绍)。
有向图
如果在前面的事实中增加一条sub(d,g),那么g既是d的子节点,又是c得子节点。我们把这样的树叫做有向图。有向图的搜索与树的搜索过程是相同的。
图的搜索
图是最复杂的数据结构了。在图中不分什么父节点与子节点,每个节点都与一定数量的其它节点相连,一条连接叫做一条路经。每条路径都有不同的权值,从某个节点到另外的节点所走过的路径就叫做这两个节点之间的通路。图的搜索的目的就是要找出这些通路,有时还需要所有的权值加起来最小。图的最典型的例子就是地理上的地图了,每个城市相当于一个节点,城市之间的道路就是路径了。搜索工作就是一个旅游向导,它能够帮助你决定到达另外一个城市所需的路径。
图的搜索和树的搜索是类似的,不过由于图中存在着环路,所以如果不加以控制,而直接使用前面的程序就会导致永远不会结束。
解决环路的办法也很简单:使用一个列表储存已经考虑过的节点,如果正在考虑的节点已经在此列表中就表明没有必要再搜索此节点了。下面是完整的Prolog程序。
c(a,b).%图的连接信息由事实给出。
c(a,c).
c(a,d).
c(c,b).
c(b,e).
c(e,f).
c(f,b).
c(b,d).
member(A,[A|X]).%列表谓词,见前面的章节。
member(A,[B|X]):-member(A,X).
connect(X,Y):-%由于连接是双向的,所以使用connect/2谓词来表示。
c(X,Y);c(Y,X).%其中“;”表示或者的意思。
findroad(X,X,L,L).%递归的边界条件。
findroad(X,Y,L,L1):-%L为储存的路由表。
connect(X,Z),
not(member(Z,L)),%X所连接的节点Z不在已经储存的路由表中。
findroad(Z,Y,[Z|L],L1).
谓词c/2定义了图的信息,你可以自己在纸上画画。findroad/4为寻找路由的谓词,它的调用方式为findroad(a,e,[],X)。第一个子句定义了边界条件,它用来把第三个参数传给第四个参数,第三个参数为临时的路由表,第四个参数为最终的路由表。第二个子句先找出与X相连的节点Z,然后判断Z是否在临时的路由表中,如果不在,就把Z加入到临时路由表中,并且寻找从Z到Y的路由。下面是运行结果:
?-findroad(a,e,[],L),write(L),nl,fail.
[e,b]
[e,f,b]
[e,b,c]
[e,f,b,c]
[e,b,a,c]
[e,f,b,a,c]
[e,b,d,a,c]
[e,f,b,d,a,c]
[e,b,a,d]
[e,f,b,a,d]
[e,b,c,a,d]
[e,f,b,c,a,d]
[e,b,d]
[e,f,b,d]
no
这里有两点需要说明:第一,我们找出来的路由表是倒过来的,不过这并不影响结果;第二,这个程序找出了所有的通路,但是并不能判断哪条通路是最短通路,另外我们还没有加入路径的权值。这些内容就交给你了。