1
d={'a':['b','d'],'b':['c'],'c':['d']}

def path(d,consider,end,route=''):
    if consider==end:

        return route
    else:
        for e in d[consider]:
            print(e)
            route = path(d,e,end,route)
            route=str(e)+route
            print route

            return route

path(d,'a','d') prints:

b
c
d
d
cd
bcd
Out[89]:
'bcd'

Why does it not print the second consideration of 'a' i.e 'd'. it outputs the branch for 'b' but does not seem to explore 'd' even though it is supposed to by the for loop.

2
  • is your last return indeed inside the for loop? Commented Feb 12, 2018 at 12:09
  • 1
    You have a return in your for loop. That means your loop will only run one iteration, and then the function is exited. Commented Feb 12, 2018 at 12:09

4 Answers 4

1

You can also use breadth-first search to find the shortest path:

import collections
def path(d, consider, end):
  queue = collections.deque([consider])
  seen = []
  flag = False
  while queue:
    val = queue.popleft()
    if val == end:
      return seen
    seen.append(val)
    queue.extend([i for i in d[val] if i not in seen])
  return flag

result = path({'a':['b','d'],'b':['c'],'c':['d']}, 'a', 'd')
result = result if result else []
print(result)

Output:

['a', 'b']
Sign up to request clarification or add additional context in comments.

Comments

1

You have two possible routes from "a" to "d": the short one ("a"=>"d") given in d['a'][1], and the long one ("a" => "b" => "c" => "d") computed by recursion from d['a'][0].

The problem is that your path function only "considers" the first element for a given key:

    for e in d[consider]:
        route = path(d,e,end,route)
        route=str(e)+route
        # this return here means we stop at `d[consider][0]`
        # and never checks `d[consider][1:]` 
        return route

Actually your function should return a list of all possible routes (instead of the single first possible one) so the caller can select the shortest one (or the longest one or whatever you like)

As a side note, I would not build routes from string concatenations but as lists themselves so there's no ambiguity when someone starts using names with one than more letter as keys in d.

Comments

1

The problem is that you unconditionally return the route in the very first iteration of the loop, i.e. after following the first branch, regardless of whether that actually led to end. Instead, you have to check whether the route returned is not None (None being implicitly returned after the loop). You also should check whether consider in d before starting the loop. Also, you should not overwrite the original value of route inside the loop. In fact, the route parameter is useless, as it will ever only be "".

def path(d, consider, end):
    if consider == end:
        return ''
    elif consider in d:
        for e in d[consider]:
            res = path(d, e, end)
            if res is not None:
                return e + res

If you want all the routes that lead to end, use yield instead of return. In this case, the condition is not needed, as this will only yield valid paths in the first place, and never None, but you need another loop to iterate the different successful paths, if any.

def path(d, consider, end):
    if consider == end:
        yield ''
    elif consider in d:
        for e in d[consider]:
            for res in path(d, e, end):
                yield e + res

This way, list(path(d,'a','d')) results in ['bcd', 'd']

Note, however, that on a cyclic graph this may lead to an infinite recursion, even if there is a short path to the goal, depending on the layout of the graph. You could counter this by either passing an ever-growing set of already visited nodes in each iteration, or popping visited nodes from the graph d itself, or by including a counter of already visited nodes, and stopping if that counter is higher than the number of nodes in the graph.

1 Comment

This will still stop at the first route found - like it already does - even when there are more than one possible route.
0

Move the return?

def path(d,consider,end,route=''):
    if consider==end:
        return route
    else:
        for e in d[consider]:
            print(e)
            route = path(d, e, end, route)
            route=str(e)+route
            print(route)

        return route

5 Comments

"Move the return?" Yes, but not there.
@tobias_k not there? Where else?
@Alfe If you have it just inside the loop, it returns the very first path, and after the loop, it returns the last path, regardless of whether those led to the goal or not.
@tobias_k Yeah, maybe the algorithm is only called when a solution exists.
@Alfe But even then, without a check, it will just find any (non-)solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.