2023-06-16 13:41:14 +02:00

81 lines
3.2 KiB
Python

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