Ajout des codes de tests et valeurs démo
This commit is contained in:
parent
286d49fb20
commit
cff833f61d
64
tests/01_cluster_splitter.py
Normal file
64
tests/01_cluster_splitter.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
from sklearn.cluster import KMeans
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
import random, time
|
||||||
|
from clustering import split_tour_across_clusters
|
||||||
|
|
||||||
|
def generate_cities(nb, max_coords=1000):
|
||||||
|
return [random.sample(range(max_coords), 2) for _ in range(nb)]
|
||||||
|
|
||||||
|
def plot_clusters(cities, clusters):
|
||||||
|
# Création d'une liste de couleurs pour les différents clusters
|
||||||
|
colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k']
|
||||||
|
|
||||||
|
# Création d'un nouveau graphique
|
||||||
|
plt.figure()
|
||||||
|
|
||||||
|
# Pour chaque cluster
|
||||||
|
for i, cluster in clusters.items():
|
||||||
|
# Sélection d'une couleur pour le cluster
|
||||||
|
color = colors[i % len(colors)]
|
||||||
|
|
||||||
|
# Pour chaque ville dans le cluster
|
||||||
|
for city_index in cluster:
|
||||||
|
# Récupération des coordonnées de la ville
|
||||||
|
city = cities[city_index]
|
||||||
|
|
||||||
|
# Ajout de la ville au graphique
|
||||||
|
plt.scatter(city[0], city[1], c=color, s=20)
|
||||||
|
|
||||||
|
# show first city in black and twice bigger
|
||||||
|
plt.scatter(cities[0][0], cities[0][1], c='k', s=200)
|
||||||
|
|
||||||
|
|
||||||
|
# Affichage du graphique
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
nb_ville = 100
|
||||||
|
max_coords = 1000
|
||||||
|
nb_truck = 4
|
||||||
|
|
||||||
|
# Define the coordinates of the cities
|
||||||
|
# And set depot at the first city in the middle of the map
|
||||||
|
start_time_generate = time.time()
|
||||||
|
cities = generate_cities(nb_ville, max_coords)
|
||||||
|
cities[0] = [max_coords/2, max_coords/2]
|
||||||
|
stop_time_generate = time.time()
|
||||||
|
|
||||||
|
# Split the tour across clusters with nb_truck trucks
|
||||||
|
start_time_split = time.time()
|
||||||
|
clusters = split_tour_across_clusters(cities, nb_truck)
|
||||||
|
stop_time_split = time.time()
|
||||||
|
|
||||||
|
# show the number of cities in each cluster
|
||||||
|
for cluster in clusters.values():
|
||||||
|
print(len(cluster))
|
||||||
|
|
||||||
|
# show the time
|
||||||
|
print("\n---- TIME ----")
|
||||||
|
print("generate cities time: ", stop_time_generate - start_time_generate)
|
||||||
|
print("split cities time: ", stop_time_split - start_time_split)
|
||||||
|
|
||||||
|
# show the clusters
|
||||||
|
plot_clusters(cities, clusters)
|
||||||
108
tests/02_cluster_recuit_live_animation.py
Normal file
108
tests/02_cluster_recuit_live_animation.py
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
from sklearn.cluster import KMeans
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
import random, time, math
|
||||||
|
from clustering import split_tour_across_clusters
|
||||||
|
|
||||||
|
random.seed(1)
|
||||||
|
|
||||||
|
def generate_cities(nb, max_coords=1000):
|
||||||
|
return [random.sample(range(max_coords), 2) for _ in range(nb)]
|
||||||
|
|
||||||
|
def distance(city1, city2):
|
||||||
|
return math.sqrt((city1[0] - city2[0]) ** 2 + (city1[1] - city2[1]) ** 2)
|
||||||
|
|
||||||
|
def total_distance(cities):
|
||||||
|
return sum([distance(cities[i - 1], cities[i]) for i in range(len(cities))])
|
||||||
|
|
||||||
|
previous_route = None
|
||||||
|
|
||||||
|
def draw_cities(cities, previous_route, color='b', title=' '):
|
||||||
|
plt.title(title)
|
||||||
|
|
||||||
|
# If there's a previous route, we remove it.
|
||||||
|
if previous_route is not None:
|
||||||
|
previous_route.remove()
|
||||||
|
|
||||||
|
x = [city[0] for city in cities]
|
||||||
|
y = [city[1] for city in cities]
|
||||||
|
x.append(x[0])
|
||||||
|
y.append(y[0])
|
||||||
|
|
||||||
|
# We plot the route with the specified color and keep a reference to the Line2D object.
|
||||||
|
previous_route, = plt.plot(x, y, color=color, marker='x', linestyle='-')
|
||||||
|
|
||||||
|
plt.draw()
|
||||||
|
plt.pause(0.005)
|
||||||
|
|
||||||
|
# We return the reference so we can remove this route when a new one is found.
|
||||||
|
return previous_route
|
||||||
|
|
||||||
|
|
||||||
|
def simulated_annealing(cities, color='b', temperature=100000, cooling_rate=0.9999, temperature_ok=0.001):
|
||||||
|
interration = 0
|
||||||
|
plt.ion()
|
||||||
|
current_solution = cities.copy()
|
||||||
|
best_solution = cities.copy()
|
||||||
|
previous_route = draw_cities(best_solution, None, color, 'Initial solution')
|
||||||
|
while temperature > temperature_ok:
|
||||||
|
new_solution = current_solution.copy()
|
||||||
|
# Swap two cities in the route
|
||||||
|
i = random.randint(0, len(new_solution) - 1)
|
||||||
|
j = random.randint(0, len(new_solution) - 1)
|
||||||
|
new_solution[i], new_solution[j] = new_solution[j], new_solution[i]
|
||||||
|
# Calculate the acceptance probability
|
||||||
|
current_energy = total_distance(current_solution)
|
||||||
|
new_energy = total_distance(new_solution)
|
||||||
|
delta = new_energy - current_energy
|
||||||
|
if delta < 0 or random.random() < math.exp(-delta / temperature):
|
||||||
|
current_solution = new_solution
|
||||||
|
if total_distance(current_solution) < total_distance(best_solution):
|
||||||
|
best_solution = current_solution
|
||||||
|
previous_route = draw_cities(best_solution, previous_route, color, 'Current best solution, total distance = ' + str(total_distance(best_solution)) + ', iteration = ' + str(interration))
|
||||||
|
# Cool down
|
||||||
|
temperature *= cooling_rate
|
||||||
|
interration += 1
|
||||||
|
plt.ioff()
|
||||||
|
return best_solution
|
||||||
|
|
||||||
|
nb_ville = 100
|
||||||
|
max_coords = 1000
|
||||||
|
nb_truck = 4
|
||||||
|
temperature = 10000
|
||||||
|
cooling_rate = 0.999
|
||||||
|
temperature_ok = 0.000001
|
||||||
|
|
||||||
|
start_time_generate = time.time()
|
||||||
|
cities = generate_cities(nb_ville, max_coords) # generate 100 cities
|
||||||
|
cities[0] = [max_coords/2, max_coords/2] # placing depot at the center
|
||||||
|
stop_time_generate = time.time()
|
||||||
|
|
||||||
|
|
||||||
|
start_time_split = time.time()
|
||||||
|
clusters = split_tour_across_clusters(cities, nb_truck)
|
||||||
|
stop_time_split = time.time()
|
||||||
|
|
||||||
|
for cluster in clusters.values():
|
||||||
|
print(len(cluster))
|
||||||
|
print("\n---- TIME ----")
|
||||||
|
print("generate cities time: ", stop_time_generate - start_time_generate)
|
||||||
|
print("split cities time: ", stop_time_split - start_time_split)
|
||||||
|
|
||||||
|
# create new figure for annealing paths
|
||||||
|
plt.figure()
|
||||||
|
colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k']
|
||||||
|
|
||||||
|
for i, cluster_indices in enumerate(clusters.values()):
|
||||||
|
# Sélection d'une couleur pour le cluster
|
||||||
|
color = colors[i % len(colors)]
|
||||||
|
|
||||||
|
# Récupération des coordonnées de la ville
|
||||||
|
cluster_cities = [cities[index] for index in cluster_indices]
|
||||||
|
|
||||||
|
# Appel de la fonction simulated_annealing avec la couleur choisie
|
||||||
|
best_route = simulated_annealing(cluster_cities, color, temperature, cooling_rate, temperature_ok)
|
||||||
|
print("Final solution for cluster ", i, ":", best_route)
|
||||||
|
print("Total distance: ", total_distance(best_route))
|
||||||
|
|
||||||
|
plt.show()
|
||||||
115
tests/03_cluster_recuit_no_animation.py
Normal file
115
tests/03_cluster_recuit_no_animation.py
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
from sklearn.cluster import KMeans
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
import random, time, math
|
||||||
|
from clustering import split_tour_across_clusters
|
||||||
|
|
||||||
|
def generate_cities(nb, max_coords=1000):
|
||||||
|
return [random.sample(range(max_coords), 2) for _ in range(nb)]
|
||||||
|
|
||||||
|
def distance(city1, city2):
|
||||||
|
return math.sqrt((city1[0] - city2[0]) ** 2 + (city1[1] - city2[1]) ** 2)
|
||||||
|
|
||||||
|
def total_distance(cities):
|
||||||
|
return sum([distance(cities[i - 1], cities[i]) for i in range(len(cities))])
|
||||||
|
|
||||||
|
previous_route = None
|
||||||
|
|
||||||
|
def simulated_annealing(cities, temperature=10000, cooling_rate=0.9999, temperature_ok=0.001, cluster_index=0):
|
||||||
|
interration = 0
|
||||||
|
current_solution = cities.copy()
|
||||||
|
best_solution = cities.copy()
|
||||||
|
while temperature > temperature_ok:
|
||||||
|
new_solution = current_solution.copy()
|
||||||
|
# Swap two cities in the route
|
||||||
|
i = random.randint(0, len(new_solution) - 1)
|
||||||
|
j = random.randint(0, len(new_solution) - 1)
|
||||||
|
new_solution[i], new_solution[j] = new_solution[j], new_solution[i]
|
||||||
|
# Calculate the acceptance probability
|
||||||
|
current_energy = total_distance(current_solution)
|
||||||
|
new_energy = total_distance(new_solution)
|
||||||
|
delta = new_energy - current_energy
|
||||||
|
if delta < 0 or random.random() < math.exp(-delta / temperature):
|
||||||
|
current_solution = new_solution
|
||||||
|
if total_distance(current_solution) < total_distance(best_solution):
|
||||||
|
best_solution = current_solution
|
||||||
|
# Cool down
|
||||||
|
temperature *= cooling_rate
|
||||||
|
interration += 1
|
||||||
|
# Print every 1000 iterations
|
||||||
|
if interration % 1000 == 0:
|
||||||
|
print("Cluster", cluster_index, ": iteration", interration, "with current total distance", total_distance(current_solution))
|
||||||
|
return best_solution
|
||||||
|
|
||||||
|
|
||||||
|
nb_ville = 20
|
||||||
|
max_coords = 1000
|
||||||
|
nb_truck = 4
|
||||||
|
temperature = 10000
|
||||||
|
cooling_rate = 0.999
|
||||||
|
temperature_ok = 0.001
|
||||||
|
|
||||||
|
start_time_generate = time.time()
|
||||||
|
cities = generate_cities(nb_ville, max_coords)
|
||||||
|
cities[0] = [max_coords/2, max_coords/2]
|
||||||
|
stop_time_generate = time.time()
|
||||||
|
|
||||||
|
start_time_split = time.time()
|
||||||
|
clusters = split_tour_across_clusters(cities, nb_truck)
|
||||||
|
stop_time_split = time.time()
|
||||||
|
|
||||||
|
for cluster in clusters.values():
|
||||||
|
print(len(cluster))
|
||||||
|
print("\n---- TIME ----")
|
||||||
|
print("generate cities time: ", stop_time_generate - start_time_generate)
|
||||||
|
print("split cities time: ", stop_time_split - start_time_split)
|
||||||
|
|
||||||
|
# create new figure for annealing paths
|
||||||
|
plt.figure()
|
||||||
|
colors = [
|
||||||
|
'#1f77b4', # Bleu moyen
|
||||||
|
'#ff7f0e', # Orange
|
||||||
|
'#2ca02c', # Vert
|
||||||
|
'#d62728', # Rouge
|
||||||
|
'#9467bd', # Violet
|
||||||
|
'#8c564b', # Marron
|
||||||
|
'#e377c2', # Rose
|
||||||
|
'#7f7f7f', # Gris
|
||||||
|
'#bcbd22', # Vert olive
|
||||||
|
'#17becf', # Turquoise
|
||||||
|
'#1b9e77', # Vert Teal
|
||||||
|
'#d95f02', # Orange foncé
|
||||||
|
'#7570b3', # Violet moyen
|
||||||
|
'#e7298a', # Fuchsia
|
||||||
|
'#66a61e', # Vert pomme
|
||||||
|
'#e6ab02', # Jaune or
|
||||||
|
'#a6761d', # Bronze
|
||||||
|
'#666666', # Gris foncé
|
||||||
|
'#f781bf', # Rose clair
|
||||||
|
'#999999', # Gris moyen
|
||||||
|
]
|
||||||
|
|
||||||
|
best_routes = []
|
||||||
|
|
||||||
|
for i, cluster_indices in enumerate(clusters.values()):
|
||||||
|
# Sélection d'une couleur pour le cluster
|
||||||
|
color = colors[i % len(colors)]
|
||||||
|
|
||||||
|
# Récupération des coordonnées de la ville
|
||||||
|
cluster_cities = [cities[index] for index in cluster_indices]
|
||||||
|
|
||||||
|
# Appel de la fonction simulated_annealing
|
||||||
|
best_route = simulated_annealing(cluster_cities, temperature, cooling_rate, temperature_ok)
|
||||||
|
best_routes.append((best_route, color))
|
||||||
|
|
||||||
|
print("Final solution for cluster ", i, ":", best_route)
|
||||||
|
print("Total distance: ", total_distance(best_route))
|
||||||
|
|
||||||
|
for i, (route, color) in enumerate(best_routes):
|
||||||
|
x = [city[0] for city in route]
|
||||||
|
y = [city[1] for city in route]
|
||||||
|
x.append(x[0])
|
||||||
|
y.append(y[0])
|
||||||
|
plt.plot(x, y, color=color, marker='x', linestyle='-', label=f"Cluster {i}")
|
||||||
|
plt.legend(loc="best")
|
||||||
|
plt.show()
|
||||||
133
tests/04_cluster_ant_colony_no_animation.py
Normal file
133
tests/04_cluster_ant_colony_no_animation.py
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
from sklearn.cluster import KMeans
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
import random, time, math
|
||||||
|
from clustering import split_tour_across_clusters
|
||||||
|
|
||||||
|
random.seed(2)
|
||||||
|
|
||||||
|
def generate_cities(nb, max_coords=1000):
|
||||||
|
return [random.sample(range(max_coords), 2) for _ in range(nb)]
|
||||||
|
|
||||||
|
def distance(city1, city2):
|
||||||
|
return math.sqrt((city1[0] - city2[0]) ** 2 + (city1[1] - city2[1]) ** 2) + 1e-10
|
||||||
|
|
||||||
|
def total_distance(cities):
|
||||||
|
return sum([distance(cities[i - 1], cities[i]) for i in range(len(cities))])
|
||||||
|
|
||||||
|
class AntColony:
|
||||||
|
def __init__(self, cities, n_ants, alpha=1, beta=2, evaporation=0.5, intensification=2, max_time=0.1):
|
||||||
|
self.cities = cities
|
||||||
|
self.n = len(cities)
|
||||||
|
self.n_ants = n_ants
|
||||||
|
self.alpha = alpha
|
||||||
|
self.beta = beta
|
||||||
|
self.evaporation = evaporation
|
||||||
|
self.intensification = intensification
|
||||||
|
self.max_time = max_time
|
||||||
|
self.pheromones = [[1 / self.n for _ in range(self.n)] for __ in range(self.n)]
|
||||||
|
|
||||||
|
def choose_next_city(self, ant):
|
||||||
|
unvisited_cities = [i for i in range(self.n) if i not in ant]
|
||||||
|
probabilities = [self.pheromones[ant[-1]][i] ** self.alpha * ((1 / distance(self.cities[ant[-1]], self.cities[i])) ** self.beta) for i in unvisited_cities]
|
||||||
|
total = sum(probabilities)
|
||||||
|
if total == 0:
|
||||||
|
probabilities = [1 / len(unvisited_cities) for _ in unvisited_cities]
|
||||||
|
else:
|
||||||
|
probabilities = [p / total for p in probabilities]
|
||||||
|
return np.random.choice(unvisited_cities, p=probabilities)
|
||||||
|
|
||||||
|
def update_pheromones(self, ant):
|
||||||
|
pheromones_delta = self.intensification / total_distance([self.cities[i] for i in ant])
|
||||||
|
for i in range(len(ant) - 1):
|
||||||
|
self.pheromones[ant[i]][ant[i+1]] += pheromones_delta
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
best_ant = []
|
||||||
|
best_distance = float('inf')
|
||||||
|
start_time = time.time()
|
||||||
|
while time.time() - start_time < self.max_time:
|
||||||
|
ants = [[random.randint(0, self.n - 1)] for _ in range(self.n_ants)]
|
||||||
|
for ant in ants:
|
||||||
|
for _ in range(self.n - 1):
|
||||||
|
ant.append(self.choose_next_city(ant))
|
||||||
|
ant_distance = total_distance([self.cities[i] for i in ant])
|
||||||
|
if ant_distance < best_distance:
|
||||||
|
best_distance = ant_distance
|
||||||
|
best_ant = ant.copy()
|
||||||
|
self.update_pheromones(ant)
|
||||||
|
self.pheromones = [[(1 - self.evaporation) * p for p in row] for row in self.pheromones]
|
||||||
|
return [self.cities[i] for i in best_ant]
|
||||||
|
|
||||||
|
nb_ville = 200
|
||||||
|
max_coords = 1000
|
||||||
|
nb_truck = 4
|
||||||
|
max_time = 5
|
||||||
|
|
||||||
|
start_time_generate = time.time()
|
||||||
|
cities = generate_cities(nb_ville, max_coords)
|
||||||
|
cities[0] = [max_coords/2, max_coords/2]
|
||||||
|
stop_time_generate = time.time()
|
||||||
|
|
||||||
|
start_time_split = time.time()
|
||||||
|
clusters = split_tour_across_clusters(cities, nb_truck)
|
||||||
|
stop_time_split = time.time()
|
||||||
|
|
||||||
|
for cluster in clusters.values():
|
||||||
|
print(len(cluster))
|
||||||
|
print("\n---- TIME ----")
|
||||||
|
print("generate cities time: ", stop_time_generate - start_time_generate)
|
||||||
|
print("split cities time: ", stop_time_split - start_time_split)
|
||||||
|
|
||||||
|
# create new figure for annealing paths
|
||||||
|
plt.figure()
|
||||||
|
colors = [
|
||||||
|
'#1f77b4', # Bleu moyen
|
||||||
|
'#ff7f0e', # Orange
|
||||||
|
'#2ca02c', # Vert
|
||||||
|
'#d62728', # Rouge
|
||||||
|
'#9467bd', # Violet
|
||||||
|
'#8c564b', # Marron
|
||||||
|
'#e377c2', # Rose
|
||||||
|
'#7f7f7f', # Gris
|
||||||
|
'#bcbd22', # Vert olive
|
||||||
|
'#17becf', # Turquoise
|
||||||
|
'#1b9e77', # Vert Teal
|
||||||
|
'#d95f02', # Orange foncé
|
||||||
|
'#7570b3', # Violet moyen
|
||||||
|
'#e7298a', # Fuchsia
|
||||||
|
'#66a61e', # Vert pomme
|
||||||
|
'#e6ab02', # Jaune or
|
||||||
|
'#a6761d', # Bronze
|
||||||
|
'#666666', # Gris foncé
|
||||||
|
'#f781bf', # Rose clair
|
||||||
|
'#999999', # Gris moyen
|
||||||
|
]
|
||||||
|
|
||||||
|
best_routes = []
|
||||||
|
|
||||||
|
for i, cluster_indices in enumerate(clusters.values()):
|
||||||
|
# Sélection d'une couleur pour le cluster
|
||||||
|
color = colors[i % len(colors)]
|
||||||
|
|
||||||
|
# Récupération des coordonnées de la ville
|
||||||
|
cluster_cities = [cities[index] for index in cluster_indices]
|
||||||
|
|
||||||
|
# Appel de la fonction AntColony.run
|
||||||
|
ant_colony = AntColony(cluster_cities, n_ants=10, max_time=max_time)
|
||||||
|
best_route = ant_colony.run()
|
||||||
|
best_routes.append((best_route, color))
|
||||||
|
|
||||||
|
print("Final solution for cluster ", i, ":", best_route)
|
||||||
|
print("Total distance: ", total_distance(best_route))
|
||||||
|
|
||||||
|
for i, (route, color) in enumerate(best_routes):
|
||||||
|
x = [city[0] for city in route]
|
||||||
|
y = [city[1] for city in route]
|
||||||
|
x.append(x[0])
|
||||||
|
y.append(y[0])
|
||||||
|
plt.plot(x, y, color=color, marker='x', linestyle='-', label=f"Cluster {i}")
|
||||||
|
# add title with nb_ville, nb_truck and max_time
|
||||||
|
plt.title(f"nb_ville = {nb_ville}, nb_truck = {nb_truck}, max_time = {max_time}")
|
||||||
|
|
||||||
|
plt.show()
|
||||||
81
tests/clustering.py
Normal file
81
tests/clustering.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
from sklearn.cluster import KMeans
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
def split_tour_across_clusters(cities, nb_truck):
|
||||||
|
if nb_truck == 1:
|
||||||
|
return {0: list(range(len(cities)))}
|
||||||
|
|
||||||
|
# clustering initial
|
||||||
|
kmeans = KMeans(n_clusters=nb_truck, random_state=0).fit(cities)
|
||||||
|
clusters = {i:[] for i in range(nb_truck)}
|
||||||
|
|
||||||
|
# assignation des indices des villes aux clusters
|
||||||
|
for i, label in enumerate(kmeans.labels_):
|
||||||
|
clusters[label].append(i)
|
||||||
|
|
||||||
|
max_iterations = len(cities)**2
|
||||||
|
iteration = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
iteration += 1
|
||||||
|
if iteration > max_iterations:
|
||||||
|
print("Le nombre maximum d'itérations a été atteint. La boucle a été interrompue.")
|
||||||
|
break
|
||||||
|
# calcul des tailles de clusters
|
||||||
|
cluster_sizes = {i:len(clusters[i]) for i in range(nb_truck)}
|
||||||
|
|
||||||
|
# identification du cluster le plus grand et du plus petit
|
||||||
|
max_cluster = max(cluster_sizes, key=cluster_sizes.get)
|
||||||
|
min_cluster = min(cluster_sizes, key=cluster_sizes.get)
|
||||||
|
|
||||||
|
# s'il n'y a pas de grande disparité, on arrête la boucle
|
||||||
|
if cluster_sizes[max_cluster] - cluster_sizes[min_cluster] <= 1:
|
||||||
|
break
|
||||||
|
|
||||||
|
# calcul du centre de chaque cluster
|
||||||
|
cluster_centers = {i:np.mean([cities[index] for index in clusters[i]], axis=0) for i in range(nb_truck)}
|
||||||
|
|
||||||
|
# calcul des distances entre le centre du cluster le plus grand et les autres
|
||||||
|
distances = {i:np.linalg.norm(cluster_centers[max_cluster]-cluster_centers[i]) for i in range(nb_truck)}
|
||||||
|
del distances[max_cluster] # on supprime la distance vers lui-même
|
||||||
|
|
||||||
|
if nb_truck >= 3:
|
||||||
|
# on identifie les 2 clusters les plus proches
|
||||||
|
closest_clusters = sorted(distances, key=distances.get)[:2]
|
||||||
|
|
||||||
|
|
||||||
|
# parmi les deux clusters les plus proches, on choisit le plus petit
|
||||||
|
if cluster_sizes[closest_clusters[0]] <= cluster_sizes[closest_clusters[1]]:
|
||||||
|
target_cluster = closest_clusters[0]
|
||||||
|
else:
|
||||||
|
target_cluster = closest_clusters[1]
|
||||||
|
else:
|
||||||
|
closest_clusters = sorted(distances, key=distances.get)[:1]
|
||||||
|
target_cluster = closest_clusters[0]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# si le transfert va créer une plus grande disparité, on arrête la boucle
|
||||||
|
if cluster_sizes[target_cluster] >= cluster_sizes[max_cluster]:
|
||||||
|
break
|
||||||
|
|
||||||
|
# calcul des distances entre le centre du cluster cible et les villes du cluster le plus grand
|
||||||
|
distances_to_target = {index:np.linalg.norm(cluster_centers[target_cluster]-cities[index])
|
||||||
|
for index in clusters[max_cluster]}
|
||||||
|
|
||||||
|
# on identifie la ville la plus proche du centre du cluster cible
|
||||||
|
closest_city_index = min(distances_to_target, key=distances_to_target.get)
|
||||||
|
|
||||||
|
# on transfère la ville du cluster le plus grand au cluster cible
|
||||||
|
clusters[target_cluster].append(closest_city_index)
|
||||||
|
clusters[max_cluster].remove(closest_city_index)
|
||||||
|
|
||||||
|
# Ajout du point de départ et d'arrivée pour chaque cluster
|
||||||
|
depot_index = 0
|
||||||
|
for cluster in clusters.values():
|
||||||
|
if cluster[0] != depot_index:
|
||||||
|
cluster.insert(0, depot_index)
|
||||||
|
if cluster[-1] != depot_index:
|
||||||
|
cluster.append(depot_index)
|
||||||
|
|
||||||
|
return clusters
|
||||||
1
tests/data_sample/15_cities_minimum_293.txt
Normal file
1
tests/data_sample/15_cities_minimum_293.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
[[-0.0, 0.0], [-21.5, -7.3], [-28.9, -0.0], [-43.1, -14.6], [-50.5, -7.4], [-64.7, -21.9], [-72.1, -0.2], [-79.3, 21.4], [-65.1, 36.1], [-57.6, 43.3], [-50.6, 21.6], [-36.0, 21.6], [-29.1, 43.2], [-14.7, 43.4], [-0.1, 28.7], [-0.0, 0.0]]
|
||||||
3
tests/data_sample/48_cities_minimum_33523.txt
Normal file
3
tests/data_sample/48_cities_minimum_33523.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[[6734.0, 1453.0], [2233.0, 10.0], [5530.0, 1424.0], [401.0, 841.0], [3082.0, 1644.0], [7608.0, 4458.0], [7573.0, 3716.0], [7265.0, 1268.0], [6898.0, 1885.0], [1112.0, 2049.0], [5468.0, 2606.0], [5989.0, 2873.0], [4706.0, 2674.0], [4612.0, 2035.0], [6347.0, 2683.0], [6107.0, 669.0], [7611.0, 5184.0], [7462.0, 3590.0],
|
||||||
|
[7732.0, 4723.0], [5900.0, 3561.0], [4483.0, 3369.0], [6101.0, 1110.0], [5199.0, 2182.0], [1633.0, 2809.0], [4307.0, 2322.0], [675.0, 1006.0], [7555.0, 4819.0], [7541.0, 3981.0], [3177.0, 756.0], [7352.0, 4506.0], [7545.0, 2801.0], [3245.0, 3305.0], [6426.0, 3173.0], [4608.0, 1198.0], [23.0, 2216.0], [7248.0, 3779.0],
|
||||||
|
[7762.0, 4595.0], [7392.0, 2244.0], [3484.0, 2829.0], [6271.0, 2135.0], [4985.0, 140.0], [1916.0, 1569.0], [7280.0, 4899.0], [7509.0, 3239.0], [10.0, 2676.0], [6807.0, 2993.0], [5185.0, 3258.0], [3023.0, 1942.0]]
|
||||||
Loading…
x
Reference in New Issue
Block a user