我一直试图写一个prolog程序而且我的bactracking有问题,这对我来说没有意义。这是我的一个条款,我遇到了麻烦:
edge(g1,a,b).
edge(g1,b,c).
edge(g1,a,c).
edge(g1,d,a).
edge(g1,d,b).
edge(g1,d,c).
spider(Grafo, AristasArana) :-
findall(edge(Grafo,X,Y),edge(Grafo,X,Y),AristasTotales),
member(edge(Grafo,X1,Y1),AristasTotales),
zpider(edge(Grafo,X1,Y1),AristasTotales,AristasArana),
length(AristasArana,L),
L > 2.
zpider(edge(Grafo,X1,Y1),AristasTotales,AristasArana) :-
member(edge(Grafo,A1,A2), AristasTotales),
X1 \= A1,
Y1 = A2,
findall(edge(Grafo,X,Y),(Y = A2),ListaAux1),
findall(edge(Grafo,X,Y),(X = A2),ListaAux2),
% Pueden haber repetidos y no se eliminan.
append(ListaAux2,ListaAux1,AristasArana).
zpider(edge(Grafo,X1,Y1),AristasTotales,AristasArana) :-
member(edge(Grafo,A1,A2), AristasTotales),
X1 \= A1,
Y1 = A1,
findall(edge(Grafo,X,Y),(Y = A1),ListaAux1),
findall(edge(Grafo,X,Y),(X = A1),ListaAux2),
% Pueden haber repetidos y no se eliminan.
append(ListaAux2,ListaAux1,AristasArana).
zpider(edge(Grafo,X1,Y1),AristasTotales,AristasArana) :-
member(edge(Grafo,A1,A2), AristasTotales),
X1 = A1,
Y1 \= A2,
findall(edge(Grafo,X,Y),(Y = A1),ListaAux1),
findall(edge(Grafo,X,Y),(X = A1),ListaAux2),
% Pueden haber repetidos y no se eliminan.
append(ListaAux2,ListaAux1,AristasArana).
zpider(edge(Grafo,X1,Y1),AristasTotales,AristasArana) :-
member(edge(Grafo,A1,A2), AristasTotales),
X1 = A2,
Y1 \= A2,
findall(edge(Grafo,X,Y),(Y = A2),ListaAux1),
findall(edge(Grafo,X,Y),(X = A2),ListaAux2),
% Pueden haber repetidos y no se eliminan.
append(ListaAux2,ListaAux1,AristasArana).
整个程序在它应该的时候并不统一,所以我尝试在gprolog上trace.
,这是我发现的第一个问题:
5 3 Exit: member(edge(g1,a,b),[edge(g1,a,b),edge(g1,b,c),edge(g1,a,c),edge(g1,d,a),edge(g1,d,b),edge(g1,d,c)]) ?
6 3 Call: a\=a ?
7 4 Call: a=a ?
7 4 Exit: a=a ?
6 3 Fail: a\=a ?
5 3 Redo: member(edge(g1,a,b),[edge(g1,a,b),edge(g1,b,c),edge(g1,a,c),edge(g1,d,a),edge(g1,d,b),edge(g1,d,c)]) ?
5 3 Exit: member(edge(g1,b,c),[edge(g1,a,b),edge(g1,b,c),edge(g1,a,c),edge(g1,d,a),edge(g1,d,b),edge(g1,d,c)]) ?
6 3 Call: a\=b ?
7 4 Call: a=b ?
7 4 Fail: a=b ?
6 3 Exit: a\=b ?
5 3 Redo: member(edge(g1,b,c),[edge(g1,a,b),edge(g1,b,c),edge(g1,a,c),edge(g1,d,a),edge(g1,d,b),edge(g1,d,c)]) ?
出于某种原因,在胜利之后6 3 Exit: a\=b ?
它将重做前一个句子,直到它失败并且我不明白为什么。
编辑: 如果有一个节点连接了3个或更多边缘,那么该计划就是统一程序(如果这些边缘来自或来自该节点则无关紧要)。 请注意,如果多个节点有三个或更多边连接,则可以多次统一。 正如你所看到的,边缘是作为事实给出的。
答案 0 :(得分:4)
首先,你大量使用这个“习语”:findall(Foo,Foo,T), member(Foo,T)
。只做Foo
你会更好。你将知识库实现为一个列表变量,大概是因为它对你来说比较熟悉,你认为它更容易使用,但它并没有真正为你买任何东西。它正在你的盘子上推动你的食物。特别是因为您没有使用可能证明工作合理性的T
做更好的事情,例如使用select/3
约束您的搜索。
其次,zpider/3
有四个子句。为什么?你正在做一些奇怪的故障,其中第一个节点和后续节点在第一个或第二个参数上匹配或不匹配。我认为你不清楚这些条款中的每一个都会执行一次,然后再回溯到。你没有从顺序运行所有这四个中受益。它们中的每一个代表一种替代解决方案。这就是为什么你有一个你不期望的回溯!
我总结你的代码非常广泛。但是,您遇到的部分问题是图表的节点都是解决方案,因此调试起来很困难。让我们制作一个更有趣的图表。我想出了这个,用graphviz语法:
graph g {
a -- c;
b -- c; b -- e;
c -- d; c -- e;
d -- e;
}
我突出显示绿色的两个节点,表示它们符合您的标准(三条边);其余的没有,所以如果我们成功,我们应该看到c
和e
作为我们回来的节点。
让我们尝试这样做效率低,只是为了好玩。我们需要三个链接,所以我们去具体了解它们。
uedge(Graph, X, Y) :- edge(Graph, X, Y).
uedge(Graph, X, Y) :- edge(Graph, Y, X).
thrice_connected(Graph, Node) :-
uedge(Graph, Node, A),
uedge(Graph, Node, B),
uedge(Graph, Node, C),
A \== B, B \== C, A \== C.
对于初学者,我定义了uedge/3
,它为我们提供了无向图边,我定义了thrice_connected/2
,它使用它来查找与此节点不同的三个连接。您可以通过运行它来看到它的工作原理,虽然效率不高,但是您会获得大量的重复值,因为A,B和C会以任何方式进行置换。所以,我在这里所做的工作有效,但并不是那么有效。这是低效的,因为每次失败时,它都会重新开始最后uedge/3
次搜索,直到完成所有这三次搜索。这对三个人来说很糟糕,只会越多越好!
为了更有效地执行此操作,您必须意识到您需要一个唯一的节点列表。在您的代码中,您需要努力为事实数据库创建edge/3
结构列表。你不需要重新创建你拥有的结构,你可以使用setof/3
创建一些更有用的东西,它偶然会为你带来唯一性:
?- setof(Adjacent, uedge(g, Node, Adjacent), AdjacentNodes).
Node = a,
AdjacentNodes = [c] ;
Node = b,
AdjacentNodes = [c, e] ;
Node = c,
AdjacentNodes = [a, b, d, e] ;
Node = d,
AdjacentNodes = [c, e] ;
Node = e,
AdjacentNodes = [b, c, d].
看看它,它几乎与解决方案非常接近。一个二阶查询说“给我一组Adjacent
个节点,定义为从Node
到Adjacent
的g边缘。”因为我们没有指定Node
,所以Prolog会生成它们,并且我们为图中的每个节点获得一个解决方案。完善!从这里解决方案几乎是微不足道的:
thrice_connected(Graph, Node) :-
setof(Adjacent, uedge(Graph, Node, Adjacent), AdjacentNodes),
length(AdjacentNodes, Len),
Len >= 3.
我们真的只是采用上一个查询,稍微概括一下并使其执行>= 3
测试。
?- thrice_connected(Graph, Node).
Graph = g,
Node = c ;
Graph = g,
Node = e.
它有效!