Zuvor Grundlagen der Graphentheorie und Grundlagen der Graphentheorie mit Matplotlib-Animation usw. Ich habe den Artikel geschrieben, aber ich habe eine Methode ausprobiert, bei der "Simuliertes Tempern" als ungefähre Lösung für das "Circular Salesman Problem" verwendet wurde, eines der schwierigen Probleme, die in der Graphentheorie als NP-schwierig angesehen werden. Ich versuchte es.
"Das Problem des Handlungsreisenden (Travelling Salesman Problem, TSP) sind die Gesamtreisekosten einer Schaltung, die alle Scheitelpunkte genau einmal umrundet und angesichts der Menge der Scheitelpunkte und der Reisekosten zwischen den einzelnen Scheitelpunkten zum Ausgangspunkt zurückkehrt. Ist ein Kombinationsoptimierungsproblem, das das kleinste sucht.
Simuliertes Tempern (SA) ist eine Methode zur Lösung eines globalen Optimierungsproblems, das in der Metalltechnik nach "Brennen" benannt ist. Starten Sie die Suche mit einer hohen "Temperatur", die es Ihnen ermöglicht, sofort zu entkommen, selbst wenn Sie in eine lokale Lösung fallen, und senken Sie die "Temperatur" schrittweise, um nach einer global optimalen Lösung zu suchen.
Daten aus Grundlagen der Graphentheorie und Grundlagen der Graphentheorie mit Matplotlib-Animation Lassen Sie uns als Beispiel das Problem des Handlungsreisenden lösen.
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 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 |
Hier ist ein Diagramm der Positionsbeziehung zwischen Städten.
%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()
Installieren Sie das Paket simanneal, um die Glühmethode zu lösen.
!pip install simanneal
In diesem Paket gibt es beispielsweise bereits eine Klasse zur Lösung des Problems des Handlungsreisenden, sodass Sie es so verwenden können, wie es ist.
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
Die Distanzmatrix kann auf diese Weise beispielsweise mit scipy 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 es mit simanneal verwenden zu können, muss es anscheinend so formatiert werden.
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]
Ordne die Städte in zufälliger Reihenfolge an und nenne sie den "Anfangskreislauf".
import random
init_state = list(japan['Town'])
random.shuffle(init_state)
Stellen Sie es in eine Klasse ein, die das Problem des reisenden Verkäufers löst.
tsp = TravellingSalesmanProblem(init_state, distance_matrix)
tsp.set_schedule(tsp.auto(minutes=0.2))
tsp.copy_strategy = "slice"
state, e = tsp.anneal()
Die Schaltung wurde ausgegeben.
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']
Machen Sie die erhaltene Schaltung zum Beispiel zu einer Schaltung ab Tokio.
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)
Das war's. Es scheint nicht die optimale Lösung zu sein, aber es scheint, dass etwas in der Nähe davon erhalten wurde.
Recommended Posts