我的问题是我想检查两个节点是否连接。
我的知识库是,
edge(a,b):-!.
edge(b,a):-!.
edge(a,e):-!.
edge(e,a):-!.
edge(b,c):-!.
edge(c,b):-!.
edge(b,d):-!.
edge(d,b):-!.
edge(c,e):-!.
edge(e,c):-!.
edge(d,e):-!.
edge(e,d):-!.
edge(a,f):-!.
edge(f,a):-!.
isConnected(X,X):-!.
isConnected(X,Y):-edge(X,Y),!.
isConnected(X,Z):-not(edge(X,Y)),edge(X,Y),isConnected(Y,Z),!.
isConnected(X,Z):not(edge(X,Y)),edge(X,Z),not(isConnected(Y,Z)),isConnected(Z,Y),!.
答案 0 :(得分:2)
哇那里。这是很多削减。削减有时在Prolog中有助于修剪不必要的答案,但是如果你有这样的事实:
edge(a, b).
将其写成:
,绝对没有任何好处edge(a, b) :- !.
毕竟,那里没有选择点,因为那里没有变量,也没有替代解决方案。首先,让我们通过删除所有这些削减来解决您的事实。
接下来让我们来看看isConnected说的是什么。让我们用英语大声朗读,看看它是否有意义。
前两个似乎很合理。第三个似乎立刻自相矛盾。 not(edge(X, Y))
如何edge(X, Y)
同时成为真?请记住,Prolog中的逗号表示和,而不仅仅是然后。该条款的其余部分毫无意义,因为这两个条件不可能都是真的。你的意思可能是这样的:如果从X到Y有一条边并且Y连接到Z,则X连接到Z.这在Prolog中看起来像这样:
isConnected(X, Z) :- edge(X, Y), isConnected(Y, Z).
从逻辑上讲,这肯定是正确的,但是对于任何类型的复杂图形来说,这将是非常昂贵的计算,因为检查X是否连接到Z可能意味着检查Y是否连接到Z以用于所有相同的节点。
您的第四个条款有一个错字,因为:
应该是:-
。更重要的是,看起来你正试图在这里补偿边缘的方向性。一个更好的地方就是第2步,提供两种情况:
isConnected(X, Y) :- edge(X, Y) ; edge(Y, X).
我不确定,根据你的事实数据库,你是否真的是这个意思;你已经复制了所有的事实来解释这两个方向。如果事实数据库表示有向图,则这可能是必要的,并且规则不应该尝试反转节点。如果它代表一个无向图,你的谓词应该用这样的规则来检查它,它检查双方,你应该只列出每个边一次。两种方式都是这样,你告诉Prolog做一堆不必要的工作,因为它首先检查edge(a, b)
,然后规则将其反转为edge(b, a)
,然后转到下一个事实{{1然后规则将其反转为edge(b, a)
,实际上检查所有内容两次。
这里房间里的大象是,即使你成功地把它变成了问题的合理解决方案,它也会非常低效。有一些算法可以确定是否有两个连接的东西跟踪已经看到的东西和没有看到的东西,我认为你实现其中之一会好得多。