You are on page 1of 13

31/5/2022 8_Metaheuristics_TSP

Μετα-ευριστικοί Αλγόριθμοι για το Πρόβλημα του


Περιοδεύοντος Πωλητή
In [2]:

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from tqdm import tqdm
#np.random.seed(33)

Στο 7ο μάθημα είδαμε τον Γενετικό Αλγόριθμο, που είναι (με διαφορά) ο πιο δημοφιλής μετα-ευριστικός
αλγόριθμος βελτιστοποίησης. Στα μαθήματα 8 και 9 θα δούμε τους τέσσερις αμέσως δημοφιλέστερους μετα-
ευριστικούς αλγόριθμους, από δύο σε κάθε μάθημα.

Σήμερα θα δούμε δύο αλγόριθμους που χρησιμοποιούνται για προβλήματα τύπου Περιοδεύοντος Πωλητή.
Ας θυμηθούμε το πρόβλημα:

Έχουμε το παρακάτο σύνολο σημείων στον δισδιάστατο χώρο και ψάχνουμε το μονοπάτι που θα περάσει
από όλα τα σημεία διανύοντας την μικρότερη συνολική απόσταση, δηλαδή το συντομότερο μονοπάτι.

In [4]:

points = np.array([[2.12,0.7],[1.68,0.97],[1.28,1.5],[1.16,2.3],[1.12,4],
[1.2,5.4],[1.32,7.2],[1.64,8.4],[1.92,8.6],[2.2,8],
[2.52,7.6],[2.72,6.4],[2.8,4.8],[2.72,3.3],[2.56,2.3],[2.4,1.2]])
#points.shape
plt.scatter(points[:,0],points[:,1]);

Οι αλγόριθμοι που θα εξετάσουμε είναι οι Tabu Search, και Ant Colony Optimization.

1. Tabu Search

http://localhost:8888/lab 1/13
31/5/2022 8_Metaheuristics_TSP

H μέθοδος tabu search, όπως την υλοποιούμε εδώ, είναι σαν ένας γενετικός αλγόριθμος για προβλήματα
περιοδεύοντος πωλητή, με δύο κύριες διαφορές:

Δεν έχουμε crossover αλλά μόνο mutation (bitswap).


Κρατούμε μια tabu list, δηλαδή μια λίστα από μονοπάτια που έχουμε ήδη εξετάσει πρόσφατα, τα οποία
δεν ξαναχρησιμοποιούμε στις αμέσως επόμενες επαναλήψεις.

Με άλλα λόγια, η tabu search που υλοποιούμε εδώ είναι ένας γενετικός όπου: 1) γίνεται αναζήτηση μονο σε
γειτονικά μονοπάτια από αυτά που ήδη έχουμε (η λειτουργία crossover παράγει μονοπάτια αρκετά
διαφορετικά από τους "γονείς" τους, ενώ η mutation/bitswap φτιάχνει πολύ μικρές παραλλαγές των αρχικών),
και 2) έχουμε ένα είδος μνήμης όπου θυμόμαστε τα μονοπάτια που περάσαμε, και δεν ξαναπερνάμε από
αυτά για έναν τουλάχιστον αριθμό επαναλήψεων.

Υπάρχουν και άλλες μικροδιαφορές με τον Γενετικό αλγόριθμο, αλλά αυτές οι δύο είναι οι κύριες. Θα
γράψουμε τώρα κάποιες συναρτήσεις που θα χρησιμοποιήσουμε, και που μας επιτρέπουν:

να υπολογίσουμε την ευκλείδια απόσταση μεταξύ δυο σημείων


να υπολογίσουμε την συνολική απόσταση ενός μονοπατιού από διάφορα σημεία (έπειτα την
τεστάρουμε)
να φτιάξουμε μια παραλλαγή, χρησιμοποιώντας λειτουργία bitswap, ενός δοθέντος μονοπατιού
να φτιάξουμε έναν αριθμό παραλλαγών ενός μονοπατιού

In [78]:

def eucl_dist(x,y):
summation = 0
for dim in np.arange(len(x)):
summation += (x[dim] - y[dim])**2

return np.sqrt(summation)

In [11]:

def path_length(array):

length = 0
for i in np.arange(array.shape[0]-1):
length += eucl_dist(array[i],array[i+1])
length += eucl_dist(array[-1],array[0])
return length

http://localhost:8888/lab 2/13
31/5/2022 8_Metaheuristics_TSP

In [15]:

indices = np.arange(points.shape[0])
np.random.shuffle(indices)
print(path_length(points[indices]))
plt.plot(points[indices,0],points[indices,1]);

52.955720876862955

In [16]:

# swaps two random elements of a sequence


def bitswap(array):
choices = np.random.choice(np.arange(array.shape[0]),2,replace=False)
t1 = array[choices[0]]
t2 = array[choices[1]]
array = np.delete(array,choices[1])
array = np.insert(array,choices[1],t1)
array = np.delete(array,choices[0])
array = np.insert(array,choices[0],t2)
return array

In [17]:

# produces an array of m bitswapped versions of a path vector


def get_neighbors(path,m):
neighbors = np.zeros((m,path.shape[0]),dtype='int')
for i in np.arange(m):
neighbors[i] = bitswap(path)
return neighbors

http://localhost:8888/lab 3/13
31/5/2022 8_Metaheuristics_TSP

Τώρα θα γράψουμε τον ολοκληρωμένο αλγόριθμο, ο οποίος έχει ως εξής:

1. αρχικοποιούμε ένα τυχαίο μονοπάτι και το βάζουμε στην λίστα ταμπού. Το αποθηκεύουμε επίσης στην
μεταβλητή με το καλύτερο μονοπάτι που βρήκαμε έως τώρα, και στην μεταβλητή με το μονοπάτι
υποψήφιο να αντικαταστήσει το καλύτερο μονοπάτι.
2. Χρησιμοποιώντας την συνάρτηση bitswap που ορίσαμε στο μάθημα με τους γενετικούς αλγορίθμους,
φτιάχνουμε έναν αριθμό παραλλαγών του βέλτιστου μονοπατιού.
3. Όποια μονοπάτια-παραλλαγές βρίσκονται στην λίστα ταμπού, δηλαδή τα έχουμε ήδη εξετάσει
πρόσφατα, τα προσπερνάμε χωρίς να τα ξανα-εξετάσουμε.
4. Απ'τα εναπομείμαντα μονοπάτια, αν το καλύτερο από αυτά είναι καλύτερο (συντομότερο) απ'το
μονοπάτι-υποψηφίου, το αντικαθιστάμε.
5. Αν το μονοπάτι-υποψήφιος είναι καλύτερο απ'το βέλτιστο μέχρι τώρα μονοπάτι, τότε ο υποψήφιος γίνεται
τώρα το βέλτιστο μονοπάτι.
6. Εξετάζουμε αν ο αριθμός μονοπατιών στην λίστα ταμπού ξεπαερνάει έναν προκαθορισμένο αριθμό
στοιχείων. Αν ναι, τότε αφαιρούμε το πιο παλιό μονοπάτι της λίστας, δηλαδή "ξεχνάμε" ότι έχουμε
περάσει από εκεί, και τώρα μπορούμε, αν τύχει, να ξαναπεράσουμε.
7. Επιστροφή στο βήμα 2. Loop για προκαθορισμένο αριθμό επαναλήψεων.
8. Το καλύτερο μονοπάτι που βρήκε ο αλγόριθμος βρίσκεται αποθηκευμένο στην μεταβλητή με το βέλτιστο
μονοπάτι.

Γράφουμε τον κώδικα σε μορφή συνάρτησης, που δέχεται ως ορίσματα:

τα σημεία x,y που θέλουμε να βρούμε το συντομότερο μονοπάτι που να τα διανύει


το μέγιστο μέγεθος της λίστας ταμπού
τον αριθμό επαναλήψεων
τον αριθμό παραλλαγών του καλύτερου μονοπατιού που θα φτιάξουμε σε κάθε επανάληψη

http://localhost:8888/lab 4/13
31/5/2022 8_Metaheuristics_TSP

In [77]:

def tabu_salesman(points,max_tabu_size = 170, n_iter=400, neighborhood_size=4):

best_path = np.arange(points.shape[0])
np.random.shuffle(best_path)
best_distance = path_length(points[best_path])
candidate = best_path.copy()
candidate_dist = best_distance.copy()
tabu = []
tabu.append(str(best_path).replace(' ','')[1:-1])

for i in tqdm(range(n_iter)):

#select candidate
neighborhood = get_neighbors(best_path,neighborhood_size)
for neighbor in neighborhood:
neighbor_dist = path_length(points[neighbor])
if (str(neighbor).replace(' ','')[1:-1] in tabu) == False:
if neighbor_dist < candidate_dist:
candidate = neighbor.copy()
candidate_dist = neighbor_dist.copy()
break

#will candidate replace best solution?


if candidate_dist < best_distance:
best_path = candidate.copy()
best_distance = candidate_dist.copy()

#tabu list maintenance


tabu.append(str(candidate).replace(' ','')[1:-1])
if len(tabu) > max_tabu_size:
tabu = tabu[1:]

return best_path

Εδώ τρέχουμε την συνάρτηση

http://localhost:8888/lab 5/13
31/5/2022 8_Metaheuristics_TSP

In [76]:

path = tabu_salesman(points,
max_tabu_size = 170,
n_iter = 400,
neighborhood_size = 14)

plt.plot(points[path,0],
points[path,1]);

100%|██████████| 400/400 [00:01<00:00, 338.86it/s]

2. Ant Colony Optimization

Ο δεύτερος αλγόριθμος που θα δούμε είναι ο Ant Colony Optimization. Αρχικά, προκειμένου να κάνουμε
αυτό το τμήμα του notebook αυτόνομο, ας επαναεισάγουμε τις βιβλιοθήκες και τα δεδομένα, και ας
ξαναορίσουμε τις συναρτήσεις υπολογισμού ευκλείδιας απόστασης και συνολικής απόστασης μονοπατιών.

In [2]:

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from tqdm import tqdm
#np.random.seed(33)

points = np.array([[2.12,0.7],[1.68,0.97],[1.28,1.5],[1.16,2.3],[1.12,4],
[1.2,5.4],[1.32,7.2],[1.64,8.4],[1.92,8.6],[2.2,8],
[2.52,7.6],[2.72,6.4],[2.8,4.8],[2.72,3.3],[2.56,2.3],[2.4,1.2]])

http://localhost:8888/lab 6/13
31/5/2022 8_Metaheuristics_TSP

In [3]:

def eucl_dist(x,y):

summation = 0
for dim in np.arange(len(x)):
summation += (x[dim] - y[dim])**2

return np.sqrt(summation)

In [4]:

def path_length(array):

length = 0
for i in np.arange(array.shape[0]-1):
length += eucl_dist(array[i],array[i+1])
length += eucl_dist(array[-1],array[0])
return length

Καθώς τα μυρμήγκια διασχίζουν τον χώρο, αφήνουν πίσω τους ένα ίχνος φερομόνης. Αν κάποιο άλλο
μυρμήγκι μυρίσει αυτές τις φερομόνες, αυξάνονται κατά κάποιο ποσοστό οι πιθανότητες να ακολουθήσει αυτό
το μονοπάτι και όχι κάποιο άλλο. Όσο περισσότερα μυρμήγκια περνάνε από ένα μονοπάτι, τόσο αυξάνονται
οι φερομόνες του μονοπατιού, και τόσο αυξάνονται οι πιθανότητες να ακολουθήσουν και άλλα μυρμήγκια, τα
οποία με τη σειρά τους θα αφήσουν τις δικές τους φερομόνες, αυξάνοντας τις πιθανότητες του μονοπατιού
επιπλέον.

Αυτός είναι ο λόγος που βλέπουμε τα μυρμήγκια να πηγαινοέρχονται σε μια συγκεκριμένη γραμμή, και αυτή
είναι μια λειτουργία που εξομειώνει ο αλγόριθμος ACO.

Συνοπτικά ο αλγόριθμος δουλεύει ως εξής. Κάθε μυρμήγκι βρίσκεται σε ένα τυχαίο σημείο. Το ποιο σημείο θα
επιλέξει ως το επόμενο στο μονοπάτι του εξαρτάται από τέσσερις παράγοντες:

1. Ποιο είναι το πιο κοντινό σημείο από εκεί που βρίσκεται


2. Ποιο είναι το επόμενο σημείο στο συντομότερο αποθηκευμένο μονοπάτι από προηγούμενες αναζητήσεις
3. Το πέρασμα προς ποιο μονοπάτι έχεις περισσότερες φερομόνες
4. Τυχαιότητα, δηλαδή παίζει ρόλο και ο παράγοντας τύχη, με την επιλογή για το επόμενο σημείο να γίνεται
στοχαστικά-πιθανολογικά.

Παρακάτω φτιάχνουμε μια συνάρτηση που να υπολογίζει έναν πίνακα αποστάσεων από κάθε σημείο σε όλα
τα άλλα. Ο αλγόριθμος θα συμβουλεύεται αυτόν τον πίνακα προεκιμένου να προσδιορίσει τον πρώτο από
τους τέσσερις παράγοντες που είδαμε πιο πάνω, δηλαδή για κάθε σημείο όπου θα βρισκόμαστε θα θέλουμε
να ξέρουμε πιο είναι το πλησιέστερο σημείο.

In [5]:

def distance_matrix(points):
dim = points.shape[0]
dist_mat = np.zeros((dim,dim))
for i, pi in enumerate(points):
for k, ki in enumerate(points):
dist_mat[i,k] = eucl_dist(pi,ki)
return dist_mat

http://localhost:8888/lab 7/13
31/5/2022 8_Metaheuristics_TSP

Σχηματίζουμε πίνακα αποστάσεων των σημείων-δεδομένων της παρούσας άσκησης, και σχεδιάζουμε ένα
heatmap του πίνακα για να δούμε πώς μοιάζει.

Η μαύρη διαγώνιος σημαίνει ότι κάθε σημείο απέχει μηδενική απόσταση από τον εαυτό του.

Το σχήμα σταυρού που σχηματίζουν οι φωτεινές περιοχές σημαίνουν ότι, επειδή τα σημεία μας σχηματίζουν
κυκλική διαδρομή, κάθε στοιχείο έχει ως κοντινότερο το διπλανό του, αμέσως κοντινότερο το διπλανό αυτού,
κτλ, μέχρι την απέναντι πλευρά του κύκλου, όπου έχουμε το μακρινότερο σημείο, και μετά ξαναεπιστρέφουμε,
σημείο προς σημείο, στην αρχική μας θέση. Έτσι στο heatmap, σε οποιοδήποτε μαύρο σημείο της διαγωνίου
κοιτάξεις, όσο πας προς τα δεξιά αρχίζει να γίνεται φωτεινότερο, κάποια στιγμή γίνεται το πιο φωτεινό από
όλα, δηλαδή φθάσαμε στην απέναντι πλευρά του κύκλου, και έπειτα αρχίζουν να σκουραίνουν καθώς
γυρνάμε πίσω στο αρχικό σημείο.

In [10]:

#dist_map demo
import seaborn as sns
dist_map = distance_matrix(points)
sns.heatmap(dist_map);

Πριν ξεκινήσουμε τον αλγόριθμο, μια άλλη δομή δεδομένων που θα χρειαστούμε είναι ο λεγόμενος "πίνακας
ήτα", ο οποίος βρίσκεται διαιρώντας κάθε στοιχείο του πίνακα αποστάσεων στο ένα και υψώνοντας σε μια
δύναμη $α$, με $α$ να είναι παράμετρος που ρυθμίζουμε εμείς.

Στην περίπτωσή μας $α=1$ οπότε, όπως βλέπουμε στο heatmap, ο πίνακας ήτα είναι αρνητική έκδοση του
πίνακα αποστάσεων, δηλαδή είναι το ίδιο σχέδιο με αντεστραμμένα χρώματα.

http://localhost:8888/lab 8/13
31/5/2022 8_Metaheuristics_TSP

In [15]:

dist_mat = distance_matrix(points)
a = 1
ita_mat = (1/dist_mat)**a
# fix the diagonal, it has value 'inf' because of divide by zero
for i in np.arange(dist_mat.shape[0]):
for j in np.arange(dist_mat.shape[1]):
if i==j:ita_mat[i,j] = 0

import seaborn as sns


sns.heatmap(ita_mat);

C:\Users\stefanos\.conda\envs\three-seven\lib\site-packages\ipykernel_laun
cher.py:3: RuntimeWarning: divide by zero encountered in true_divide
This is separate from the ipykernel package so we can avoid doing import
s until

Τώρα θα ασχοληθούμε με ένα κεντρικό μέρος του αλγορίθμου: τα μυρμήγκια.

Θα ορίσουμε, με αντικειμενοστραφή προγραμματισμό, την κλάση για τα μυρμήγκια, η οποία θα περιέχει


κάποια χαρακτηριστικά των μυρμηγκιών, και συναρτήσεις για να καθορίζουμε την διαδρομή των μυρμηγκιών,
καθώς και να καθαρίζουμε τις χρησιμοποιημένες δομές δεδομένων κάθε φορά που κάποιο μυρμήγκι
ολοκληρώνει κάποια διαδρομή.

To κάθε μυρμήγκι θα είναι ένα object της κλάσης Ant, και θα έχει τα εξής δεδομένα αποθηκευμένα και
συνδεδεμένα με την μεταβλητή στην οποία το ορίσαμε:

n_locations: ο αριθμός των κόμβων-σημείων στο εκάστοτε πρόβλημα που θέλουμε να λύσουμε
position: σε ποιο κόμβο-σημείο βρίσκεται το μυρμήγκι αυτή την στιγμή
places_visited: λίστα με τα σημεία από όπου έχει περάσει το μυρμήγκι
places_left: λίστα με τα σημεία από όπου δεν έχει περάσει ακόμα
phero_graph: πίνακας με τις φερομόνες που συνδέουν κάθε πιθανό ζευγάρι σημείων του δικτύου
travel_probas: λίστα με τις πιθανότητες για κάθε σημείο που έχει περάσει ακόμα το μυρμήγκι
tour_cost: το συνολικό μήκος της διαδρομής που έχει διανύσει το μυρμήγκι μέχρι αυτή την στιγμή

Σε κάθε επανάληψη εκτελείται η συνάρτηση ant_trip για κάθε μυρμήγκι ώσπου αυτό να εκτελέσει μια
ολοκληρωμένη διαδρομή, και η συνάρτηση ant_flush για να ξανα-αρχικοποιηθούν τα δεδομένα της
μεταβλητής τους, ώστε να είναι έτοιμα για την επόμενη επανάληψη.

http://localhost:8888/lab 9/13
31/5/2022 8_Metaheuristics_TSP

In [6]:

class Ant:
def __init__(self,n_locations):
self.n_locations = n_locations

self.position = np.random.choice(n_locations)

self.places_visited = [self.position]
self.places_left = list(np.arange(n_locations))
self.places_left.remove(self.position)

self.phero_graph = np.full((n_locations,n_locations),0.0)
self.travel_probas = np.zeros(n_locations-1)

self.tour_cost = 0.0

def ant_trip(self,g_phero_graph,dist_mat,ita_mat,a=1,b=1,Q=1):

for i in np.arange(len(self.places_left)):
#------------------------------------------
#determine probabilities for next move
#we will calculate numerators and denominators for proba-calculating fracti
ons
allowed_weights = []
for loc in self.places_left:
allowed_weights.append((g_phero_graph[self.position,loc]**a)*(ita_mat[s
elf.position,loc]**b)) #vector with numerators for each allowable moves
allowed_weights_sum = np.sum(allowed_weights)#this is the denominator of th
e proba-calculating fraction
travel_probas = allowed_weights/allowed_weights_sum
#------------------------------------------
#stochastically pick next destination, update everything that needs updatin
g
next_destination = np.random.choice(self.places_left,p=travel_probas)

#before changing position, add distance into total distance travelled...


self.tour_cost += dist_mat[self.position,next_destination]
#...then change position, update travel-log variables
self.position = next_destination
self.places_visited.append(next_destination)
self.places_left.remove(next_destination)

#after it has finished, update self.phero_graph


for i,j in zip(self.places_visited[:-1],self.places_visited[1:]):
self.phero_graph[i,j] = Q/self.tour_cost

def ant_flush(self):

self.position = np.random.choice(self.n_locations)

self.places_visited = [self.position]
self.places_left = list(np.arange(self.n_locations))
self.places_left.remove(self.position)

#self.phero_graph = np.full((n_locations,n_locations),0.0)
self.travel_probas = np.zeros(self.n_locations-1)
http://localhost:8888/lab 10/13
31/5/2022 8_Metaheuristics_TSP

self.tour_cost = 0.0

Είπαμε ότι οι πιθανότητες για το ποιο θα είναι το επόμενο σημείο στην διαδρομή ενός μυρμηγκιού
εξαρτώνται, εντός άλλων παραγόντων, και από τις φερομόνες που είναι συνδεδεμένες με κάθε πέρασμα από
ένα σημείο σε ένα άλλο του μονοπατιού. Αυτές οι φερομόνες είναι αποθηκευμένες σε ένα πίνακα, ο οποίος
θα ανανεώνεται κάθε φορά που ένα μυρμήγκι ολοκληρώνει μια διαδρομή. Η ανανέωση αυτή παίρνει υπόψιν:

με ποια σειρά προσπέλασε τα σημεία το μυρμήγκι


πόσο σύντομη ήταν η διαδρομή (όσο πιο σύντομη, τόσο καλύτερη, άρα και πιο πολύ φερομόνη
εναποτίθεται, ώστε η διαδρομή να φαίνεται ελκυστική στα υπόλοιπα μυρμήγκια
τον συντελεστή εξάτμισης της φερομόνης (πόσο θα διαρκεί η φερομόνη πριν αρχίσει να εξατμίζεται και
να αντικαθίσταται από πιο πρόσφατες φερομόνες. Κάτι σαν "μνήμη" του συστήματος)

Γράφουμε τώρα την συνάρτηση ανανέωσης φερομονών

In [7]:

def update_pheromones(g_phero_graph,ants,evapo_coef=0.05):
one_minus = 1 - evapo_coef
dim = g_phero_graph.shape[0]
for i in range(dim):
for j in range(dim):
g_phero_graph[i,j] = one_minus*g_phero_graph[i,j] + np.sum([ant.phero_graph
[i,j] for ant in ants])
return g_phero_graph

Γράφουμε τώρα τον κύριο αλγόριθμο σε μορφή συνάρτησης, και κατόπιν τον τρέχουμε. Η συνάρτηση παίρνει
τα εξής ορίσματα:

points: τα σημεία συντεταγμένων [x,y] μεταξύ των οποίων καλούμεστε να βρούμε βέλτιστη διαδρομή
a: πόσο μεγάλο ρόλο θα παίζουν οι φερομόνες στην επιλογή της διαδρομής,δηλαδή μεγαλύτερο a
σημαίνει πως κοιτάμε ολοένα και περισσότερο το τί διαδρομή κάναν τα άλλα μυρμήγκια
b: πόσο μεγάλο ρόλο θα παίζει ο πίνακας ήτα στην επιλογή της διαδρομής, δηλαδή μεγαλύτερο b
σημαίνει πιθανότερο να πάει το μυρμήγκι στο κοντινότερο σημείο από όπου βρίσκεται
evapo_coef: συντελεστής εξάτμισης φερομόνης, κάτι σαν μνήμη του συστήματος φερομονών
Q: παράμετρος που σχετίζεται με την δύναμη των φερομονών
colony_size: αριθμός μυρμηγκιών που θα χρησιμοποιηθούν
n_iter: πόσες επαναλήψεις θα τρέξουν

Σε κάθε επανάληψη υπολογίζουμε και αποθηκεύουμε το καλύτερο μονοπάτι, το οποίο και επιστρέφεται από
την συνάρτηση στο πέρας της εκτέλεσης του κώδικα.

http://localhost:8888/lab 11/13
31/5/2022 8_Metaheuristics_TSP

In [16]:

def aco(points,a,b,evapo_coef,Q,colony_size,n_iter):
n_locations = points.shape[0]
dist_mat = distance_matrix(points)
path_lengths = [] #to store length of each ant's path, later pick best

ita_mat = (1/dist_mat)**a
# to fix the diagonal, it has value 'inf' because of divide by zero
for i in np.arange(dist_mat.shape[0]):
for j in np.arange(dist_mat.shape[1]):
if i==j:ita_mat[i,j] = 0

ants = [Ant(n_locations) for i in range(colony_size)]


monitor_costs = []
monitor_iter_winner = []

g_phero_graph = np.full((n_locations,n_locations),0.04)#init with arbitrary non-zer


o

for i in tqdm(range(n_iter)):

[ant.ant_trip(g_phero_graph,dist_mat,ita_mat,Q) for ant in ants]


g_phero_graph = update_pheromones(g_phero_graph,ants,evapo_coef).copy()

iteration_winner = np.argmin([ant.tour_cost for ant in ants]) #ant that scored


best in this iteration
monitor_iter_winner.append(iteration_winner)
best_path = ants[iteration_winner].places_visited
monitor_costs.append(ants[iteration_winner].tour_cost)
[ant.ant_flush() for ant in ants]

return best_path,monitor_costs

http://localhost:8888/lab 12/13
31/5/2022 8_Metaheuristics_TSP

In [23]:

best_path, monitor_costs = aco(points = points,


a = 7,
b = 1,
evapo_coef = 0.1,
Q = 13,
colony_size = 30,
n_iter = 100)
print (monitor_costs[-1])
print('')

plt.figure(figsize=(20,5));
plt.subplot(1,2,1);
plt.plot(points[best_path,0],
points[best_path,1]);

plt.subplot(1,2,2);
plt.plot(np.arange(len(monitor_costs)),monitor_costs);

100%|██████████| 100/100 [00:02<00:00, 41.24it/s]

14.8703780408434

http://localhost:8888/lab 13/13

You might also like