Estruturas e caminhos em grafos em Prolog

De Augusto Baffa Wiki
Revisão de 19h00min de 6 de novembro de 2020 por Abaffa (discussão | contribs)
(dif) ← Edição anterior | Revisão atual (dif) | Versão posterior → (dif)
Ir para navegação Ir para pesquisar

Como exemplo, considere o seguinte grafo conectado:

Fig. 2.15

As arestas podem ser representadas no Prolog como fatos:

edge(1,2).
edge(1,4).
edge(1,3).
edge(2,3).
edge(2,5).
edge(3,4).
edge(3,5).
edge(4,5).

Para representar o fato de que as arestas são bidirecionais, poderíamos adicionar mais oito cláusulas de 'arestas' (edge(2,1),etc.) ou poderíamos tentar uma regra como:

(*) edge(X,Y) :- edge(Y,X).

No entanto, esta não é uma boa ideia. Para ver por que não é uma boa ideia, tente o seguinte objetivo.

?- edge(5,1).

Observe que a regra (*) será tentada repetidamente em um loop infinito, de modo que o objetivo não será encerrado. Tente! A melhor maneira de lidar com isso é usar uma regra como a seguinte.

connected(X,Y) :- edge(X,Y) ; edge(Y,X).

Observe o uso da disjunção ';' nesta regra. Esta regra pode ter sido escrita como duas regras:

connected(X,Y) :- edge(X,Y).
connected(X,Y) :- edge(Y,X).

Queremos desenvolver uma definição de Prolog que gere caminhos entre quaisquer dois nós do grafo. Mais especificamente, exigimos o seguinte (tipo de) comportamento para o predicado 'paths'.

?- path(1,5,P).
P = [1,2,5] ;
P = [1,2,3,5] ;
P = [1,2,3,4,5] ;
P = [1,4,5] ;
P = [1,4,3,5] ;
P = [1,4,3,2,5] ;
P = [1,3,5] ;
P = [1,3,4,5] ;
P = [1,3,2,5] ;
no

Os caminhos são representados pela lista de nós através dos quais se deve viajar para ir do nó 1 ao nó 5. Aqui está uma definição para os caminhos:

path(A,B,Path) :-
       travel(A,B,[A],Q), 
       reverse(Q,Path).

travel(A,B,P,[B|P]) :- 
       connected(A,B).
travel(A,B,Visited,Path) :-
       connected(A,C),           
       C \== B,
       \+member(C,Visited),
       travel(C,B,[C|Visited],Path).


Uma leitura declarativa para a primeira cláusula equivale a "Um caminho de A para B é obtido se A e B estiverem conectados". Uma leitura declarativa para a segunda cláusula equivale a "Um caminho de A para B é obtido desde que A esteja conectado a um nó C diferente de B que não está na parte anteriormente visitada do caminho, e se continua encontrando um caminho de C para B". Evitar nós repetidos garante que o programa não funcionará continuamente.


Exercício 2.15 Suponha que as arestas tenham comprimentos. Como se calcula os caminhos mais curtos entre pontos no Prolog? Dica: pode-se usar o conjunto de objetivos, discutido na seção 2.12 e seção 4.15 ...

shortest(A,B,Path,Length) :-
   setof([P,L],path(A,B,P,L),Set),
   Set = [_|_], % fail if no path 
   minimal(Set,[Path,Length]).

Experimente este exercício (com a dica) antes de olhar para esta solução simplificada.

Veja Também