I implemented Dijkstra's algorithm in python. I'm not used to posting, but I intend to write it as clearly as possible (a lie)
Having recently studied graph theory at university, I suddenly wanted to find the shortest path lol The code is dirty, but I hope it helps someone!
Dijkstra's algorithm is an algorithm that finds the "minimum weight path". The basic idea of Dijkstra's algorithm is as follows.
If you don't know what you're talking about, I recommend watching the video below! Explanation of Dijkstra's algorithm of Yobinori
I will introduce the code at once.
dijkstra.py
import heapq
class Map:  #Define Map class
    def __init__(self,start,goal):
        self.__start = start
        self.__goal = goal
        self.v_list =[] #[(0,s),(1,a)](weight,label) 
        self.e_list = {}  #Edge dictionary
        self.dic_v_list = []
        self.dic_e_list ={}
    #Initialize
    def add_list(self,cross_streets,load):
        for i in range(len(cross_streets)):
            if cross_streets[i]==self.__start:
                self.v_list.append((0,cross_streets[i]))
            else:
                self.v_list.append((float('inf'),cross_streets[i])) #float('inf')Is infinite(∞,'a')
        
        if self.__start <= self.__goal:
            self.e_list = load
        else:
            #(1,2):If there is 5(2,1):Add 5 to the dictionary
            for ld in load:
                self.e_list.update({(ld[1],ld[0]):load[ld]})
    
    #Function to add a way
    def load_add(self,weight,label): 
        #Determine if it is a start
        if weight != 0:
            #Find the same path as the label ex. label:2→(1,2,4)
            loads = [(k1,k2,val) for (k1,k2), val in self.e_list.items() if k2 ==label]
            #Find all paths that contain the specified label
            for load in loads:
                #Find the corresponding vertex and calculate the new weight
                for (v0,v1) in [(v0,v1) for (v0,v1) in self.dic_v_list if v1==load[0]]:
                        #Determine if the argument weight is equal to the path + vertex weight
                        if weight == v0+load[2]:
                            self.dic_e_list.update([((load[0],load[1]),load[2])])
    #Follow the path in reverse
    def back_calculation(self):
        #Substitute goal
        label=[self.__goal]
        #Follow the path from goal to start
        while label[len(label)-1] != self.__start:
            load = [(k1,k2,val) for (k1,k2), val in self.dic_e_list.items() if k2 == label[len(label)-1]] #k is, for example, s,a,b
            label.append(load[0][0])
        return label
    def dijkstra(self):
        #Loop until there are no vertices
        while len(self.v_list) > 0:
            #Find the minimum
            heapq.heapify(self.v_list)
            (weight, label) = heapq.heappop(self.v_list)
            #Seeking an adjacent road
            loads = [(k1,k2,val) for (k1,k2), val in self.e_list.items() if k1 == label]
            for load in loads:
                #Find matching weights in a list of pairs
                for (v0,v1) in [(v0,v1) for (v0,v1) in self.v_list if v1==load[1]]:   
                        #Update weights
                        if weight + load[2] <= v0:
                            list_count = self.v_list.index((v0,v1)) #Substitute the element number of the list
                            self.v_list[list_count] = (weight + load[2],v1)
            #Added fixed vertices and paths
            self.dic_v_list.append((weight,label))
            self.load_add(weight,label)
    def serach(self):
        self.dijkstra()
        return self.back_calculation()
cross_streets = [1,2,3,4,5,6,7,8]  #All intersections(vertex)List of
load = {(1,3):5,(1,2):1,(1,4):4,(2,4):2,(3,5):2,(4,5):5,(4,6):6,(5,8):1,(5,7):3,(6,7):2,(8,7):4} #All roads(Edge)Dictionary
map = Map(7,1)  #Create a Map instance
map.add_list(cross_streets,load) #Make a map
print(map.serach())
v_list is a tuple type list of labels and vertices, and e_list is a dictionary type of edges. (For example, if the length of the side between s-t is 10, it is expressed as {(s, t): 10}) dic_v_list and dic_e_list store vertices and edges with fixed minimum weight distances.
class Map:  #Define Map class
    def __init__(self,start,goal):
        self.__start = start
        self.__goal = goal
        self.v_list =[] #[(0,s),(1,a)](label,vertex) 
        self.e_list = {}  #Edge dictionary
        self.dic_v_list = []
        self.dic_e_list ={}
The vertices and paths are passed as follows:
cross_streets = [1,2,3,4,5,6,7,8]  #All intersections(vertex)List of
load = {(1,3):5,(1,2):1,(1,4):4,(2,4):2,(3,5):2,(4,5):5,(4,6):6,(5,8):1,(5,7):3,(6,7):2,(8,7):4} #All roads(Edge)Dictionary
map = Map(7,1)
This is the function that finds the most important path with the least weight in this program. The flow is as follows. ⑴ Use heapq to find the vertex with the smallest weight. Then take it out with heapq.pop. ⑵ Store the side extending from the extracted vertex in load as a list type. (3) Compare the weights of the vertices connected to each side, and update if the weights can be updated. (4) Add the confirmed vertices and edges to dic_v_list and dic_e_list.
In other words, this function is responsible for "finding the vertices with the least weight" and "updating the weights of adjacent vertices".
    def dijkstra(self):
        #Loop until there are no vertices
        while len(self.v_list) > 0:
            #Find the minimum
            heapq.heapify(self.v_list)
            (weight, label) = heapq.heappop(self.v_list)
            #Seeking an adjacent road
            loads = [(k1,k2,val) for (k1,k2), val in self.e_list.items() if k1 == label]
            for load in loads:
                #Find matching weights in a list of pairs
                for (v0,v1) in [(v0,v1) for (v0,v1) in self.v_list if v1==load[1]]:   
                        #Update weights
                        if weight + load[2] <= v0:
                            list_count = self.v_list.index((v0,v1)) #Substitute the element number of the list
                            self.v_list[list_count] = (weight + load[2],v1)
            #Added fixed vertices and paths
            self.dic_v_list.append((weight,label))
            self.load_add(weight,label)
The rest are all extra functions. (Omit explanation)
Try it with the following graph.

They are arranged in order from goal to start, but you have successfully executed the answer.
#original data:Start 7,Goal 1
cross_streets = [1,2,3,4,5,6,7,8]  #All intersections(vertex)List of
load = {(1,3):5,(1,2):1,(1,4):4,(2,4):2,(3,5):2,(4,5):5,(4,6):6,(5,8):1,(5,7):3,(6,7):2,(8,7):4} #All roads(Edge)Dictionary
map = Map(7,1)
#Execution result
[1, 3, 5, 7]
#Of course it holds even if it is reversed
cross_streets = [1,2,3,4,5,6,7,8]  #All intersections(vertex)List of
load = {(1,3):5,(1,2):1,(1,4):4,(2,4):2,(3,5):2,(4,5):5,(4,6):6,(5,8):1,(5,7):3,(6,7):2,(8,7):4} #All roads(Edge)Dictionary
map = Map(1,7)
#Execution result
[7, 5, 3, 1]
        Recommended Posts