0

I am newly learning Python, and I am trying to create a bfs algorithm that can take vertices of a weighted graph and return the bfs. Eventually, I will need to add the weighted edges to the vertices so that I can calculate the distance travelled, however I am able to get the bfs to work with my vertices alone. This is the code so far:

# Python implementation to find the
# shortest path in the graph using
# dictionaries

# Function to find the shortest
# path between two nodes of a graph
def BFS_SP(graph, start, goal):
    
    explored = []
    
    # Queue for traversing the
    # graph in the BFS
    queue = [[start]]
    
    # If the desired node is
    # reached
    if start == goal:
        print("Same Node")
        return
    
    # Loop to traverse the graph
    # with the help of the queue
    while queue:
        path = queue.pop(0)
        node = path[-1]
        
        # Condition to check if the
        # current node is not visited
        if node not in explored:
            neighbours = graph[node]
            
            # Loop to iterate over the
            # neighbours of the node
            for neighbour in neighbours:
                new_path = list(path)
                new_path.append(neighbour)
                queue.append(new_path)
                
                # Condition to check if the
                # neighbour node is the goal
                if neighbour == goal:
                    print("Shortest path = ", *new_path)
                    return
            explored.append(node)

    # Condition when the nodes
    # are not connected
    print("So sorry, but a connecting"\
                " path doesn't exist :(")
    return
# Driver Code
if __name__ == "__main__":
    
    # Graph using dictionaries
    graph = {
        'Arad':             ['Zerind', 'Timisoara', 'Sibiu'],
        'Bucharest':        ['Urziceni', 'Giurgiu', 'Pitesti', 'Fagaras'],
        'Craiova':          ['Dobreta', 'Pitesti', 'Rimnicu Vilcea'],
        'Dobreta':          ['Mehadia', 'Craiova'],
        'Eforie':           ['Hirsova'],
        'Fagaras':          ['Sibiu', 'Bucharest'],
        'Giurgiu':          ['Bucharest'],
        'Hirsova':          ['Eforie', 'Urziceni'],
        'Iasi':             ['Neamt', 'Vaslui'],
        'Lugoj':            ['Mehadia', 'Timisoara'],
        'Mehadia':          ['Lugoj', 'Dobreta'],
        'Neamt':            ['Iasi'],
        'Oradea':           ['Zerind', 'Sibiu'],
        'Pitesti':          ['Rimnicu Vilcea', 'Bucharest', 'Craiova'],
        'Rimnicu Vilcea':   ['Sibiu', 'Pitesti', 'Craiova'],
        'Sibiu':            ['Rimnicu Vilcea', 'Fagaras', 'Arad', 'Oradea'],
        'Timisoara':        ['Lugoj', 'Arad'],
        'Urziceni':         ['Bucharest', 'Hirsova'],
        'Vaslui':           ['Iasi', 'Urziceni'],
        'Zerind':           ['Oradea', 'Arad']
    }
    
    # Function Call
    BFS_SP(graph, 'Arad', 'Bucharest')

I need my graph array to actually look like this instead:

graph = {
    'Arad':             [['Zerind', 75], ['Timisoara', 118], ['Sibiu', 140]],
    'Bucharest':        [['Urziceni', 85], ['Giurgiu', 90], ['Pitesti', 101], ['Fagaras', 211]],
    'Craiova':          [['Dobreta', 120], ['Pitesti', 138], ['Rimnicu Vilcea', 146]],
    'Dobreta':          [['Mehadia', 75], ['Craiova', 120]],
    'Eforie':           [['Hirsova', 86]],
    'Fagaras':          [['Sibiu', 99], ['Bucharest', 211]],
    'Giurgiu':          [['Bucharest', 90]],
    'Hirsova':          [['Eforie', 86], ['Urziceni', 98]],
    'Iasi':             [['Neamt', 87], ['Vaslui', 92]],
    'Lugoj':            [['Mehadia', 70], ['Timisoara', 111]],
    'Mehadia':          [['Lugoj', 70], ['Dobreta', 75]],
    'Neamt':            [['Iasi', 87]],
    'Oradea':           [['Zerind', 71], ['Sibiu', 151]],
    'Pitesti':          [['Rimnicu Vilcea', 97], ['Bucharest', 101], ['Craiova', 138]],
    'Rimnicu Vilcea':   [['Sibiu', 80], ['Pitesti', 97], ['Craiova', 146]],
    'Sibiu':            [['Rimnicu Vilcea', 80], ['Fagaras', 99], ['Arad', 140], ['Oradea', 151]],
    'Timisoara':        [['Lugoj', 111], ['Arad', 118]],
    'Urziceni':         [['Bucharest', 85], ['Hirsova', 98]],
    'Vaslui':           [['Iasi', 92], ['Urziceni', 142]],
    'Zerind':           [['Oradea', 71], ['Arad', 75]]
}

And I'm some what of a loss on how I can modify my code to take the new version of graph as individual nodes. If I implement my new graph as I need it into my old code, I receive an error that say:

Traceback (most recent call last):
  File "/Users/meikebuettner/hello/astar_test.py", line 79, in <module>
    BFS_SP(graph, 'Arad', 'Bucharest')
  File "/Users/meikebuettner/hello/astar_test.py", line 30, in BFS_SP
    neighbours = graph[node]
TypeError: unhashable type: 'list'

Any insight, help, idea or topic to research would be GREATLY appreciated! Thank you!!

Prune
  • 76,765
  • 14
  • 60
  • 81
  • 1
    Can you clarify how the second value of each subarray (the digit) in your desired output is to be derived from both the key and the elements in the key's value? – Ajax1234 Jun 08 '21 at 22:46
  • 1
    Please supply the expected [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) (MRE). We should be able to copy and paste a contiguous block of your code, execute that file, and reproduce your problem along with tracing output for the problem points. This lets us test our suggestions against your test data and desired output. This code is not minimal, and you haven't explained the distance derivation. – Prune Jun 08 '21 at 22:50
  • For starters, reduce the code to the minimum necessary to exhibit the problem: I doubt that this requires a dgree-4 graph of 20 nodes. Why are you not using a graphing package? – Prune Jun 08 '21 at 22:51
  • 1
    @Prune just replace the graph initialization with the one given in the second block of code. Anyways, there is a problem which is simple - BFS does not necessarily report the shortest distance between two vertices in a graph, just *some* distance between the two vertices. – Jerry Halisberry Jun 08 '21 at 22:52
  • Why would this person use a graphing package? They said they are newly learning python (and probably algorithms in general). It doesn't make any sense to do that. – Jerry Halisberry Jun 08 '21 at 22:53
  • BFS is quite capable of returning the shortest distance: that detail is in the termination condition, not in the search order. – Prune Jun 08 '21 at 22:53
  • We expect you to perform basic diagnosis to include with your post. At the very least, print the suspected values at the point of error and trace them back to their sources. In many cases, doing this basic diagnosis will show you where the problem lies, and you won't need Stack Overflow at all. In this case, your distance-enhanced graph is of a different format: you have to pull the city name out of the reference: `node[0]` instead of merely `node`. – Prune Jun 08 '21 at 22:57
  • Your code is not yet clean enough for a simple substitution at line 30: you will later run into a case where you pass only the city name. This is the time to learn basic debugging: you wrote almost 80 lines of code, but haven't learned to trace it, even with simple `print` insertions. See this [lovely debugging site](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/) for help. – Prune Jun 08 '21 at 22:59
  • Are you suggesting to readd a vertex if you see it again, but with a shorter distance? I am pretty sure the runtime of this blows up, and anyways this modification is something I've never heard of before. How else would you find shortest paths in a graph with BFS? – Jerry Halisberry Jun 08 '21 at 23:01
  • 1
    I didn't suggest anything specific. Note, however, that doing what you suggest leads directly to Dijkstra's algorithm. – Prune Jun 08 '21 at 23:03
  • The point is that the question asker is probably not going to figure this out by themself by just messing around with a BFS algorithm - I am trying to help them realize this. My suggestion does not give you Dijkstra's, you need a priority queue for that and this is not a BFS anymore. – Jerry Halisberry Jun 08 '21 at 23:13

1 Answers1

0

The problem was caused by you adding nodes, which is a list in your new data structure, to new_path

for neighbour in neighbours: # neighbour: ['Rimnicu Vilcea', 97]
    ...
    new_path.append(neighbour)

new_path is expected to contain only the names of the nodes, not names and weights.

We can fix as:

for neighbour_node in neighbours:
    neighbour = neighbour_node[0]
    ...

Reasons for this suggestion:

  • neighbour_node is more correct name than neighbour in this case because it represents the node, not the name of the neigbour.
  • We can fix the comparison for neighbour and goal without modifying that line
# Condition to check if the
# neighbour node is the goal
if neighbour == goal:

Note: there are several ways to fix your code, mine is just one. You can try the others like changing the data structure of path and new_path.

Tuan Chau
  • 1,243
  • 1
  • 16
  • 30