Salve, era da realizzare un programma che facesse ciò:
Implementare in modo semplice in python un algoritmo che risolva il problema del commesso viaggiatore utilizzando per trovare la soluzione un algoritmo di ottimizzazione genetico:
è data una griglia bidimensionale di passo fissato. Posizionate 20 punti in 20 nodi della griglia scelti in modo random (città) il problema è quello di visitare tutte e 20 le città senza mai passare per due volte nella stessa città e minimizzando il cammino fatto.
Suggerimento: codificate il problema con un vettore soluzione di dimensione 20 che contiene l'indice di ciascuna città nell'ordine in cui le visitate e con il vincolo che non ci possono essere due elementi del vettore con eguale valore.
Partite da una soluzione random e eseguite mutazioni che conservano il vincolo (SWAP (esempio: (1,2,3) --> (1,3,2)) e CROSS-OVER ORDINATO (esempio: (1,2,3,4,5) e (3,5,1,4,2) --> (1,4,3,2,5))
evolvete solo le mutazioni positive (che minimizzano la distanza percorsa) mentre lasciate estinguere le soluzioni negative
Graficate poi la soluzione ogni N epoche
Ho fatto ciò utilizzando i suggerimenti dati da questo pdf
http://phd.fisica.unimi.it/assets/Comp_Phys-esercizio4.pdf e questo è il risultato finale:
#ALGORITMO DEL COMMESSO VIAGGIATORE#
#PER SEMPLICITA' IL RETICOLO SARA' QUADRATO E CI SI MUOVERA' A PASSI DI 1#
import random
import numpy as np
import scipy.linalg
import matplotlib.pyplot as plt
%matplotlib inline
##################################################################################################
def random_swap(a): #Scambia a caso due indici adiacenti
l = len(a)
i = random.randint(0,l-1)
if i != l-1 :
a[i],a[i+1] = a[i+1], a[i]
else:
a[i],a[0] = a[0],a[i]
def xandy(lenght,point): #converte x+y*L in (x,y)#
y = int(point/lenght)
x = point - y*lenght
return(x,y)
def euclidean_distance(x_a,y_a,x_b,y_b): #distanza euclidea#
return np.sqrt((x_a - x_b)**2 + (y_a - y_b)**2)
def tot_dist(a): #Calcola la distanza totale percorsa in un cammino
dist = 0
x_c,y_c = 0,0
for j in range(0,L):
x,y = xandy(L,a[j])
dist += euclidean_distance(x_c,y_c,x,y)
x_c,y_c=x,y
return dist
def crossover(x,y,r): #fa il crossover (a metà)#
for i in range(r,L):
for j in range(0,i):
if x[j] != y[i]:
x[i]=y[i]
###################################################################################################
L = 30
V = L*L
cromosomi = [random.sample(range(0, V), L) for _ in range(V)] #lista di liste contenenti i cammini#
fitness=[]
for i in range(0,V): #calcolo la distanza totale percorsa in ciascun cammino e riordino cromosomi con cammini da distanza minore a maggiore#
distanza = tot_dist(cromosomi[i])
fitness.append(distanza)
fitness, cromosomi = zip(*sorted(zip(fitness, cromosomi)))
for i in range(0,int(V/2)):
fitness = []
best_genes = cromosomi[0].copy() #cammino più breve
c = random.randint(0, L) #faccio crossover tra i cammini a e b dall'elemento c in poi#
a,b = random.sample(range(0,V),2)
cromosomi_a = cromosomi[a].copy()
crossover(cromosomi[a],cromosomi[b],c)
crossover(cromosomi[b],cromosomi_a,c)
c_a = cromosomi[a].copy()
c_b = cromosomi[b].copy()
random_swap(cromosomi[a]) #faccio lo swap di elementi adiacenti#
random_swap(cromosomi[b])
d_a_o = tot_dist(c_a)
d_b_o = tot_dist(c_b)
d_a = tot_dist(cromosomi[a])
d_b = tot_dist(cromosomi[b])
if d_a_o <= d_a: #accetto lo swap solamente se minimizza la distanza percorsa#
for k in range(0,L):
cromosomi[a][k] = c_a[k]
if d_b_o <= d_b:
for k in range(0,L):
cromosomi[b][k] = c_b[k]
for k in range(0,L):
cromosomi[V-1][k] = best_genes[k]
d = 0
for i in range(0,V): #riordinamento
d = tot_dist(cromosomi[i])
fitness.append(d)
fitness, cromosomi = zip(*sorted(zip(fitness, cromosomi)))
Ora, dato che vengo dal C, e questo è il primo anno che programmo in python seriamente, il mio modo di programmare è molto basato sul C, mi piacerebbe che mi deste qualche consiglio per rendere il programma migliore e segnalarmi eventuali errori, grazie.
Inoltre non saprei come graficare i vettori, pensavo con un grafo che unisce i punti sul reticolo, come potrei fare?