1

I am writing a simple program in SWI-Prolog to run through a directed graph, from one node to the next. I'm having trouble avoiding cycles though and would like some help. Each edge has a cost associated with it, and each node has a "stay time".

edge(a, b, 10).
edge(a, c, 20).
edge(c, d, 50).
edge(c, b, 40).
edge(d, b, 30).
edge(d, a, 40).

stay(a, 10).
stay(c, 30).
stay(b, 15).
stay(d, 20). 

route(Start, End, Route, Cost) :-
  edge(Start, End, Cost),
  append([Start], [End], Route).

route(Start, End, Route, Cost) :-
  edge(Start, Next, FirstCost),
  stay(Next, StayTime),
  route(Next, End, NewRoute, SecondCost),
  Cost is FirstCost + SecondCost + StayTime,
  append([Start], NewRoute, Route).

I get the following output in swipl:

?- route(a,b,Routing,Cost).
Routing = [a, b],
Cost = 10 ;
Routing = [a, c, b],
Cost = 90 ;
Routing = [a, c, d, b],
Cost = 150 ;
Routing = [a, c, d, a, b],
Cost = 180 ;
Routing = [a, c, d, a, c, b],
Cost = 260 .

As you can see, after the 3rd route, the cycles begin to occur. I would like to avoid them, but I'm slightly unsure of how to do so.

false
  • 10,264
  • 13
  • 101
  • 209
Copephobia
  • 37
  • 5

1 Answers1

2

Here are some 'tricks of the trade' to make the program working, and more efficient: instead of appending, 'cons' the nodes in a accumulator, and also accumulate costs. This requires two more variables, but the resulting procedure is tail recursive, and Prolog can apply a basic optimization that make the program complexity linear in space.

route(Start, End, PathAcc, CostAcc, [End,Start|PathAcc], CostPath) :-
  edge(Start, End, CostEdge),
  CostPath is CostAcc + CostEdge.

route(Start, End, PathAcc, CostAcc, Path, Cost) :-
  edge(Start, Next, CostEdge),
  \+ memberchk(Next, PathAcc),
  stay(Next, StayTime),
  CostNext is CostAcc + CostEdge + StayTime,
  route(Next, End, [Start|PathAcc], CostNext, Path, Cost).

Then, to recover your original interface, we add the entry point :

route(Start, End, Route, Cost) :-
  route(Start, End, [], 0, RevRoute, Cost), reverse(RevRoute, Route).
CapelliC
  • 59,646
  • 5
  • 47
  • 90
  • Regarding tail-recursiveness: cf @mat's wrote in an answer (http://stackoverflow.com/a/17017920/4609915): "[...] *tail call (and hence also tail recursion) optimisation only helps if the predicate is deterministic* [...]" – repeat Nov 04 '15 at 07:12