Prolog Bactracking

时间:2016-12-13 23:40:57

标签: prolog backtracking

我一直试图写一个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个或更多边缘,那么该计划就是统一程序(如果这些边缘来自或来自该节点则无关紧要)。 请注意,如果多个节点有三个或更多边连接,则可以多次统一。 正如你所看到的,边缘是作为事实给出的。

1 个答案:

答案 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;
}

graph

我突出显示绿色的两个节点,表示它们符合您的标准(三条边);其余的没有,所以如果我们成功,我们应该看到ce作为我们回来的节点。

让我们尝试这样做效率低,只是为了好玩。我们需要三个链接,所以我们去具体了解它们。

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个节点,定义为从NodeAdjacent的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.

它有效!