Wird hier behandelt.
Grundlagen der Graphentheorie </ b> https://qiita.com/maskot1977/items/e1819b7a1053eb9f7d61
Ich habe in der Vergangenheit einen Artikel geschrieben und von vielen Leuten "Likes" erhalten, aber dieses Mal möchte ich den Inhalt mit Animation leicht verständlich machen.
Eine einfache Animation kann wie folgt gezeichnet werden.
# -*- coding: UTF-8 -*-
import math
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from IPython.display import HTML
fig = plt.figure()
ims = [] #Video = Liste, in der eine Reihe von Standbildern gespeichert sind
for i in range(360):
rad = math.radians(i)
x1, y1 = math.cos(rad), math.sin(rad)
x2, y2 = math.cos(rad * 2), math.sin(rad * 2)
im = plt.scatter(x1, y1) #Standbild Teil 1 (kein Listentyp)
im2 = plt.scatter(x2, y2) #Standbild Teil 2 (kein Listentyp)
im3 = plt.plot([x1, x2], [y1, y2]) #Standbild Teil 3 (Listentyp)
image = [im, im2] + im3 #Eine Liste repräsentiert ein Standbild
ims.append(image) #Fügen Sie ein Standbild hinzu
#In Video konvertieren
ani = animation.ArtistAnimation(fig, ims, interval=10, repeat_delay=1000)
ani.save("Animation1.gif", writer='pillow') #Als GIF-Datei speichern
HTML(ani.to_jshtml()) #Anzeige in HTML
Verwenden Sie dieselben Daten wie Grundlagen der Graphentheorie. Die Koordinatendaten (Breiten- / Längengrad) der Stadt, in der sich das Präfekturbüro befindet, werden geschrieben. Die Stadt, in der sich das Präfekturbüro befindet, wird als "top" </ b> bezeichnet, und die gerade Linie, die die Städte verbindet, wird als "side" </ b> bezeichnet. Städte, die durch eine Seite verbunden sind, werden "benachbart" </ b> genannt.
import urllib.request
url = 'https://raw.githubusercontent.com/maskot1977/ipython_notebook/master/toydata/location.txt'
urllib.request.urlretrieve(url, 'location.txt') #Daten herunterladen
('location.txt', <http.client.HTTPMessage at 0x11c9c2320>)
Lesen wir es mit Pandas, die in [Grundlagen der Graphentheorie] nicht verwendet wurden (https://qiita.com/maskot1977/items/e1819b7a1053eb9f7d61).
import pandas as pd
japan = pd.read_csv('location.txt')
japan
Town | Longitude | Latitude | |
---|---|---|---|
0 | Sapporo | 43.06417 | 141.34694 |
1 | Aomori | 40.82444 | 140.74000 |
2 | Morioka | 39.70361 | 141.15250 |
3 | Sendai | 38.26889 | 140.87194 |
4 | Akita | 39.71861 | 140.10250 |
5 | Yamagata | 38.24056 | 140.36333 |
... | ... | ... | ... |
45 | Kagoshima | 31.56028 | 130.55806 |
46 | Naha | 26.21250 | 127.68111 |
Illustriert.
%matplotlib inline
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 8))
plt.scatter(japan['Latitude'], japan['Longitude'])
for city, x, y in zip(japan['Town'], japan['Latitude'], japan['Longitude']):
plt.text(x, y, city, alpha=0.5, size=12)
plt.grid()
Lassen Sie uns anhand der obigen Daten eine Animation der Suche nach Tiefenpriorität, der Suche nach Breitenpriorität und der Suche nach bester Priorität erstellen, die die grundlegenden Algorithmen der Graphentheorie sind.
Ich habe es nicht in Grundlagen der Graphentheorie verwendet, aber mit scipy kann die Distanzmatrix (Abstand zwischen Eckpunkten) wie folgt berechnet werden.
import numpy as np
from scipy.spatial import distance
mat = japan[['Latitude', 'Longitude']].values
dist_mat = distance.cdist(mat, mat, metric='euclidean') #Euklidische Entfernung
Um beim Zeichnen eines Diagramms eine gerade Linie zwischen zwei Punkten zu zeichnen, mache ich meine eigene Funktion, um die Koordinaten aus dem Satz von Seiten (Satz von Eckpunkten) zu finden.
def get_edges(routes):
edges = []
for route in routes:
if len(route) == 2:
town1, y1, x1 = [value for value in japan.values][route[0]]
town2, y2, x2 = [value for value in japan.values][route[1]]
edges.append([[x1, x2], [y1, y2]])
return edges
Das Anwendungsbeispiel sieht folgendermaßen aus.
get_edges([[1, 2], [3, 4], [5, 6]])
[[[140.74, 141.1525], [40.82444, 39.70361]],
[[140.87194, 140.1025], [38.26889, 39.71861]],
[[140.36333, 140.46778], [38.240559999999995, 37.75]]]
Als ersten Algorithmus für die Diagrammsuche führen wir nun eine Suche mit Tiefenpriorität durch.
Bei der Diagrammsuche ist es sehr wichtig, wie eine "benachbarte Liste" erstellt wird (welcher Scheitelpunkt von welchem Scheitelpunkt ausgehen kann), aber "Kanten zwischen Spitzen unterhalb eines bestimmten Abstands (Schwellenwerts) verbinden" </ b> Ich würde gerne mit dieser Politik gehen. Unter Verwendung von numpy und der Distanzmatrix, die in Grundlagen der Graphentheorie nicht verwendet wurden, kann sie wie folgt berechnet werden.
def neighbor(town, dist_mat=dist_mat, threshold=1): #Funktion zum Abrufen der Adjazenzliste 1
return [x[0] for x in enumerate(np.where(dist_mat[town] <= threshold, True, False)) if x[1]]
Das Anwendungsbeispiel sieht folgendermaßen aus.
neighbor(12) #Welche Städte sind 1 Entfernung von Tokio entfernt?
[7, 8, 9, 10, 11, 12, 13]
In Grundlagen der Graphentheorie wurde die while-Anweisung verwendet, um das Graphsuchproblem zu lösen, aber hier möchte ich den Fortschritt Schritt für Schritt zeigen. Ich habe die Funktion definiert, um einen Schritt der Suche wie folgt voranzutreiben.
def traverse(i=0): #Ein Schritt der Tiefenprioritätssuche
if len(stack) != 0: #Wenn der Stapel nicht leer ist
next_town, current_town = stack.pop() #Holen Sie sich die nächste Route (aktueller Standort und nächste Stadt)
current_direction = [[next_town, current_town]] #Zum Zeichnen
if next_town not in visited_towns: #Wenn die nächste Stadt nicht besucht wurde
used_routes.append([next_town, current_town]) #Registrieren Sie die Route
visited_towns.append(next_town) #Machen Sie es besucht
for nei in neighbor(next_town): #Nehmen Sie die Städte neben der besuchten Stadt nacheinander heraus
if nei not in visited_towns: #Wenn Sie nicht besucht haben
stack.append([nei, next_town]) #Legen Sie die Route auf den Stapel
return current_direction #Zum Zeichnen
Jetzt animieren wir es.
# -*- coding: UTF-8 -*-
import math
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from IPython.display import HTML
fig = plt.figure(figsize=(10, 8))
stack = [[12, 12]] #Der Ausgangspunkt ist Tokio
visited_towns = [] #Anordnung zur Aufbewahrung der besuchten Städte
used_routes = [] #Ein Array, in dem die tatsächlich verwendeten Routen gespeichert werden
current_direction = [] #Route wird gerade überprüft
ims = [] #Anordnung zum Speichern von Standbildern
i = 0
while len(stack) > 0: #Wenn der Stapel nicht leer ist
if i != 0: #Da der Anfangswert zum ersten Mal angezeigt wird, kann die Suche nicht fortgesetzt werden.
current_direction = traverse() #Machen Sie die Suche einen Schritt
image = [] #Ein Array, in dem zu schreibende Teile in einem Standbild gespeichert werden
for edge in get_edges(stack): #Die rote Linie ist die "Kandidaten" -Route im Stapel
image += plt.plot(edge[0], edge[1], 'r', alpha=0.5)
for edge in get_edges(current_direction): #Die blaue Linie ist die Route, die gerade überprüft wird
image += plt.plot(edge[0], edge[1], 'b', lw=5, alpha=0.5)
for edge in get_edges(used_routes): #Schwarze Linie wird Route verwendet
image += plt.plot(edge[0], edge[1], 'k', lw=2)
for city, x, y in zip(japan['Town'], japan['Latitude'], japan['Longitude']):
image.append(plt.text(x, y, city, alpha=0.5, size=12)) #Stadtnamen anzeigen
if len(current_direction) > 0:
current_town = current_direction[0][0]
image += plt.plot(japan.iloc[current_town, :]['Latitude'],
japan.iloc[current_town, :]['Longitude'],
markersize=20, marker='o') #Maru ist die Stadt, die gerade überprüft wird
ims.append(image) #Speichern Sie ein Standbild
i += 1
#In Video konvertieren
ani = animation.ArtistAnimation(fig, ims, interval=500, repeat_delay=1000)
ani.save("Animation2.gif", writer='pillow') #Als GIF-Datei speichern
HTML(ani.to_jshtml()) #Anzeige in HTML
Suchanimation für Tiefenpriorität 1
Bei der obigen Suche nach Tiefenpriorität ist die "letzte Stadt auf dem Stapel" der nächste Zielkandidat. Wenn Sie in eine Stadt ziehen, befinden sich die Nachbarstädte im Stapel. Zu diesem Zeitpunkt ändert sich das Verhalten auch bei gleicher Suche nach Tiefenpriorität, indem die Reihenfolge berücksichtigt wird, in der sie in den Stapel gelegt werden. Ändern wir es so, dass Städte, die nahe beieinander liegen, bevorzugt als Zielkandidaten ausgewählt werden.
import numpy as np
def neighbor(town, dist_mat=dist_mat, threshold=1): #Funktion zum Abrufen der Adjazenzliste 2
return np.argsort(dist_mat[town])[1:np.where(dist_mat[town] <= threshold, 1, 0).sum()][::-1]
Der folgende Code entspricht dem vorherigen "Tiefenprioritätssuchanimation 1" (nur der Dateiname des gespeicherten Videos ist unterschiedlich). Mal sehen, wie das Ändern der Funktion "Nachbar" ihr Verhalten ändert.
# -*- coding: UTF-8 -*-
# -*- coding: UTF-8 -*-
import math
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from IPython.display import HTML
fig = plt.figure(figsize=(10, 8))
stack = [[12, 12]] #Der Ausgangspunkt ist Tokio
visited_towns = [] #Anordnung zur Aufbewahrung der besuchten Städte
used_routes = [] #Ein Array, in dem die tatsächlich verwendeten Routen gespeichert werden
current_direction = [] #Route wird gerade überprüft
ims = [] #Anordnung zum Speichern von Standbildern
i = 0
while len(stack) > 0: #Wenn der Stapel nicht leer ist
if i != 0: #Da der Anfangswert zum ersten Mal angezeigt wird, kann die Suche nicht fortgesetzt werden.
if stack[0] != []: #Die Suche wird auch bei der letzten Anzeige nicht fortgesetzt
current_direction = traverse() #Machen Sie die Suche einen Schritt
image = [] #Ein Array, in dem zu schreibende Teile in einem Standbild gespeichert werden
for edge in get_edges(stack): #Die rote Linie ist die "Kandidaten" -Route im Stapel
image += plt.plot(edge[0], edge[1], 'r', alpha=0.5)
for edge in get_edges(current_direction): #Die blaue Linie ist die Route, die gerade überprüft wird
image += plt.plot(edge[0], edge[1], 'b', lw=5, alpha=0.5)
for edge in get_edges(used_routes): #Schwarze Linie wird Route verwendet
image += plt.plot(edge[0], edge[1], 'k', lw=2)
for city, x, y in zip(japan['Town'], japan['Latitude'], japan['Longitude']):
image.append(plt.text(x, y, city, alpha=0.5, size=12)) #Stadtnamen anzeigen
if len(current_direction) > 0:
current_town = current_direction[0][0]
image += plt.plot(japan.iloc[current_town, :]['Latitude'],
japan.iloc[current_town, :]['Longitude'],
markersize=20, marker='o') #Maru ist die Stadt, die gerade überprüft wird
ims.append(image) #Speichern Sie ein Standbild
if len(stack) == 0: #Für die letzte Anzeige
current_direction = []
stack.append([])
elif stack[0] == []: #Für die letzte Flucht
break
i += 1
#In Video konvertieren
ani = animation.ArtistAnimation(fig, ims, interval=500, repeat_delay=1000)
ani.save("Animation3.gif", writer='pillow') #Als GIF-Datei speichern
HTML(ani.to_jshtml()) #Anzeige in HTML
Suche nach Tiefenprioritätssuche 2
Als nächstes führen wir eine Suche nach Breitenpriorität durch. Der grundlegende Algorithmus ist fast der gleiche. Stellen Sie den Stapel (first in, last out) einfach in eine Warteschlange (queue: first in, first out).
Schreiben Sie die Funktion "Traverse" neu. Der neu zu schreibende Teil ist nur ein Punkt, der als "Änderung" angezeigt wird. Da sich die als Stapel verwendete Liste in eine Warteschlange ändert, möchte ich den Variablennamen "Stapel" ändern, aber dadurch wird der neu zu schreibende Teil vergrößert. Lassen wir also "Stapel" so wie er ist.
def traverse(i=0): #1 Schritt der Suche nach Breitenpriorität
if len(stack) != 0:
next_town, current_town = stack.pop(0) #Wechselpunkt
current_direction = [[next_town, current_town]]
if next_town not in visited_towns:
used_routes.append([next_town, current_town])
visited_towns.append(next_town)
for nei in neighbor(next_town):
if nei not in visited_towns:
stack.append([nei, next_town])
return current_direction
Die Funktion zum Abrufen der Adjazenzliste wurde ebenfalls neu geschrieben, ist jedoch im Grunde dieselbe wie die vorherige. Der Stapel befindet sich in der Warteschlange, sodass Sie die Reihenfolge einfach umdrehen.
import numpy as np
def neighbor(town, dist_mat=dist_mat, threshold=1): #Funktion zum Abrufen der Adjazenzliste 3
return np.argsort(dist_mat[town])[1:np.where(dist_mat[town] <= threshold, 1, 0).sum()] #Ändere nur das Ende
Der folgende Code entspricht auch "Tiefenprioritätssuchanimation 1" und "Tiefenprioritätssuchanimation 2" (nur der Dateiname des gespeicherten Videos ist unterschiedlich). Mal sehen, wie das Ändern der Funktion "Traverse" das Verhalten ändert.
# -*- coding: UTF-8 -*-
import math
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from IPython.display import HTML
fig = plt.figure(figsize=(10, 8))
stack = [[12, 12]] #Der Ausgangspunkt ist Tokio
visited_towns = [] #Anordnung zur Aufbewahrung der besuchten Städte
used_routes = [] #Ein Array, in dem die tatsächlich verwendeten Routen gespeichert werden
current_direction = [] #Route wird gerade überprüft
ims = [] #Anordnung zum Speichern von Standbildern
i = 0
while len(stack) > 0: #Wenn der Stapel nicht leer ist
if i != 0: #Da der Anfangswert zum ersten Mal angezeigt wird, kann die Suche nicht fortgesetzt werden.
if stack[0] != []: #Die Suche wird auch bei der letzten Anzeige nicht fortgesetzt
current_direction = traverse() #Machen Sie die Suche einen Schritt
image = [] #Ein Array, in dem zu schreibende Teile in einem Standbild gespeichert werden
for edge in get_edges(stack): #Die rote Linie ist die "Kandidaten" -Route im Stapel
image += plt.plot(edge[0], edge[1], 'r', alpha=0.5)
for edge in get_edges(current_direction): #Die blaue Linie ist die Route, die gerade überprüft wird
image += plt.plot(edge[0], edge[1], 'b', lw=5, alpha=0.5)
for edge in get_edges(used_routes): #Schwarze Linie wird Route verwendet
image += plt.plot(edge[0], edge[1], 'k', lw=2)
for city, x, y in zip(japan['Town'], japan['Latitude'], japan['Longitude']):
image.append(plt.text(x, y, city, alpha=0.5, size=12)) #Stadtnamen anzeigen
if len(current_direction) > 0:
current_town = current_direction[0][0]
image += plt.plot(japan.iloc[current_town, :]['Latitude'],
japan.iloc[current_town, :]['Longitude'],
markersize=20, marker='o') #Maru ist die Stadt, die gerade überprüft wird
ims.append(image) #Speichern Sie ein Standbild
if len(stack) == 0: #Für die letzte Anzeige
current_direction = []
stack.append([])
elif stack[0] == []: #Für die letzte Flucht
break
i += 1
#In Video konvertieren
ani = animation.ArtistAnimation(fig, ims, interval=500, repeat_delay=1000)
ani.save("Animation4.gif", writer='pillow') #Als GIF-Datei speichern
HTML(ani.to_jshtml()) #Anzeige in HTML
Suchanimation mit Breitenpriorität
Schließlich die Suche mit der besten Priorität. In den beiden obersten Suchanfragen haben wir Städte mit kurzer Entfernung hinzugefügt, die beim Hinzufügen zum Stapel (oder zur Warteschlange) bevorzugt abgerufen werden sollen. Bei der Suche mit der besten Priorität wird der gesamte Stapel (tatsächlich eine Warteschlange) nach dem Hinzufügen sortiert, sodass die Städte mit der kürzesten Entfernung bevorzugt daraus extrahiert werden. Das Ergebnis ist ein "Mindestbaum".
Die einzige Änderung gegenüber der Vergangenheit besteht darin, den gesamten Stapel neu zu sortieren.
def sort_stack(stack):
return [stack[i] for i in np.argsort([dist_mat[edge[0]][edge[1]] for edge in stack])]
Der folgende Code ist im Grunde der gleiche wie zuvor. Die einzigen Änderungen sind das Hinzufügen der Funktion "sort_stack" und das Umbenennen der Datei, in der das Video gespeichert wird.
# -*- coding: UTF-8 -*-
import math
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from IPython.display import HTML
fig = plt.figure(figsize=(10, 8))
stack = [[12, 12]] #Der Ausgangspunkt ist Tokio
visited_towns = [] #Anordnung zur Aufbewahrung der besuchten Städte
used_routes = [] #Ein Array, in dem die tatsächlich verwendeten Routen gespeichert werden
current_direction = [] #Route wird gerade überprüft
ims = [] #Anordnung zum Speichern von Standbildern
i = 0
while len(stack) > 0: #Wenn der Stapel nicht leer ist
if i != 0: #Da der Anfangswert zum ersten Mal angezeigt wird, kann die Suche nicht fortgesetzt werden.
if stack[0] != []: #Die Suche wird auch bei der letzten Anzeige nicht fortgesetzt
stack = sort_stack(stack) #Stapel für die Suche mit der besten Priorität sortieren
current_direction = traverse() #Machen Sie die Suche einen Schritt
image = [] #Ein Array, in dem zu schreibende Teile in einem Standbild gespeichert werden
for edge in get_edges(stack): #Die rote Linie ist die "Kandidaten" -Route im Stapel
image += plt.plot(edge[0], edge[1], 'r', alpha=0.5)
for edge in get_edges(current_direction): #Die blaue Linie ist die Route, die gerade überprüft wird
image += plt.plot(edge[0], edge[1], 'b', lw=5, alpha=0.5)
for edge in get_edges(used_routes): #Schwarze Linie wird Route verwendet
image += plt.plot(edge[0], edge[1], 'k', lw=2)
for city, x, y in zip(japan['Town'], japan['Latitude'], japan['Longitude']):
image.append(plt.text(x, y, city, alpha=0.5, size=12)) #Stadtnamen anzeigen
if len(current_direction) > 0:
current_town = current_direction[0][0]
image += plt.plot(japan.iloc[current_town, :]['Latitude'],
japan.iloc[current_town, :]['Longitude'],
markersize=20, marker='o') #Maru ist die Stadt, die gerade überprüft wird
ims.append(image) #Speichern Sie ein Standbild
if len(stack) == 0: #Für die letzte Anzeige
current_direction = []
stack.append([])
elif stack[0] == []: #Für die letzte Flucht
break
i += 1
#In Video konvertieren
ani = animation.ArtistAnimation(fig, ims, interval=500, repeat_delay=1000)
ani.save("Animation5.gif", writer='pillow') #Als GIF-Datei speichern
HTML(ani.to_jshtml()) #Anzeige in HTML
Suchanimation mit der besten Priorität