Uno de los algoritmos clásicos de ruta más corta Dijkstra

Prefacio

  Hoy hablamos de la ruta más corta de búsqueda de gráficos. La ruta más corta aquí incluye la distancia o el consumo de tiempo que debe considerarse de un nodo a otro nodo, es decir, el tiempo (u otras formas de consumo) requerido para llegar al El final desde el punto de partida es el mínimo. , A diferencia del camino más corto de la categoría de búsqueda del laberinto que mencionamos anteriormente, hay menos nodos en el camino (es decir, se supone que la distancia entre los nodos es 1)

  Ps: El artículo original es el tweet de WeChat del blogger, algunos de los cuales se pondrán en contacto, ahora se mudan a CSDN ~

CSDN (゜ - ゜) Saludos つ ロ

1. Explicación del principio del algoritmo

Lenguaje de implementación: Python

  Para los problemas anteriores, miramos la siguiente imagen:

Inserte la descripción de la imagen aquí
  En este momento, parece que necesitamos atravesar los nodos en el gráfico. La ruta más corta obtenida por la búsqueda BFS convencional no es necesariamente la que requiere menos tiempo.

  Aquí presentamos el algoritmo clásico de búsqueda de ruta más corta de una sola fuente: el algoritmo de Dijkstra . Fuente única significa que puede obtener la distancia más corta desde un punto de inicio específico a todos los demás nodos.

  Esencialmente es un algoritmo de optimización de bucles . En cada bucle, completa los siguientes pasos:

1. Encuentre el nodo con la distancia más corta conocida desde el punto de inicio, es decir, el punto que se puede alcanzar más rápido
2. Actualice el costo de distancia de los nodos circundantes a través de otros nodos conectados a este punto, es decir, la distancia desde el punto de partida a estos nodos circundantes

  Tomando el diagrama anterior como ejemplo, usemos un diagrama para mostrar un flujo de algoritmo simple:

Flujo del algoritmo
  En primer lugar, el nodo más corto que conocemos primero es el punto de partida en sí. Al principio, tenemos que configurar todos los puntos para que sean infinitos y luego usar los bordes conocidos para actualizar estos valores, pero el gráfico que di aquí tiene todos los pesos de borde Dado. A partir del punto de partida M, se encuentran A, B, N y se actualizan los pesos.

Inserte la descripción de la imagen aquí
  Al comienzo del segundo ciclo, después de encontrar el punto más cercano al punto de inicio como B, comience a buscar otros nodos conectados a B. Recuerde que estos nodos no pueden ser los puntos más cercanos marcados por el ciclo anterior, porque los puntos más cercanos determinados por los ciclos anteriores son para esta vuelta No tiene sentido actualizar su distancia al punto de partida, ya es el más cercano.

  Podemos ver que hay A y N que cumplen con las condiciones, y luego actualizan su distancia al punto de partida. Se encuentra que MB + BA <MA, es decir, hay una forma más cercana de llegar al punto A desde el punto de partida, por lo que se actualiza el valor de MA, y también se actualiza el valor de MN.

Inserte la descripción de la imagen aquí
  En el tercer ciclo, el punto más cercano al punto de inicio es A. Encuentre el nodo que está conectado a A y no ha sido marcado. Solo hay N, pero MA + AN = 10> MN = 8, entonces el valor de distancia de MN no está actualizado.

  El siguiente ciclo puede salir después de alcanzar el punto final, por lo que se puede obtener la distancia más corta desde el punto de inicio a todos los demás nodos, incluido el punto de inicio al punto final.

Inserte la descripción de la imagen aquí

Dos, implementación de código

def Dijkstra(directed=False):
	"""
	Functions: Implementation of Dijkstra using Python
	Args:
		directed: whether the graph is directed or not
	return:
		None
	"""
	# init distance
	limit = 10000
	# number of nodes, number of links, start_index, end_index
	N, K, s, e = list(map(int, input().split()))
	# graph mat
	ad_mat = [[0 for i in range(N)] for j in range(N)]
	# distance to start_node
	Dis = [limit for i in range(N)]
	# tags array
	vis = [0 for i in range(N)]
	# use links to fresh graph mat
	for i in range(K):
		u, v, w = list(map(int, input().split()))
		ad_mat[u][v] = w
		if directed == False:
			ad_mat[v][u] = w
	# init distance of start_node
	Dis[s] = 0
    # core logic
	for i in range(N):
        # select nodes with shortest distance to start from unchecked nodes
		min_ind = Dis.index(min([Dis[k] for k in range(len(Dis)) if vis[k] == 0]))
		# sign the node
		vis[min_ind] = 1
		for j in range(N):
            # fresh distance from start_node to adjacent nodes
			# unchecked, exist links, shorter then original dis
			if vis[j] == 0 \
				and ad_mat[min_ind][j] != 0 \
					and Dis[j] > (Dis[min_ind]+ad_mat[min_ind][j]):
				Dis[j] = Dis[min_ind]+ad_mat[min_ind][j]
	print('Shortest distance from s to e: {}'.format(Dis[e]))
	return

Detalles del código explicados

min_ind = Dis.index(min([Dis[k] for k in range(len(Dis)) if vis[k] == 0]))

  Esta oración es para encontrar el índice del nodo más cercano al punto de partida de los nodos que no están marcados como el punto más cercano por el bucle anterior.

if vis[j] == 0 and ad_mat[min_ind][j] != 0 \
	and Dis[j] > (Dis[min_ind]+ad_mat[min_ind][j]):
	Dis[j] = Dis[min_ind]+ad_mat[min_ind][j]

  Este es el paso central para actualizar la distancia (también llamado proceso de relajación) : después de encontrar el punto A más cercano de este ciclo, busque el no marcado de sus nodos vecinos (si se ha marcado, significa que es el punto más cercano y actualizado Nodos circundantes), hay un vínculo de ruta con A, y la distancia entre el punto de inicio y A y A al nodo y al nodo que es menor que la distancia desde el punto de inicio actual al nodo, y luego el valor de distancia desde se actualiza el punto de partida al punto.

complejidad del tiempo

  A través de la introducción del código, podemos encontrar que la complejidad temporal de la implementación dada es O (n ^ 2)

Prueba de muestra

  Ejecutemos un ejemplo de prueba:
Inserte la descripción de la imagen aquí

>>> Dijkstra(directed=False)
7 9 0 6
0 1 9
1 2 12
2 3 6
1 3 5
3 4 14
4 5 3
3 5 8
5 6 10
6 1 7
Shortest distance from s to e: 16
Time used: 0.01053s
    
>>> Dijkstra(directed=True)
7 9 0 6
0 1 9
1 2 12
2 3 6
1 3 5
3 4 14
4 5 3
3 5 8
5 6 10
6 1 7
Shortest distance from s to e: 32
Time used: 0.01255s

  El algoritmo de Dijkstra anterior es aplicable tanto a gráficos dirigidos como no dirigidos. A continuación, dibujamos la ruta más corta en dos casos:

Inserte la descripción de la imagen aquí

Gráfico no dirigido (violeta), gráfico dirigido (verde)


3. BFS se da cuenta de Dijkstra

Idea de realización

  A continuación, utilizaremos la idea de BFS combinada con colas de prioridad para implementar el algoritmo de Dijkstra . A través de nuestra explicación anterior, podemos encontrar que cada ciclo es para encontrar el punto más cercano al punto de inicio que no ha sido marcado. Este proceso se puede abstraer para encontrar la ruta más corta en BFS.
  Sin embargo, debido a que los nodos agregados a la cola están desordenados, la cola de prioridad debe usarse para ordenar las operaciones y definir un objeto que se pueda comparar.

  De esta forma, cada vez que lleguemos al punto más cercano al punto de partida a través del BFS, y luego realicemos la operación de relajación, se reducirá mucho el proceso de comparación, en general, cada borde solo se ha verificado una vez.

Inserte la descripción de la imagen aquí
  Primero tenemos que definir un objeto de cola comparable :

# comparable object definition
class compare_obj:
	def __init__(self, s, dis):
		self.s = s # node_index
		self.dis = dis # distance from start node to this
	def __lt__(self, other):
		return self.dis < other.dis

  La configuración de algunas matrices y variables temporales sigue siendo la misma que antes, y se agrega una matriz G para registrar el número de nodos adyacentes para cada nodo.


Código

# 使用BFS结合优先队列实现Dijkstra
def BFS_Dijkstra(directed=False):
	"""
	Functions: Implementation of Dijkstra using BFS and PriorityQueue
	Args:
		directed: whether the graph is directed or not
	return:
		None
	"""
	import time
	from queue import PriorityQueue
    
    # comparable object definition
	class compare_obj:
		def __init__(self, s, dis):
			self.s = s
			self.dis = dis

		def __lt__(self, other):
			return self.dis < other.dis
        
	# init distance
	limit = 10000
	# number of nodes, number of links, start_index, end_index
	N, K, s, e = list(map(int, input().split()))
	start = time.time()
	# graph mat
	ad_mat = [[0 for i in range(N)] for j in range(N)]
	# distance to start_node
	Dis = [limit for i in range(N)]
	# tags array
	vis = [0 for i in range(N)]
	# number of adjacent nodes of one node
	G = [[] for i in range(N)]
	# use links to fresh graph mat
	for i in range(K):
		u, v, w = list(map(int, input().split()))
		ad_mat[u][v] = w
		G[u].append(v)
		if directed == False:
			ad_mat[v][u] = w
			G[v].append(u)
            
	# init distance of start_node
	Dis[s] = 0
    # BFS with Priority Queue
	Q = PriorityQueue()
	Q.put(compare_obj(s, Dis[s]))
	while Q.qsize() != 0:
		node = Q.get_nowait()
		s, dis = node.s, node.dis
		# if check, continue
		if vis[s]:
		    continue
		# sign the node
		vis[s] = True
		# fresh distance
		for i in range(len(G[s])):
		    ad_node = G[s][i]
		    if not vis[ad_node] and Dis[ad_node] > (ad_mat[s][ad_node]+Dis[s]):
			    Dis[ad_node] = ad_mat[s][ad_node]+Dis[s]
			    Q.put_nowait(compare_obj(ad_node, Dis[ad_node]))

	print('Shortest distance from s to e: {}'.format(Dis[e]))
	print('Time used: {:.5f}s'.format(time.time()-start))
	return

Muestra de prueba

  Echemos un vistazo a los resultados de la prueba:

>>> BFS_Dijkstra(True)
7 9 0 6
0 1 9
1 2 12
2 3 6
1 3 5
3 4 14
4 5 3
3 5 8
5 6 10
6 1 7
Shortest distance from s to e: 32
Time used: 0.00573s

  Se puede ver que el tiempo de ejecución del algoritmo Dijkstra implementado por BFS es dos veces más rápido que la implementación del bucle for en el ejemplo dado.

Cuatro, resumen:

  Además del algoritmo de Dijkstra, hay otros algoritmos clásicos de búsqueda de ruta más corta, y continuaré llenando los huecos más adelante.

  Las ideas de relajación entre diferentes algoritmos son básicamente las mismas, es decir, encontrar un camino más corto y actualizar el valor de la distancia. Pero la estrategia de búsqueda y los tipos de problemas que son adecuados para resolver varían.


WeChat

CSDN BLog

Ven y quedate calvo con el cuchillo ~

Referencia

[1] Pequeña columna de algoritmos: búsqueda de ruta más corta de Dijkstra

Supongo que te gusta

Origin blog.csdn.net/weixin_40519529/article/details/113047815
Recomendado
Clasificación