[PYTHON] Brennmethode und Problem mit reisenden Verkäufern

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.

Patrouillenverkäufer Problem

"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.

Grillmethode

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.

Standortdaten der Präfekturbüros der Präfekturen

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()

output_3_0.png

simanneal Paket

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

Distanzmatrix

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]

Erster Satz

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)

Berechnung starten

tsp.set_schedule(tsp.auto(minutes=0.2))
tsp.copy_strategy = "slice"
state, e = tsp.anneal()

Berechnungsergebnis

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']

Schaltung von der angegebenen Stadt

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

Illustriert

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)

output_14_0.png

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

Brennmethode und Problem mit reisenden Verkäufern
Über das Problem der reisenden Verkäufer
Über das bestellte Patrouillenverkäuferproblem
Ich habe Paizas Kampagnenproblem ausprobiert. (Rundschreiben Verkäufer Problem)
Python: Ich habe das Problem des Handlungsreisenden ausprobiert
Lösen Sie das Problem des Handlungsreisenden mit OR-Tools
Ich habe versucht, das Problem des Handlungsreisenden umzusetzen
Lösung des Problems des Handlungsreisenden mit dem genetischen Algorithmus (GA) und seiner Bibliothek (vcopt)
Lösen Sie das asymmetrische Python-Problem für reisende Verkäufer mit der Branch-and-Bound-Methode
Kombinationsoptimierung - typisches Problem - Rundschreiben Verkäufer Problem