Auparavant, Bases de la théorie des graphes et Bases de la théorie des graphes avec animation matplotlib, etc. J'ai écrit l'article, mais j'ai essayé une méthode utilisant "le recuit simulé" comme solution approximative au "problème du vendeur circulaire", qui est l'un des problèmes difficiles qui sont considérés comme NP difficiles en théorie des graphes. Je l'ai essayé.
«Le problème du voyageur de commerce (TSP) est le coût total de déplacement d'un circuit qui fait le tour de tous les sommets exactement une fois et revient au point de départ, étant donné l'ensemble des sommets et le coût du trajet entre chaque sommet. Est un problème d'optimisation de combinaison qui recherche le plus petit.
Le recuit simulé (SA) est une méthode de résolution d'un problème d'optimisation global nommé d'après la «gravure» en génie des métaux. Commencez la recherche avec une «température» élevée qui vous permet de vous échapper immédiatement même si vous tombez dans une solution locale, et baissez progressivement la «température» pour rechercher une solution globale optimale.
Données de Graph Theory Basics et Graph Theory Basics with matplotlib Animation À titre d'exemple, résolvons le problème du voyageur de commerce.
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 0x7f9f4e7685c0>)
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 |
6 | Fukushima | 37.75000 | 140.46778 |
7 | Mito | 36.34139 | 140.44667 |
8 | Utsunomiya | 36.56583 | 139.88361 |
9 | Maebashi | 36.39111 | 139.06083 |
10 | Saitama | 35.85694 | 139.64889 |
11 | Chiba | 35.60472 | 140.12333 |
12 | Tokyo | 35.68944 | 139.69167 |
13 | Yokohama | 35.44778 | 139.64250 |
14 | Niigata | 37.90222 | 139.02361 |
15 | Toyama | 36.69528 | 137.21139 |
16 | Kanazawa | 36.59444 | 136.62556 |
17 | Fukui | 36.06528 | 136.22194 |
18 | Kofu | 35.66389 | 138.56833 |
19 | Nagano | 36.65139 | 138.18111 |
20 | Gifu | 35.39111 | 136.72222 |
21 | Shizuoka | 34.97694 | 138.38306 |
22 | Nagoya | 35.18028 | 136.90667 |
23 | Tsu | 34.73028 | 136.50861 |
24 | Otsu | 35.00444 | 135.86833 |
25 | Kyoto | 35.02139 | 135.75556 |
26 | Osaka | 34.68639 | 135.52000 |
27 | Kobe | 34.69139 | 135.18306 |
28 | Nara | 34.68528 | 135.83278 |
29 | Wakayama | 34.22611 | 135.16750 |
30 | Tottori | 35.50361 | 134.23833 |
31 | Matsue | 35.47222 | 133.05056 |
32 | Okayama | 34.66167 | 133.93500 |
33 | Hiroshima | 34.39639 | 132.45944 |
34 | Yamaguchi | 34.18583 | 131.47139 |
35 | Tokushima | 34.06583 | 134.55944 |
36 | Takamatsu | 34.34028 | 134.04333 |
37 | Matsuyama | 33.84167 | 132.76611 |
38 | Kochi | 33.55972 | 133.53111 |
39 | Fukuoka | 33.60639 | 130.41806 |
40 | Saga | 33.24944 | 130.29889 |
41 | Nagasaki | 32.74472 | 129.87361 |
42 | Kumamoto | 32.78972 | 130.74167 |
43 | Oita | 33.23806 | 131.61250 |
44 | Miyazaki | 31.91111 | 131.42389 |
45 | Kagoshima | 31.56028 | 130.55806 |
46 | Naha | 26.21250 | 127.68111 |
Voici un diagramme de la relation de position entre les villes.
%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()
Installez le package simanneal pour résoudre la méthode de recuit.
!pip install simanneal
A titre d'exemple, ce package a déjà une classe pour résoudre le problème du voyageur de commerce, vous pouvez donc l'utiliser tel quel.
from simanneal import Annealer
class TravellingSalesmanProblem(Annealer):
"""Test annealer with a travelling salesman problem.
"""
# pass extra data (the distance matrix) into the constructor
def __init__(self, state, distance_matrix):
self.distance_matrix = distance_matrix
super(TravellingSalesmanProblem, self).__init__(state) # important!
def move(self):
"""Swaps two cities in the route."""
# no efficiency gain, just proof of concept
# demonstrates returning the delta energy (optional)
initial_energy = self.energy()
a = random.randint(0, len(self.state) - 1)
b = random.randint(0, len(self.state) - 1)
self.state[a], self.state[b] = self.state[b], self.state[a]
return self.energy() - initial_energy
def energy(self):
"""Calculates the length of the route."""
e = 0
for i in range(len(self.state)):
e += self.distance_matrix[self.state[i-1]][self.state[i]]
return e
La matrice de distance peut être calculée comme ceci en utilisant, par exemple, scipy
import numpy as np
from scipy.spatial import distance
mat = japan[['Latitude', 'Longitude']].values
dist_mat = distance.cdist(mat, mat, metric='euclidean') #Distance euclidienne
Pour l'utiliser avec simanneal, il semble qu'il doive être formaté comme ceci.
distance_matrix = {}
for i, town in enumerate(japan['Town']):
if town not in distance_matrix.keys():
distance_matrix[town] = {}
for j, town2 in enumerate(japan['Town']):
distance_matrix[town][town2] = dist_mat[i][j]
Organisez les villes dans un ordre aléatoire et appelez cela le "circuit initial".
import random
init_state = list(japan['Town'])
random.shuffle(init_state)
Placez-le dans une classe qui résout le problème du voyageur de commerce.
tsp = TravellingSalesmanProblem(init_state, distance_matrix)
tsp.set_schedule(tsp.auto(minutes=0.2))
tsp.copy_strategy = "slice"
state, e = tsp.anneal()
Le circuit a été émis.
state
['Otsu',
'Kyoto',
'Nara',
'Osaka',
'Kobe',
'Tottori',
'Matsue',
'Hiroshima',
'Yamaguchi',
'Fukuoka',
'Saga',
'Nagasaki',
'Naha',
'Kagoshima',
'Miyazaki',
'Kumamoto',
'Oita',
'Matsuyama',
'Kochi',
'Okayama',
'Takamatsu',
'Tokushima',
'Wakayama',
'Tsu',
'Gifu',
'Nagoya',
'Shizuoka',
'Kofu',
'Maebashi',
'Niigata',
'Akita',
'Aomori',
'Sapporo',
'Morioka',
'Sendai',
'Yamagata',
'Fukushima',
'Utsunomiya',
'Mito',
'Chiba',
'Yokohama',
'Tokyo',
'Saitama',
'Nagano',
'Toyama',
'Kanazawa',
'Fukui']
Faites le circuit obtenu, par exemple, un circuit à partir de Tokyo.
while state[0] != 'Tokyo':
state = state[1:] + state[:1] # rotate NYC to start
print()
print("%i mile route:" % e)
print(" ➞ ".join(state))
56 mile route:
Tokyo ➞ Saitama ➞ Nagano ➞ Toyama ➞ Kanazawa ➞ Fukui ➞ Otsu ➞ Kyoto ➞ Nara ➞ Osaka ➞ Kobe ➞ Tottori ➞ Matsue ➞ Hiroshima ➞ Yamaguchi ➞ Fukuoka ➞ Saga ➞ Nagasaki ➞ Naha ➞ Kagoshima ➞ Miyazaki ➞ Kumamoto ➞ Oita ➞ Matsuyama ➞ Kochi ➞ Okayama ➞ Takamatsu ➞ Tokushima ➞ Wakayama ➞ Tsu ➞ Gifu ➞ Nagoya ➞ Shizuoka ➞ Kofu ➞ Maebashi ➞ Niigata ➞ Akita ➞ Aomori ➞ Sapporo ➞ Morioka ➞ Sendai ➞ Yamagata ➞ Fukushima ➞ Utsunomiya ➞ Mito ➞ Chiba ➞ Yokohama
plt.figure(figsize=(10, 10))
Xs = []
Ys = []
for i in range(len(state)):
Xs.append(list(japan[japan['Town'] == state[i]].iloc[:, 2])[0])
Ys.append(list(japan[japan['Town'] == state[i]].iloc[:, 1])[0])
plt.plot(Xs, Ys)
for city, x, y in zip(japan['Town'], japan['Latitude'], japan['Longitude']):
plt.text(x, y, city, alpha=0.5, size=12)
Je vois. Cela ne semble pas être la solution optimale, mais il semble que quelque chose de proche a été obtenu.
Recommended Posts