0

I am (or was supposed to, at least) make a Dijkstra implementation, but upon reviewing what I've done it looks more like a breadth-first search. But I wonder if I have come across a way to kind of do both things at the same time?

Essentially by using an OOP approach I can perform a BFS that also preserves knowledge of the shortest weighted path, thereby eliminating the need to determine during the search process whether some node has a lower cost than its alternatives like Dijkstra does.


I've searched for clues as to why a more "pure" implementation of Dijkstra should be faster than this, most prominently the answers in these two threads:

What is difference between BFS and Dijkstra's algorithms when looking for shortest path?
Why use Dijkstra's Algorithm if Breadth First Search (BFS) can do the same thing faster?

I haven't seen anything that made me understand the question to what I'm wondering, though.


My approach is essentially this:

  • Start at whatever node we require, this becomes a "path"
  • Step out to each adjacent node, and each of these steps create a new path that we store in a path collection
  • Every path contains an ordered list of which nodes it has visited from the starting node all the way to wherever it is, as well as the associated weight/cost
  • Mark the "parent" path as closed (stop iterating on it)
  • Select the first open path in the path collection and repeat the above
  • When there are no more open paths, delete all paths that didn't make it to the destination node
  • Compare the lengths and return the path with the lowest weight

I struggle to see where the performance difference between this and pure Dijkstra would come from. Dijkstra would still have to iterate over all possible paths, right? So I perform exactly the same number of steps with this implementation as if I change returnNextOpenPath() (serving the function of a normal queue, just not implemented as one) to a more priority queue-looking returnShortestOpenPath().

There's presumably a marginal performance penalty at the end where I examine all the collected, non-destroyed paths before I can print a result instead of just popping from a queue - but aside from that, am I just not seeing where else my implementation would also be worse than "pure" Dijkstra?

I don't think it matters, but in case it does: The actual code I have for this is gigantic so my first instinct is to hold off on posting it for now, but here's a stripped down version of it.

class DijkstraNode:
    def getNeighbors(self):
        # returns a Dict of all adjacent nodes and their cost

    def weightedDistanceToNeighbor(self, neighbor):
        # returns the cost associated with traversing from current node to the chosen neighbor node
    return int(self.getNeighbors()[neighbor])


class DijkstraEdge:
    def __init__(self, startingNode: DijkstraNode, destinationNode: DijkstraNode)
        self.start = startingNode
        self.goal  = destinationNode

    def weightedLength(self):
        return self.start.weightedDistanceToNeighbor(self.goal)


class DijkstraPath:
    def __init__(self, startingNode: DijkstraNode, destinationNode: DijkstraNode):
        self.visitedNodes: list[DjikstraNode] = [startingNode]
        self.previousNode = self.start
        self.edges: list[DijkstraEdge] = []

    def addNode(self, node: DijkstraNode)
        # if the node we're inspecting is new, add it to all the lists
        if not node in self.visitedNodes:        
            self.edges.append(DijkstraEdge(self.prevNode, node))
            self.visitedNodes.append(node)
            self.prevNode = node
        
        # if the node we just added above is our destination, stop iterating on this path
        if node = self.goal:
            self.closed = True
            self.valid  = True


class DijkstraTree:
    def bruteforceAllPaths(self, startingNode: DijkstraNode, destinationNode: DijkstraNode):
        self.pathlist = []
        self.pathlist.append(DjikstraPath(startingNode, destinationNode))

        cn: DjikstraNode

        # iterate over all open paths
        while self.hasOpenPaths():
            currentPath = self.returnNextOpenPath()
            neighbors: Dict = currentPath.lastNode().getNeighbors()

            for c in neighbors:
                cn = self.returnNode(c)

                # copy the current path 
                tmpPath = deepcopy(currentPath)

                # add the child node onto the newly made path
                tmpPath.addNode(cn)

                # add the new path to pathlist
                if tmpPath.isOpen() or tmpPath.isValid():
                    self.pathlist.append(tmpPath)

            # then we close the parent path
            currentPath.close()
Vegard
  • 3,587
  • 2
  • 22
  • 40

0 Answers0