Sera traité ici.
Bases de la théorie des graphes </ b> https://qiita.com/maskot1977/items/e1819b7a1053eb9f7d61
J'ai écrit un article dans le passé et j'ai reçu des "j'aime" de nombreuses personnes, mais cette fois, j'aimerais rendre le contenu facile à comprendre grâce à l'animation.
Une animation simple peut être dessinée comme suit.
# -*- coding: UTF-8 -*-
import math
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from IPython.display import HTML
fig = plt.figure()
ims = [] #Vidéo = liste qui stocke un ensemble d'images fixes
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) #Image fixe partie 1 (pas de type liste)
im2 = plt.scatter(x2, y2) #Image fixe partie 2 (pas de type liste)
im3 = plt.plot([x1, x2], [y1, y2]) #Image fixe partie 3 (type de liste)
image = [im, im2] + im3 #Une liste représente une image fixe
ims.append(image) #Ajouter une image fixe
#Convertir en vidéo
ani = animation.ArtistAnimation(fig, ims, interval=10, repeat_delay=1000)
ani.save("Animation1.gif", writer='pillow') #Enregistrer en tant que fichier gif
HTML(ani.to_jshtml()) #Afficher sur HTML
Utilisez les mêmes données que Bases de la théorie des graphes. Les coordonnées (latitude / longitude) de la ville où se trouve la préfecture sont écrites. La ville où se trouve le bureau préfectoral s'appelle le "haut" </ b>, et la ligne droite reliant les villes est appelée le "côté" </ b>. Les villes reliées par un côté sont appelées "adjacentes" </ b>.
import urllib.request
url = 'https://raw.githubusercontent.com/maskot1977/ipython_notebook/master/toydata/location.txt'
urllib.request.urlretrieve(url, 'location.txt') #Télécharger les données
('location.txt', <http.client.HTTPMessage at 0x11c9c2320>)
Lisons-le en utilisant des pandas, qui n'ont pas été utilisés dans Bases de la théorie des graphes.
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 |
Illustré.
%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()
En utilisant les données ci-dessus, créons une animation de recherche de priorité de profondeur, de recherche de priorité de largeur et de recherche de meilleure priorité, qui sont les algorithmes de base de la théorie des graphes.
Je ne l'ai pas utilisé dans Basics of Graph Theory, mais si vous utilisez scipy, la matrice de distance (distance entre les sommets) peut être calculée comme suit.
import numpy as np
from scipy.spatial import distance
mat = japan[['Latitude', 'Longitude']].values
dist_mat = distance.cdist(mat, mat, metric='euclidean') #Distance euclidienne
Afin de tracer une ligne droite entre deux points lors du dessin d'un graphe, je vais créer ma propre fonction pour trouver les coordonnées de l'ensemble des côtés (ensemble des sommets).
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
L'exemple d'utilisation ressemble à ceci.
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]]]
Maintenant, en tant que premier algorithme de recherche de graphique, nous allons faire une recherche de priorité de profondeur.
Dans la recherche de graphe, comment créer une "liste adjacente" (quel sommet peut aller de quel sommet) est très important, mais "relier les arêtes entre les pics au-dessous d'une certaine distance (seuil)" </ b> Je voudrais suivre cette politique. En utilisant numpy et la matrice de distance, qui n'ont pas été utilisées dans Bases de la théorie des graphes, il peut être calculé comme suit.
def neighbor(town, dist_mat=dist_mat, threshold=1): #Fonction pour obtenir la liste de contiguïté 1
return [x[0] for x in enumerate(np.where(dist_mat[town] <= threshold, True, False)) if x[1]]
L'exemple d'utilisation ressemble à ceci.
neighbor(12) #Quelles villes sont à moins de 1 distance de Tokyo?
[7, 8, 9, 10, 11, 12, 13]
Dans Bases de la théorie des graphes, l'instruction while a été utilisée pour résoudre le problème de recherche de graphes, mais ici, je veux montrer la progression étape par étape. J'ai défini la fonction pour avancer d'une étape de la recherche comme suit.
def traverse(i=0): #Une étape de recherche de priorité en profondeur
if len(stack) != 0: #Si la pile n'est pas vide
next_town, current_town = stack.pop() #Obtenir l'itinéraire suivant (emplacement actuel et ville suivante)
current_direction = [[next_town, current_town]] #Pour dessiner
if next_town not in visited_towns: #Si la ville suivante n'a pas été visitée
used_routes.append([next_town, current_town]) #Enregistrer l'itinéraire
visited_towns.append(next_town) #Faites-le visiter
for nei in neighbor(next_town): #Sortez les villes adjacentes à la ville visitée une par une
if nei not in visited_towns: #Si vous n'avez pas visité
stack.append([nei, next_town]) #Mettez l'itinéraire sur la pile
return current_direction #Pour dessiner
Maintenant animons-le.
# -*- 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]] #Le point de départ est Tokyo
visited_towns = [] #Arrangement pour stocker les villes visitées
used_routes = [] #Un tableau qui stocke les routes réellement utilisées
current_direction = [] #Route en cours de vérification
ims = [] #Disposition pour stocker des images fixes
i = 0
while len(stack) > 0: #Si la pile n'est pas vide
if i != 0: #La valeur initiale étant affichée la première fois, la recherche ne peut pas continuer.
current_direction = traverse() #Faites une étape de recherche
image = [] #Un tableau qui stocke les parties à écrire dans une image fixe
for edge in get_edges(stack): #La ligne rouge est la route «candidate» dans la pile
image += plt.plot(edge[0], edge[1], 'r', alpha=0.5)
for edge in get_edges(current_direction): #La ligne bleue est l'itinéraire en cours de vérification
image += plt.plot(edge[0], edge[1], 'b', lw=5, alpha=0.5)
for edge in get_edges(used_routes): #La ligne noire est un itinéraire utilisé
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)) #Afficher le nom de la ville
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 est la ville en cours de vérification
ims.append(image) #Stocker une image fixe
i += 1
#Convertir en vidéo
ani = animation.ArtistAnimation(fig, ims, interval=500, repeat_delay=1000)
ani.save("Animation2.gif", writer='pillow') #Enregistrer en tant que fichier gif
HTML(ani.to_jshtml()) #Afficher sur HTML
Animation de recherche de priorité de profondeur 1
Dans la recherche de priorité de profondeur ci-dessus, la «dernière ville de la pile» est la prochaine destination candidate. Lorsque vous vous déplacez dans une ville, les villes voisines sont dans la pile. À ce stade, le comportement change même avec la même recherche de priorité de profondeur en tenant compte de l'ordre de leur mise dans la pile. Modifions-le pour que les villes proches les unes des autres soient préférentiellement sélectionnées comme destinations candidates.
import numpy as np
def neighbor(town, dist_mat=dist_mat, threshold=1): #Fonction pour obtenir la liste de contiguïté 2
return np.argsort(dist_mat[town])[1:np.where(dist_mat[town] <= threshold, 1, 0).sum()][::-1]
Le code ci-dessous est le même que celui de la précédente "Animation de recherche de priorité à la profondeur 1" (seul le nom de fichier de la vidéo enregistrée est différent). Voyons comment changer la fonction voisin
change son comportement.
# -*- 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]] #Le point de départ est Tokyo
visited_towns = [] #Arrangement pour stocker les villes visitées
used_routes = [] #Un tableau qui stocke les routes réellement utilisées
current_direction = [] #Route en cours de vérification
ims = [] #Disposition pour stocker des images fixes
i = 0
while len(stack) > 0: #Si la pile n'est pas vide
if i != 0: #La valeur initiale étant affichée la première fois, la recherche ne peut pas continuer.
if stack[0] != []: #La recherche ne se poursuit pas même au dernier affichage
current_direction = traverse() #Faites une étape de recherche
image = [] #Un tableau qui stocke les parties à écrire dans une image fixe
for edge in get_edges(stack): #La ligne rouge est la route «candidate» dans la pile
image += plt.plot(edge[0], edge[1], 'r', alpha=0.5)
for edge in get_edges(current_direction): #La ligne bleue est l'itinéraire en cours de vérification
image += plt.plot(edge[0], edge[1], 'b', lw=5, alpha=0.5)
for edge in get_edges(used_routes): #La ligne noire est un itinéraire utilisé
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)) #Afficher le nom de la ville
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 est la ville en cours de vérification
ims.append(image) #Stocker une image fixe
if len(stack) == 0: #Pour le dernier affichage
current_direction = []
stack.append([])
elif stack[0] == []: #Pour la dernière évasion
break
i += 1
#Convertir en vidéo
ani = animation.ArtistAnimation(fig, ims, interval=500, repeat_delay=1000)
ani.save("Animation3.gif", writer='pillow') #Enregistrer en tant que fichier gif
HTML(ani.to_jshtml()) #Afficher sur HTML
Animation de recherche de priorité de profondeur 2
Ensuite, faisons une recherche de priorité de largeur. L'algorithme de base est presque le même, il suffit de mettre la pile (premier entré, dernier sorti) dans une file d'attente (file d'attente: premier entré, premier sorti).
Réécrivez la fonction «traversée». La partie à réécrire n'est qu'un seul point indiqué comme "changement". Étant donné que la liste utilisée comme pile change en file d'attente, je voudrais changer le nom de la variable stack
, mais cela augmentera la partie à réécrire, alors laissons pile
telle quelle.
def traverse(i=0): #1 étape de recherche de priorité de largeur
if len(stack) != 0:
next_town, current_town = stack.pop(0) #point de changement
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
La fonction pour obtenir la liste de contiguïté est également réécrite, mais elle est fondamentalement la même que la précédente. La pile est mise en file d'attente, il vous suffit donc de retourner la commande.
import numpy as np
def neighbor(town, dist_mat=dist_mat, threshold=1): #Fonction pour obtenir la liste de contiguïté 3
return np.argsort(dist_mat[town])[1:np.where(dist_mat[town] <= threshold, 1, 0).sum()] #Changer seulement la fin
Le code suivant est également le même que «Animation de recherche de priorité de profondeur 1» et «Animation de recherche de priorité de profondeur 2» (seul le nom de fichier de la vidéo enregistrée est différent). Voyons comment changer la fonction «traverse» change le comportement.
# -*- 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]] #Le point de départ est Tokyo
visited_towns = [] #Arrangement pour stocker les villes visitées
used_routes = [] #Un tableau qui stocke les routes réellement utilisées
current_direction = [] #Route en cours de vérification
ims = [] #Disposition pour stocker des images fixes
i = 0
while len(stack) > 0: #Si la pile n'est pas vide
if i != 0: #La valeur initiale étant affichée la première fois, la recherche ne peut pas continuer.
if stack[0] != []: #La recherche ne se poursuit pas même au dernier affichage
current_direction = traverse() #Faites une étape de recherche
image = [] #Un tableau qui stocke les parties à écrire dans une image fixe
for edge in get_edges(stack): #La ligne rouge est la route «candidate» dans la pile
image += plt.plot(edge[0], edge[1], 'r', alpha=0.5)
for edge in get_edges(current_direction): #La ligne bleue est l'itinéraire en cours de vérification
image += plt.plot(edge[0], edge[1], 'b', lw=5, alpha=0.5)
for edge in get_edges(used_routes): #La ligne noire est un itinéraire utilisé
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)) #Afficher le nom de la ville
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 est la ville en cours de vérification
ims.append(image) #Stocker une image fixe
if len(stack) == 0: #Pour le dernier affichage
current_direction = []
stack.append([])
elif stack[0] == []: #Pour la dernière évasion
break
i += 1
#Convertir en vidéo
ani = animation.ArtistAnimation(fig, ims, interval=500, repeat_delay=1000)
ani.save("Animation4.gif", writer='pillow') #Enregistrer en tant que fichier gif
HTML(ani.to_jshtml()) #Afficher sur HTML
Animation de recherche de priorité de largeur
Enfin, la meilleure recherche prioritaire. Dans les deux premières recherches, nous avons ajouté une priorité pour les villes à courte distance lors de leur ajout à la pile (ou à la file d'attente). La recherche de la meilleure priorité trie la pile entière (en fait une file d'attente) après son ajout afin que les villes avec la distance la plus courte en soient de préférence extraites. Le résultat est un "arbre minimum".
Le seul changement par rapport au passé est de trier à nouveau la pile entière.
def sort_stack(stack):
return [stack[i] for i in np.argsort([dist_mat[edge[0]][edge[1]] for edge in stack])]
Le code ci-dessous est fondamentalement le même qu'avant. Les seuls changements sont l'ajout de la fonction sort_stack
et le changement de nom du fichier qui enregistre la vidéo.
# -*- 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]] #Le point de départ est Tokyo
visited_towns = [] #Arrangement pour stocker les villes visitées
used_routes = [] #Un tableau qui stocke les routes réellement utilisées
current_direction = [] #Route en cours de vérification
ims = [] #Disposition pour stocker des images fixes
i = 0
while len(stack) > 0: #Si la pile n'est pas vide
if i != 0: #La valeur initiale étant affichée la première fois, la recherche ne peut pas continuer.
if stack[0] != []: #La recherche ne se poursuit pas même au dernier affichage
stack = sort_stack(stack) #Trier les piles pour une recherche de la meilleure priorité
current_direction = traverse() #Faites une étape de recherche
image = [] #Un tableau qui stocke les parties à écrire dans une image fixe
for edge in get_edges(stack): #La ligne rouge est la route «candidate» dans la pile
image += plt.plot(edge[0], edge[1], 'r', alpha=0.5)
for edge in get_edges(current_direction): #La ligne bleue est l'itinéraire en cours de vérification
image += plt.plot(edge[0], edge[1], 'b', lw=5, alpha=0.5)
for edge in get_edges(used_routes): #La ligne noire est un itinéraire utilisé
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)) #Afficher le nom de la ville
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 est la ville en cours de vérification
ims.append(image) #Stocker une image fixe
if len(stack) == 0: #Pour le dernier affichage
current_direction = []
stack.append([])
elif stack[0] == []: #Pour la dernière évasion
break
i += 1
#Convertir en vidéo
ani = animation.ArtistAnimation(fig, ims, interval=500, repeat_delay=1000)
ani.save("Animation5.gif", writer='pillow') #Enregistrer en tant que fichier gif
HTML(ani.to_jshtml()) #Afficher sur HTML
Meilleure animation de recherche de priorité
Recommended Posts