简体   繁体   中英

Possible problem with python dijkstra's algorithm implementation using classes

So I have been trying to create a graph program in python with classes that has dfs, bfs and dijkstra's algorithmn implemented within it and so far have come up with:

class Vertex:
    def __init__(self, name):
        self.name = name
        self.connections = {}

    def addNeighbour(self, neighbour, cost):
        self.connections[neighbour] = cost


class Graph:
    def __init__(self):
        self.vertexs = {}

    def addVertex(self, newVertex):
        new = Vertex(newVertex)
        self.vertexs[newVertex] = new

    def addEdge(self, src, dest, cost):
        self.vertexs[src].addNeighbour(self.vertexs[dest], cost)

    def dfs(self, start, end, visited):
        visited[start] = True
        print(start, end=' ')
        if start == end:
            # End node found
            return True
        else:
            # Use depth first search
            for connection in graph.vertexs[start].connections:
                if visited[connection.name] == False:
                    if self.dfs(connection.name, end, visited) == True:
                        # Return true to stop extra nodes from being searched
                        return True

    def bfs(self, start, end, visited, queue):
        if len(queue) == 0:
            # Queue is empty
            queue.append(start)
        visited[start] = True
        currentNode = queue.pop(0)
        print(currentNode, end=' ')
        if start == end:
            # End node found
            return True
        else:
            # Do breadth first search
            for connection in graph.vertexs[currentNode].connections:
                if visited[connection.name] == False:
                    # Queue all its unvisited neighbours
                    queue.append(connection.name)
            for newNode in queue:
                self.bfs(newNode, end, visited, queue)

    def dijkstra(self, current, currentDistance, distances, visited, unvisited):
        for neighbour, distance in distances.items():
            if neighbour.name not in unvisited:
                continue
            newDistance = currentDistance + distance
            if unvisited[neighbour.name] is None or unvisited[neighbour.name] > newDistance:
                unvisited[neighbour.name] = newDistance
        visited[current] = currentDistance
        del unvisited[current]
        if not unvisited:
            return True
        candidates = [node for node in unvisited.items() if node[1]]
        current, currentDistance = sorted(candidates)[0]
        self.dijkstra(current, currentDistance, graph.vertexs[current].connections, visited, unvisited)
        return visited


def setup():
    graphList = {
        # Node number: [destination number, cost]
        0: {4: 6, 6: 1},
        1: {6: 2},
        2: {0: 9, 1: 4, 3: 3},
        3: {4: 7},
        4: {1: 3, 5: 5},
        5: {0: 2, 1: 6, 4: 3},
        6: {2: 4, 3: 6}
    }
    graph = Graph()

    for i in range(len(graphList)):
        graph.addVertex(i)

    for dictLength in range(len(graphList)):
        for key in list(graphList[dictLength].keys()):
            graph.addEdge(dictLength, key, graphList[dictLength][key])
    return graph, graphList


graph, graphList = setup()


print("DFS travsersal path from node 1 to node 0:")
graph.dfs(1, 0, [False] * len(graphList))
print()

print("BFS traversal path from node 1 to node 0:")
graph.bfs(1, 0, [False] * len(graphList), [])
print()

print("Shortest possible path from node 1 to 0:")
result = graph.dijkstra(1, 0, graph.vertexs[2].connections, {}, {node: None for node in graphList})
cost = result[len(result) - 1]
path = " ".join([str(arrInt) for arrInt in list(result.keys())])
print(path, "costing", cost)

But there seems to be a problem with the output I think. If I wanted to travel from node 1 to node 0, the current output is:

DFS travsersal path from node 1 to node 0: 1 6 2 0 BFS traversal path from node 1 to node 0: 1 6 2 3 0 3 4 5 Shortest possible path from node 1 to 0: 1 0 3 4 5 6 2 costing 10

However, I think the output should be:

DFS travsersal path from node 1 to node 0: 1 6 2 0 4 5 3 BFS traversal path from node 1 to node 0: 1 6 2 3 0 4 5 Shortest possible path from node 1 to 0: 1 6 2 0 costing 15

Can anyone see any problems with this?

Thanks!

There are actually several issues in your code:

  1. You need to specify to your Djikstra's algorithm where to stop, in your code it is not mentioned what is the end node (in your example it should be 0)

  2. Computing the cost as cost = result[len(result) - 1] does not get you the last element in a dictionary (dictionaries are usually not ordered, so the "last element" does not even exist!). You should retrieve the cost as cost = result[end] , where end is the final node, 0 in your example.

  3. You are calling the function as result = graph.dijkstra(1, 0, graph.vertexs[2].connections, {}, {node: None for node in graphList}) , however, the third argument of this function should be the set of neighbours of the initial node, so it should be graph.vertexs[1].connections in your case.

In summary, to make the code work as expected, you can modify the function as follows:

def dijkstra(self, current, currentDistance, distances, visited, unvisited, end):
    for neighbour, distance in distances.items():
        if neighbour.name not in unvisited:
            continue
        newDistance = currentDistance + distance
        if unvisited[neighbour.name] is None or unvisited[neighbour.name] > newDistance:
            unvisited[neighbour.name] = newDistance
    visited[current] = currentDistance

    if current == end:
      return visited

    del unvisited[current]
    if not unvisited:
        return True
    candidates = [node for node in unvisited.items() if node[1]]
    current, currentDistance = sorted(candidates)[0]
    
    self.dijkstra(current, currentDistance, graph.vertexs[current].connections, visited, unvisited, end)
    return visited

And call it as follows:

print("Shortest possible path from node 1 to 0:")
start = 1
end = 0
result = graph.dijkstra(start, 0, graph.vertexs[start].connections, {}, {node: None for node in graphList}, end)
cost = result[end]
path = " ".join([str(arrInt) for arrInt in list(result.keys())])
print(path, "costing", cost)

By doing this, the output becomes

Shortest possible path from node 1 to 0: 1 6 2 0 costing 15

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM