Erinnern Sie sich an Dijkstras Algorithmus
Der Dijkstra-Algorithmus ist einer der beliebtesten graphentheoretischen Algorithmen. Es wird verwendet, um den kürzesten Pfad zwischen Knoten in einem gerichteten Graphen zu finden. Wir beginnen mit dem ursprünglichen Knoten und den bekannten Kantenlängen zwischen den Knoten.
Zuerst weisen wir allen Knoten den Abstand von der Quelle zu. Knoten s erhält den Wert 0, da es sich um die Quelle handelt. Der Rest erhält zunächst ∞ -Werte.
Unser interessierender Knoten ist der Rohknoten mit dem niedrigsten Wert (grau dargestellt), d. H. S. Zuerst "lockern" wir jeden benachbarten Scheitelpunkt zu unserem interessierenden Knoten und aktualisieren ihre Werte auf das Minimum ihres aktuellen Wertes oder den Wert des interessierenden Knotens plus der Länge der Verbindungskante ...
Knoten s ist jetzt vollständig (schwarz) und seine Nachbarn a und b haben neue Werte angenommen. Der neue interessierende Knoten ist b, daher wiederholen wir den Vorgang des „Schwächens“ der benachbarten Knoten von b und des Finalisierens des Werts des kürzesten Pfades für b.
Nachdem wir jeden Knoten durchlaufen haben, erhalten wir endlich ein Diagramm, das die kürzeste Pfadlänge von der Quelle zu jedem Knoten zeigt.
Unser letztes Diagramm nach dem Ausführen des Dijkstra-Algorithmus. Die Zahlen an jedem Knoten repräsentieren den kürzestmöglichen Abstand vom Quellknoten.
Konzeptualisierung von Labyrinthbildern.
Wir können uns ein Bild als eine Matrix von Pixeln vorstellen. Jedes Pixel hat (der Einfachheit halber) einen Wert von RGB 0,0,0 (schwarz) oder 255,255,255 (weiß). Unser Ziel ist es, einen kürzesten Weg zu schaffen, der auf Weiß beginnt und nicht zu schwarzen Rändern führt. Um dieses Ziel darzustellen, können wir jedes Pixel als Knoten betrachten und Kanten zwischen benachbarten Pixeln mit Kantenlängen basierend auf der Differenz der RGB-Werte zeichnen. Wir werden die euklidische Quadratabstandsformel verwenden und 0,1 addieren, um sicherzustellen, dass es keine Pfadlänge 0 gibt - (Voraussetzung für den Dijkstra-Algorithmus):
Diese Formel macht den Schnittabstand über die Labyrinthgrenze übermäßig groß. Wie wir sehen, führt der kürzeste Weg von der Quelle zum Ziel eindeutig um die Barriere herum und nicht durch diese hindurch.
Implementierung
Wir können OpenCV, eine beliebte Computer-Vision-Bibliothek für Python, verwenden, um Pixelwerte zu extrahieren und unsere Labyrinthbilder anzuzeigen. Definieren wir auch die Koordinaten unserer Start- und Endpositionen, indem wir unserem Labyrinth Punkte hinzufügen
import cv2
import matplotlib.pyplot as plt
import numpy as np
img = cv2.imread('maze.png') # read an image from a file using
cv2.circle(img,(5,220), 3, (255,0,0), -1) # add a circle at (5, 220)
cv2.circle(img, (25,5), 3, (0,0,255), -1) # add a circle at (5,5)
plt.figure(figsize=(7,7))
plt.imshow(img) # show the image
plt.show()
Wir erstellen eine Vertex-Klasse, mit deren Hilfe wir die Koordinaten verfolgen können. Wir möchten auch den übergeordneten Knoten verfolgen, damit wir den gesamten Pfad wiederherstellen können, sobald wir die Entfernungswerte gefunden haben.
class Vertex:
def __init__(self,x_coord,y_coord):
self.x=x_coord
self.y=y_coord
self.d=float('inf') #current distance from source node
self.parent_x=None
self.parent_y=None
self.processed=False
self.index_in_queue=None
Wir müssen eine Scheitelpunktmatrix erstellen, die die zweidimensionale Anordnung der Pixel im Bild darstellt. Dies wird die Grundlage für den Dijkstra-Algorithmus sein. Wir unterhalten auch eine Warteschlange mit niedriger Priorität, um unverarbeitete Knoten zu verfolgen.
def find_shortest_path(img,src,dst):
pq=[] #min-heap priority queue
imagerows,imagecols=img.shape[0],img.shape[1]
matrix = np.full((imagerows, imagecols), None)
#access matrix elements by matrix[row][col]
#fill matrix with vertices
for r in range(imagerows):
for c in range(imagecols):
matrix[r][c]=Vertex(c,r)
matrix[r][c].index_in_queue=len(pq)
pq.append(matrix[r][c])
#set source distance value to 0
matrix[source_y][source_x].d=0
#maintain min-heap invariant (minimum d Vertex at list index 0)
pq = bubble_up(pq, matrix[source_y][source_x].index_in_queue)
Wir benötigen einige Hilfsfunktionen, um die Kanten und die Länge der Kanten zwischen den Eckpunkten zu ermitteln:
#Implement euclidean squared distance formula
def get_distance(img,u,v):
return 0.1 + (float(img[v][0])-float(img[u][0]))**2+(float(img[v][1])-float(img[u][1]))**2+(float(img[v][2])-float(img[u][2]))**2
#Return neighbor directly above, below, right, and left
def get_neighbors(mat,r,c):
shape=mat.shape
neighbors=[]
#ensure neighbors are within image boundaries
if r > 0 and not mat[r-1][c].processed:
neighbors.append(mat[r-1][c])
if r < shape[0] - 1 and not mat[r+1][c].processed:
neighbors.append(mat[r+1][c])
if c > 0 and not mat[r][c-1].processed:
neighbors.append(mat[r][c-1])
if c < shape[1] - 1 and not mat[r][c+1].processed:
neighbors.append(mat[r][c+1])
return neighbors
Jetzt können wir den Dijkstra-Algorithmus implementieren und allen Eckpunkten von Pixeln im Labyrinthbild Abstandswerte (d) zuweisen:
while len(pq) > 0:
u=pq[0] #smallest-value unprocessed node
#remove node of interest from the queue
pq[0]=pq[-1]
pq[0].index_in_queue=0
pq.pop()
pq=bubble_down(pq,0) #min-heap function, see source code
u.processed=True
neighbors = get_neighbors(matrix,u.y,u.x)
for v in neighbors:
dist=get_distance(img,(u.y,u.x),(v.y,v.x))
if u.d + dist < v.d:
v.d = u.d+dist
v.parent_x=u.x #keep track of the shortest path
v.parent_y=u.y
idx=v.index_in_queue
pq=bubble_down(pq,idx)
pq=bubble_up(pq,idx)
Jetzt können wir die Funktion für den kürzesten Pfad aufrufen und die Lösung in unser Labyrinth zeichnen:
img = cv2.imread('maze.png') # read an image from a file using opencv (cv2) library
p = find_shortest_path(img, (25,5), (5,220))
drawPath(img,p)
plt.figure(figsize=(7,7))
plt.imshow(img) # show the image on the screen
plt.show()
Probieren wir andere Labyrinthe aus dem ganzen Internet aus.
Der vollständige Quellcode ist auf GitHub verfügbar hier .
Fortsetzung: Python-Lernprojekt: 40 Zeilen Code-Oberfläche (Teil 2)
Erfahren Sie in unseren kostenpflichtigen Online-SkillFactory-Kursen, wie Sie einen hochkarätigen Beruf von Grund auf neu aufbauen oder Ihre Fähigkeiten und Gehälter verbessern können:
- Kurs für maschinelles Lernen (12 Wochen)
- Data Science von Grund auf lernen (12 Monate)
- Analytics-Beruf mit jedem Startlevel (9 Monate)
- «Python -» (9 )