1.1 MiB
Project Algorithmique avancée¶
Contexte du projet ¶
L'ADEME a lancé un appel pour promouvoir de nouvelles solutions de mobilité. CesiCDP, avec son équipe de 4 personnes, répond à cet appel en se concentrant sur la gestion des tournées de livraison. L'objectif est de minimiser la durée totale de la tournée en tenant compte des contraintes de trafic et autres. Un financement attractif est en jeu pour développer l'activité de CesiCDP. L'ajout de contraintes supplémentaires au problème rendra l'étude plus réaliste et plus difficile à résoudre, mais démontrera l'expertise de l'équipe.
Notre objectif est de développer un algorithme qui nous permettra de passer par tous les points de livraison dans un temps optimisé.
Contraintes choisies ¶
La contrainte que nous avons choisie est la suivante :
- Avoir plusieurs camions disponibles simultanément pour effectuer les livraisons.
Formulation du problème ¶
Considérons un graphe G=(V,E), où V est l'ensemble des villes (ou points de livraison) et E est l'ensemble des routes entre les villes. Il y a k camions disponibles pour effectuer les livraisons.
Le problème consiste à trouver un itinéraire pour chaque camion, de sorte que toutes les livraisons soient effectuées dans le temps le plus court possible.
Le problème que nous avons avec les contraintes mentionnées ci-dessus est le VRP (Problème de Routage des Véhicules).
Contraintes du problème ¶
Liste des contraintes du problème :
- Tous les clients doivent être servis
- Un client ne peut être servi que par un seul véhicule.
- En quittant un client, un véhicule ne peut se rendre que chez un seul autre client.
Nous allons donc affecter chaque client à une route desservie par un seul véhicule.
Démonstration de la complexité du problème de routage des véhicules (VRP) ¶
Introduction¶
Le Problème de Routage des Véhicules (VRP) est une extension du Problème du Voyageur de Commerce (TSP) et est connu pour être un problème NP-difficile. Nous allons démontrer la complexité du VRP en nous basant sur le problème de la chaîne Hamiltonienne, qui est connu pour être NP-complet.
Définitions préliminaires¶
Problème de la chaîne Hamiltonienne : le problème consiste à déterminer si un graphe non orienté donné possède une chaîne Hamiltonienne, c'est-à-dire un chemin qui visite chaque sommet exactement une fois.
Problème du voyageur de commerce (TSP) : le problème consiste à trouver le circuit le plus court possible qui visite chaque ville d'un ensemble donné de villes et revient à la ville d'origine.
Problème de routage des véhicules (VRP) : le problème consiste à livrer une série de clients avec plusieurs véhicules tout en minimisant le coût total, tel que le temps de livraison ou la distance parcourue.
Preuve de complexité du TSP¶
Le TSP (Traveling Salesman Problem) est une extension du problème de chaîne Hamiltonienne. En fait, un cas particulier du TSP est le problème de chaîne Hamiltonienne, dans lequel toutes les arêtes ont le même poids (ou coût). Dans ce cas, trouver le circuit le plus court dans le TSP revient à trouver une chaîne Hamiltonienne dans le graphe. Puisque le problème de chaîne Hamiltonienne est NP-complet, le TSP doit être au moins aussi difficile, donc le TSP est NP-complet.
Preuve de complexité du VRP¶
Le VRP (Vehicle Routing Problem) est une extension du TSP (Traveling Salesman Problem). En fait, un cas particulier du VRP est le TSP où il n'y a qu'un seul véhicule disponible pour effectuer les livraisons. Dans ce cas, trouver la solution au VRP revient à trouver la solution au TSP.
Puisque le TSP est NP-complet, le VRP doit être au moins aussi difficile, donc le VRP est NP-difficile. De plus, le VRP introduit des contraintes supplémentaires, telles que la présence de plusieurs véhicules et éventuellement des capacités de véhicule et des fenêtres de temps, ce qui le rend encore plus complexe.
Conclusion¶
En conclusion, nous avons démontré que le problème de VRP (Vehicle Routing Problem) est un problème NP-difficile en le réduisant au problème TSP (Traveling Salesman Problem) NP-complet, qui à son tour peut être réduit au problème de chaîne Hamiltonienne NP-complet. Cette démonstration met en évidence la difficulté de résoudre le VRP, en particulier pour de grandes instances. En pratique, des méthodes (méta)heuristiques et approximatives sont souvent utilisées pour résoudre le VRP, comme nous le verrons plus tard.
Modélisation mathématique du VRP ¶
Paramètres¶
$V=\{0,1,2,...,n_v\}$ : l'ensemble des villes, où 0 est la base (ou dépôt), $1,2,...,n_v$ sont les villes de livraison. $n_v+1$ sera le dépôt pour le retour.
$K=\{1,2,...,k\}$ : tous les camions.
$E$ représente les arcs entre deux clients $i,j \in V$
$G=(V,E)$ : le graphe avec V comme sommets (villes) et E comme arêtes
$d_{ij}$ : distance (ou temps de voyage) de la ville i à la ville $j$ (coût de voyage)
Variables de décision¶
$x_{ijk}$ : variable binaire qui vaut 1 si le camion $k$ va d'une ville $i$ à une ville $j$, sinon elle vaut 0.
Fonction objective¶
$$\min \sum_{k∈K} \sum_{i∈V} \sum_{j∈V} d_{ij} x_{ijk} $$
Contraintes du VRP¶
Chaque ville est visitée qu'une et une seule fois : $$\sum_{k \in K} \sum_{j \in V} x_{ijk} = 1, \forall i \in V, i \ne 0$$
Si un camion arrive à une ville il doit en partir : $$\sum_{i \in V} x_{ijk} = \sum_{j \in V} x_{ijk}, \forall k \in K, \forall i \in V, \forall j \in V $$
Contrainte d'élimination des sous-tours (pour s'assurer que chaque camion effectue un tour complet) $$\sum_{i \in S, j \notin S} x_{ijk} \geq 1, \forall k \in K, \forall \; sous-ensemble \; S \; de \; V, 0 \in S, S \ne V $$
Modélisation mathématique du VRPTW ¶
Note: Les éléments suivants viennent en addition à la modélisation mathématique du VRP.
Variable de décision¶
$[a_i, b_i]$ la fenêtre de temps pour le client $i$
$T_{ik}$ le moment auquel le véhicule $k$ arrive au client $i$
**Contrainte de TW **¶
$T_{ik}$ est le moment auquel le véhicule $k$ arrive au client $i$.
$d_{ij}$ est le temps nécessaire pour se rendre du client $i$ au client $j$.
$T_{jk}$ est le moment auquel le véhicule $k$ arrive au client $j$.
$M$ est une grande constante
- Les fenêtres de temps doivent être respectées :
$$ a_j \leq T_{jk} \leq b_j,∀k∈ K ,∀j∈V$$
- Les temps d'arrivé aux différentes villes pour chaque camion doivent être cohérent :
$$ T_{ik} + d_{ij} \leq T_{jk} + M(1-x_{ijk}), ∀i, j ∈ V, ∀k∈ K $$
Algorithme de résolution¶
Dans le cadre de notre projet, nous avons envisagé d'adopter trois algorithmes heuristiques distincts pour résoudre notre problème d'optimisation de trajet entre les villes. Notre objectif est de les comparer afin de déterminer celui qui nous fournira l'itinéraire le plus proche de l'optimal. Les trois algorithmes que nous allons mettre en œuvre sont les suivants :
- L'algorithme des colonies de fourmis
- L'algorithme du recuit simulé
- L'algorithme des essaims particulaires
Nous examinerons ensuite leur mise en œuvre et leur fonctionnement pour évaluer leur performance.
Algorithme par colonies de fourmis¶
L'algorithme par colonies de fourmis est une approche inspirée du comportement des fourmis dans la recherche de chemins optimaux. Cet algorithme permet de résoudre des problèmes d'optimisation de manière efficace.
# Variables to edit: nb_ville = 100 max_coords = 1000 nb_truck = 4 max_time = 4 nb_ants = 10 alpha = 1 beta = 6 evaporation = 0.5 intensification = 2
# Import necessary libraries import matplotlib.pyplot as plt import random, time from tests.libs.clustering import split_tour_across_clusters from tests.libs.aco import AntColony, total_distance # Function to generate city coordinates def generate_cities(nb, max_coords=1000): # Generate a list of nb cities, each with random coordinates within the range of max_coords return [random.sample(range(max_coords), 2) for _ in range(nb)] # Calculate maximum time per cluster max_time_per_cluster = max_time / nb_truck # Track the time taken to generate cities start_time_generate = time.time() # Generate cities cities = generate_cities(nb_ville, max_coords) # Set the first city's coordinates at the center cities[0] = [max_coords/2, max_coords/2] stop_time_generate = time.time() # Track the time taken to split cities across clusters start_time_split = time.time() # Split cities into clusters clusters = split_tour_across_clusters(cities, nb_truck) stop_time_split = time.time() # Print out the time taken for each process print("\n---- TIME ----") print("Generate cities time: ", stop_time_generate - start_time_generate) print("Split cities time: ", stop_time_split - start_time_split) # Initialize a new figure for displaying paths plt.figure() plt.title("Ant Colony Optimization") # List of colors for plotting 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 ] # Initialize a list for storing the best routes best_routes = [] print("\n---- CLUSTER ----") # Iterate through each cluster for i, cluster_indices in enumerate(clusters.values()): # Select a color for the cluster color = colors[i % len(colors)] # Retrieve city coordinates for the cluster cluster_cities = [cities[index] for index in cluster_indices] # Run the AntColony algorithm and store the best route ant_colony = AntColony(cluster_cities, n_ants=nb_ants, max_time=max_time_per_cluster, alpha=alpha, beta=beta, evaporation=evaporation, intensification=intensification) best_route = ant_colony.run() best_routes.append((best_route, color)) # Print the total distance for the cluster print("Total distance for cluster {} ({} cities) : {}".format(i+1, len(cluster_cities), total_distance(best_route))) # Calculate and print total distance for all clusters full_total_distance = 0 for route, color in best_routes: full_total_distance += total_distance(route) print("Total distance for all clusters: ", full_total_distance) # Plot each route for i, (route, color) in enumerate(best_routes): x = [city[0] for city in route] y = [city[1] for city in route] # Close the loop of the route x.append(x[0]) y.append(y[0]) # Plot the route plt.plot(x, y, color=color, marker='o', linestyle='-', label=f"Cluster {i}") # Display the plot plt.show()
---- TIME ---- Generate cities time: 0.0 Split cities time: 0.14944958686828613 ---- CLUSTER ---- Total distance for cluster 1 (27 cities) : 2164.794990827718 Total distance for cluster 2 (27 cities) : 2122.813485403375 Total distance for cluster 3 (26 cities) : 2278.707718333369 Total distance for cluster 4 (27 cities) : 2599.5739910855473 Total distance for all clusters: 9165.890185650009
Algorithme du recuit simulé¶
L'algorithme du recuit simulé est une technique d'optimisation inspirée du processus de recuit utilisé dans la métallurgie. Il permet d'explorer de manière probabiliste l'espace des solutions en acceptant occasionnellement des mouvements qui peuvent initialement sembler moins favorables. Cette approche permet de trouver des solutions globales et d'éviter de rester bloqué dans des optima locaux.
# Variables to edit: nb_ville = 100 max_coords = 1000 nb_truck = 4 temperature = 10000 cooling_rate = 0.9999 temperature_ok = 0.001
# Import necessary libraries import matplotlib.pyplot as plt import random, time from tests.libs.clustering import split_tour_across_clusters from tests.libs.simulated_annealing import SimulatedAnnealing, total_distance # Function to generate city coordinates def generate_cities(nb, max_coords=1000): # Generate a list of nb cities, each with random coordinates within the range of max_coords return [random.sample(range(max_coords), 2) for _ in range(nb)] # Track the time taken to generate cities start_time_generate = time.time() # Generate cities cities = generate_cities(nb_ville, max_coords) # Set the first city's coordinates at the center cities[0] = [max_coords/2, max_coords/2] stop_time_generate = time.time() # Track the time taken to split cities across clusters start_time_split = time.time() # Split cities into clusters clusters = split_tour_across_clusters(cities, nb_truck) stop_time_split = time.time() # Print out the time taken for each process print("\n---- TIME ----") print("Generate cities time: ", stop_time_generate - start_time_generate) print("Split cities time: ", stop_time_split - start_time_split) # Initialize a new figure for displaying paths plt.figure() plt.title("Simulated Annealing") # List of colors for plotting 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 ] # Initialize a list for storing the best routes best_routes = [] # Iterate through each cluster for i, cluster_indices in enumerate(clusters.values()): # Select a color for the cluster color = colors[i % len(colors)] # Retrieve city coordinates for the cluster cluster_cities = [cities[index] for index in cluster_indices] # Run the SimulatedAnnealing algorithm and store the best route simulated_annealing = SimulatedAnnealing(cluster_cities, temperature=10000, cooling_rate=0.999, temperature_ok=0.01) best_route = simulated_annealing.run() best_routes.append((best_route, color)) # Print the total distance for the cluster print("Total distance for cluster {} ({} cities) : {}".format(i+1, len(cluster_cities), total_distance(best_route))) # Calculate and print total distance for all clusters full_total_distance = 0 for route, color in best_routes: full_total_distance += total_distance(route) print("Total distance for all clusters: ", full_total_distance) # Plot each route for i, (route, color) in enumerate(best_routes): x = [city[0] for city in route] y = [city[1] for city in route] # Close the loop of the route x.append(x[0]) y.append(y[0]) # Plot the route plt.plot(x, y, color=color, marker='o', linestyle='-', label=f"Cluster {i}") # Add legend to the plot plt.legend(loc="best") # Display the plot plt.show()
---- TIME ---- Generate cities time: 0.0009992122650146484 Split cities time: 0.05595564842224121 Total distance for cluster 1 (27 cities) : 2812.9786606142725 Total distance for cluster 2 (27 cities) : 2870.0631219087168 Total distance for cluster 3 (27 cities) : 2660.992000654059 Total distance for cluster 4 (27 cities) : 3193.529103806893 Total distance for all clusters: 11537.562886983942
Algorithme par essaims particulaires¶
L'algorithme des essaims particulaires est une méthode d'optimisation basée sur le comportement collectif des essaims d'animaux, tels que les oiseaux ou les poissons. Il utilise des particules qui se déplacent dans l'espace de recherche et communiquent entre elles pour trouver des solutions optimales. Cet algorithme est efficace pour explorer l'espace des solutions et trouver des optima locaux et globaux.
# Variables to edit: num_cities = 50 num_trucks = 5 num_particles = 50 max_iterations = 300 inertia_weight = 2 cognitive_weight = 1 social_weight = 1.5 incompleteness_probability = 0.8 start_time = 8 end_time = 18 infinite_distance_value = 1e6
# Importing necessary libraries import random import numpy as np import networkx as nx import matplotlib.pyplot as plt import matplotlib from IPython.display import display, Markdown import matplotlib.cm as cm from tests.libs.pso import Particle # Function to generate a matrix of distances between cities def generate_distance_matrix(num_cities, incompleteness_probability): # Initialize an empty matrix with zeros distances = np.zeros((num_cities, num_cities)) # Iterate over each city for i in range(num_cities): # For each city, iterate over every other city to calculate distance for j in range(i+1, num_cities): # With certain probability, some distances will be missing (infinite) if random.random() < incompleteness_probability: distances[i, j] = float('inf') distances[j, i] = float('inf') else: # Assign random integer value to the distances between i and j distances[i, j] = random.randint(1, 10) distances[j, i] = distances[i, j] return distances # Function to generate random time windows for each city def generate_time_windows(num_cities, start_time, end_time): time_windows = [] # For each city, generate a random start and end time for _ in range(num_cities): start = random.randint(start_time, end_time - 1) end = random.randint(start + 1, end_time) time_windows.append((start, end)) return time_windows # Function to generate a graph representation of the cities and the distances between them def generate_city_graph(distances, time_windows): num_cities = distances.shape[0] # Create an empty graph G = nx.Graph() # Add cities as nodes with their corresponding time windows for i in range(num_cities): G.add_node(i+1, time_window=time_windows[i]) # Add edges between cities with their corresponding distances for u in range(num_cities): for v in range(u+1, num_cities): if distances[u, v] != float('inf'): G.add_edge(u+1, v+1, distance=distances[u, v]) return G # Generate the distances and time windows distances = generate_distance_matrix(num_cities, incompleteness_probability) time_windows = generate_time_windows(num_cities, start_time, end_time) # Generate the city graph G = generate_city_graph(distances, time_windows) # Initialize the particles and the global best position and cost particles = [] global_best_position = np.random.permutation(range(1, num_cities+1)) global_best_cost = float('inf') for _ in range(num_particles): particle = Particle(num_cities, num_trucks, distances, time_windows, infinite_distance_value) particles.append(particle) particle_cost = particle.evaluate_cost() if particle_cost < global_best_cost: global_best_cost = particle_cost global_best_position = particle.position.copy() # Main loop for the particle swarm optimization algorithm iteration = 0 best_costs = [] while iteration < max_iterations: for particle in particles: particle.update_velocity(global_best_position, inertia_weight, cognitive_weight, social_weight) particle.update_position() particle_cost = particle.evaluate_cost() if particle_cost < global_best_cost: global_best_cost = particle_cost global_best_position = particle.position.copy() best_costs.append(global_best_cost) iteration += 1 # Display the best solution found best_position_str = ", ".join(str(node) for node in global_best_position) best_cost_str = str(global_best_cost) markdown_text = f"### Best solution found:\n\n- **City Positions**: {best_position_str}\n- **Total Cost**: {best_cost_str}" display(Markdown(markdown_text)) # ---------------- Plot the best cost for each iteration ---------------------------------------------------------------------- # Compute the layout positions for the nodes using the spring layout algorithm pos = nx.spring_layout(G) # Create labels for each node in the graph labels = {node: str(node) for node in G.nodes()} # Draw the nodes of the graph using the computed positions nx.draw_networkx_nodes(G, pos) # Draw the edges of the graph using the computed positions nx.draw_networkx_edges(G, pos) # Draw the labels for the nodes using the computed positions and labels nx.draw_networkx_labels(G, pos, labels) # Set the title of the plot plt.title("Graph of cities with time windows") # Turn off the axis display plt.axis("off") # Display the plot plt.show() # ---------------- Plot Truck Paths ---------------------------------------------------------------------- # Initialize an empty list for truck paths truck_paths = [] # Create a colormap for the trucks cmap = matplotlib.colormaps['gist_ncar'] colors = [cmap(i/num_trucks) for i in range(num_trucks)] # Split the global best position into separate paths for each truck for truck in range(num_trucks): path = [] for i in range(len(global_best_position)): if i % num_trucks == truck: city = global_best_position[i] path.append(int(city)) path.append(path[0]) # Making the path circular by adding the first city to the end of the path truck_paths.append(path) # Plot the truck paths on a graph plt.figure(figsize=(10, 6)) plt.title("Truck Paths") plt.axis("off") for i in range(num_trucks): path = truck_paths[i] path_str = " -> ".join(str(city) for city in path) text = f"Truck {i+1}: {path_str}" display(Markdown(text)) edges = [(path[j], path[j+1]) for j in range(len(path)-1)] nx.draw_networkx_edges(G, pos, edgelist=edges, edge_color=colors[i], label=f"Truck {i+1}") nx.draw_networkx_labels(G, pos, labels) plt.legend() plt.show() # ---------------- Plot path cost for Each Truck ---------------------------------------------------------------------- # Initialize an empty list for the total cost of each path path_costs = [] # Calculate the total cost of each path for path in truck_paths: path_cost = 0 for i in range(len(path) - 1): u, v = path[i] - 1, path[i + 1] - 1 # Adjust the indices # If the distance is infinite, use a replacement value. distance = distances[u, v] if distances[u, v] != float('inf') else infinite_distance_value path_cost += distance path_costs.append(path_cost) # Plot a histogram of the path costs plt.figure(figsize=(10, 6)) plt.bar(range(1, num_trucks + 1), path_costs, color=cm.rainbow(np.linspace(0, 1, num_trucks))) plt.xlabel("Truck") plt.ylabel("Path Cost") plt.title("Path Cost for Each Truck") plt.show() # ---------------- Plot Evolution of the Best Cost Over Iterations ---------------------------------------------------------------------- # Plot a graph of the evolution of the best cost plt.figure(figsize=(10, 6)) plt.plot(range(1, max_iterations + 1), best_costs) plt.title("Evolution of the Best Cost Over Iterations") plt.xlabel("Iteration") plt.ylabel("Best Cost") plt.grid(True) plt.show()
Best solution found:¶
- City Positions: 31, 27, 40, 38, 2, 7, 34, 46, 42, 4, 32, 47, 18, 30, 44, 5, 20, 33, 1, 36, 43, 10, 19, 6, 11, 21, 13, 37, 16, 23, 25, 17, 22, 28, 29, 49, 50, 26, 8, 35, 48, 3, 24, 45, 14, 15, 9, 39, 12, 41
- Total Cost: 96000856.0
Truck 1: 31 -> 7 -> 32 -> 5 -> 43 -> 21 -> 25 -> 49 -> 48 -> 15 -> 31
Truck 2: 27 -> 34 -> 47 -> 20 -> 10 -> 13 -> 17 -> 50 -> 3 -> 9 -> 27
Truck 3: 40 -> 46 -> 18 -> 33 -> 19 -> 37 -> 22 -> 26 -> 24 -> 39 -> 40
Truck 4: 38 -> 42 -> 30 -> 1 -> 6 -> 16 -> 28 -> 8 -> 45 -> 12 -> 38
Truck 5: 2 -> 4 -> 44 -> 36 -> 11 -> 23 -> 29 -> 35 -> 14 -> 41 -> 2
Plan d'expérience et statistiques¶
L'objectif du plan d'expérience est de comparer différentes configurations et paramètres des algorithmes afin d'optimiser leurs performances. Cela nous permettra d'identifier les réglages les plus efficaces pour obtenir un itinéraire proche de l'optimal.
Algorithme par colonies de fourmis (avec suite de valeurs : ATT48)¶
D'après de nombreuses recherches sur internet, les valeurs optimales des paramètres de l'algorithme des colonies de fourmis sont les suivantes :
- Nombre de fourmis (nb_ants) : 10
- Facteur d'importance de la phéromone (alpha) : 1
- Facteur d'importance de la distance (beta) : 4
- Facteur d'évaporation (evaporation) : 0.5
- Facteur de dépot de phéromones (intensification) : 2
A l'aide des scripts Python suivants, nous pouvons tester pour une configuration précise et pour chaque limite de temps (max_times), la variation des résultats en fonction des valeurs de Alpha et Beta
Note: Pour la réalisation de nos tests, nous avons utilisé la suite de valeur ATT48, donc l'optimal est connu : 33523
Identification de la valeur optimale de Alpha¶
# Variables to edit: max_times = [0.1, 0.2, 0.5, 1, 2] alphas = [1, 2, 4, 6] n_ants = 10 beta = 4 evaporation = 0.5 intensification = 2 n_runs = 2 # Number of times each configuration will be run to obtain an average
import matplotlib.pyplot as plt import numpy as np from tests.libs.clustering import split_tour_across_clusters from tests.libs.aco import AntColony, total_distance cities = [[6734, 1453], [2233, 10], [5530, 1424], [401, 841], [3082, 1644], [7608, 4458], [7573, 3716], [7265, 1268], [6898, 1885], [1112, 2049], [5468, 2606], [5989, 2873], [4706, 2674], [4612, 2035], [6347, 2683], [6107, 669], [7611, 5184], [7462, 3590], [7732, 4723], [5900, 3561], [4483, 3369], [6101, 1110], [5199, 2182], [1633, 2809], [4307, 2322], [675, 1006], [7555, 4819], [7541, 3981], [3177, 756], [7352, 4506], [7545, 2801], [3245, 3305], [6426, 3173], [4608, 1198], [23, 2216], [7248, 3779], [7762, 4595], [7392, 2244], [3484, 2829], [6271, 2135], [4985, 140], [1916, 1569], [7280, 4899], [7509, 3239], [10, 2676], [6807, 2993], [5185, 3258], [3023, 1942]] optimal = 33523 average_best_route_per_max_time_per_alpha = [] def calculer_temps_total(max_times, alphas, n_runs): temps_total = 0 for max_time in max_times: for alpha in alphas: temps_total += max_time * n_runs return temps_total print("The script will take at least : {} seconds".format(calculer_temps_total(max_times, alphas, n_runs))) for alpha in alphas: average_best_route_per_max_time = [] for max_time in max_times: total_best_route_length = 0 for _ in range(n_runs): clusters = split_tour_across_clusters(cities, 1) total_distance_for_run = 0 for i, cluster_indices in enumerate(clusters.values()): cluster_cities = [cities[index] for index in cluster_indices] ant_colony = AntColony(cluster_cities, n_ants=n_ants, max_time=max_time, alpha=alpha, beta=beta, evaporation=evaporation, intensification=intensification) best_route = ant_colony.run() total_distance_for_run += total_distance(best_route) total_best_route_length += total_distance_for_run average_best_route_length = total_best_route_length / n_runs average_best_route_per_max_time.append(average_best_route_length) average_best_route_per_max_time_per_alpha.append(average_best_route_per_max_time) colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k'] total_bars_per_group = len(alphas) total_groups = len(max_times) unit_space_per_group = 1 bars_space_per_group = total_bars_per_group / (total_bars_per_group + 1) bar_width = bars_space_per_group / total_bars_per_group bar_positions = np.arange(total_groups) plt.figure(figsize=(10, 7)) for i, average_best_route_per_max_time in enumerate(average_best_route_per_max_time_per_alpha): plt.bar(bar_positions + i * bar_width, average_best_route_per_max_time, width=bar_width, color=colors[i], label=f'Alpha={alphas[i]}') for j, v in enumerate(average_best_route_per_max_time): plt.text(j + i * bar_width, v + 0.01 + (i*0.2), int(v), va='bottom', ha='center') plt.axhline(y=optimal, color='r') plt.xticks(bar_positions + bar_width / 2, max_times) # Set the x-axis labels to be the max_time values plt.xlabel('Max time') plt.ylabel('Average best route length') plt.legend() title = "" title += "Average best route length ({} iterations) for different max times\n".format(n_runs) title += "Nb cities: " + str(len(cities)) + " / " title += "Ants: " + str(n_ants) + " / " title += "Alpha: " + str(alphas) + " / " title += "Beta: " + str(beta) + " / " title += "Evaporation: " + str(evaporation) + " / " title += "Intensification: " + str(intensification) plt.title(title) plt.show()
The script will take at least : 30.4 seconds
Grâce à cet histogramme, nous pouvons constater que la meilleure valeur pour alpha est 1.
Identification de la valeur optimale de Beta¶
# Variables to edit: max_times = [0.1, 0.2, 0.5, 1, 2] betas = [2, 4, 6, 8] n_ants = 10 alpha = 1 evaporation = 0.5 intensification = 2 n_runs = 2 # Number of times each configuration will be run to obtain an average
import matplotlib.pyplot as plt from tests.libs.clustering import split_tour_across_clusters from tests.libs.aco import AntColony, total_distance import numpy as np cities = [[6734, 1453], [2233, 10], [5530, 1424], [401, 841], [3082, 1644], [7608, 4458], [7573, 3716], [7265, 1268], [6898, 1885], [1112, 2049], [5468, 2606], [5989, 2873], [4706, 2674], [4612, 2035], [6347, 2683], [6107, 669], [7611, 5184], [7462, 3590], [7732, 4723], [5900, 3561], [4483, 3369], [6101, 1110], [5199, 2182], [1633, 2809], [4307, 2322], [675, 1006], [7555, 4819], [7541, 3981], [3177, 756], [7352, 4506], [7545, 2801], [3245, 3305], [6426, 3173], [4608, 1198], [23, 2216], [7248, 3779], [7762, 4595], [7392, 2244], [3484, 2829], [6271, 2135], [4985, 140], [1916, 1569], [7280, 4899], [7509, 3239], [10, 2676], [6807, 2993], [5185, 3258], [3023, 1942]] optimal = 33523 average_best_route_per_max_time_per_beta = [] def calculer_temps_total(max_times, betas, n_runs): temps_total = 0 for max_time in max_times: for beta in betas: temps_total += max_time * n_runs return temps_total print("The script will take at least : {} seconds".format(calculer_temps_total(max_times, betas, n_runs))) for beta in betas: average_best_route_per_max_time = [] for max_time in max_times: total_best_route_length = 0 for _ in range(n_runs): clusters = split_tour_across_clusters(cities, 1) total_distance_for_run = 0 for i, cluster_indices in enumerate(clusters.values()): cluster_cities = [cities[index] for index in cluster_indices] ant_colony = AntColony(cluster_cities, n_ants=n_ants, max_time=max_time, alpha=alpha, beta=beta, evaporation=evaporation, intensification=intensification) best_route = ant_colony.run() total_distance_for_run += total_distance(best_route) total_best_route_length += total_distance_for_run average_best_route_length = total_best_route_length / n_runs average_best_route_per_max_time.append(average_best_route_length) average_best_route_per_max_time_per_beta.append(average_best_route_per_max_time) # Maintenant, nous avons toutes les valeurs moyennes, créons un histogramme colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k'] total_bars_per_group = len(betas) total_groups = len(max_times) # Unité d'espace pour chaque groupe de barres (incluant l'espace entre les groupes) unit_space_per_group = 1 # Espace qui serait occupé par les barres dans chaque groupe bars_space_per_group = total_bars_per_group / (total_bars_per_group + 1) # La largeur de chaque barre serait bar_width = bars_space_per_group / total_bars_per_group index = np.arange(len(max_times)) plt.figure() for i, average_best_route_per_max_time in enumerate(average_best_route_per_max_time_per_beta): plt.bar(index + i * bar_width, average_best_route_per_max_time, bar_width, color=colors[i], label='Beta = '+str(betas[i])) for j, v in enumerate(average_best_route_per_max_time): plt.text(j + i * bar_width, v + 0.01 + (i*0.2), int(v), va='bottom', ha='center') plt.axhline(y=optimal, color='r') plt.xlabel('Max time') plt.ylabel('Average best route length') title = "" title += "Comparaison of betas with average best route length ({} iterations) for different max times and each betas\n".format(n_runs) title += "Nb cities: " + str(len(cities)) + " / " title += "Ants: " + str(n_ants) + " / " title += "Alpha: " + str(alpha) + " / " title += "Evaporation: " + str(evaporation) + " / " title += "Intensification: " + str(intensification) plt.title(title) plt.xticks(index + bar_width / 2, max_times) plt.legend() plt.tight_layout() plt.show()
The script will take at least : 30.4 seconds
Grâce à cet histogramme, nous pouvons constater que la meilleure valeur pour beta est 4 ou 6 voire 8.
Résultats pour les valeurs optimales de Alpha (1) et Beta (4) pour chaque temps maximum¶
# Variables to edit: max_times = [0.01, 0.1, 0.2, 0.5, 1, 2, 5, 10] n_ants = 10 alpha = 1 beta = 4 evaporation = 0.5 intensification = 2 n_runs = 3 # Number of times each configuration will be run to obtain an average
import matplotlib.pyplot as plt from tests.libs.clustering import split_tour_across_clusters from tests.libs.aco import AntColony, total_distance cities = [[6734, 1453], [2233, 10], [5530, 1424], [401, 841], [3082, 1644], [7608, 4458], [7573, 3716], [7265, 1268], [6898, 1885], [1112, 2049], [5468, 2606], [5989, 2873], [4706, 2674], [4612, 2035], [6347, 2683], [6107, 669], [7611, 5184], [7462, 3590], [7732, 4723], [5900, 3561], [4483, 3369], [6101, 1110], [5199, 2182], [1633, 2809], [4307, 2322], [675, 1006], [7555, 4819], [7541, 3981], [3177, 756], [7352, 4506], [7545, 2801], [3245, 3305], [6426, 3173], [4608, 1198], [23, 2216], [7248, 3779], [7762, 4595], [7392, 2244], [3484, 2829], [6271, 2135], [4985, 140], [1916, 1569], [7280, 4899], [7509, 3239], [10, 2676], [6807, 2993], [5185, 3258], [3023, 1942]] optimal = 33523 average_best_route_per_max_time = [] def calculer_temps_total(max_times, n_runs): temps_total = 0 for max_time in max_times: temps_total += max_time * n_runs return temps_total print("The script will take at least : {} seconds".format(calculer_temps_total(max_times, n_runs))) for max_time in max_times: total_best_route_length = 0 for _ in range(n_runs): clusters = split_tour_across_clusters(cities, 1) total_distance_for_run = 0 for i, cluster_indices in enumerate(clusters.values()): cluster_cities = [cities[index] for index in cluster_indices] ant_colony = AntColony(cluster_cities, n_ants=n_ants, max_time=max_time, alpha=alpha, beta=beta, evaporation=evaporation, intensification=intensification) best_route = ant_colony.run() total_distance_for_run += total_distance(best_route) total_best_route_length += total_distance_for_run average_best_route_length = total_best_route_length / n_runs average_best_route_per_max_time.append(average_best_route_length) # Maintenant, nous avons toutes les valeurs moyennes, créons un histogramme 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 ] plt.figure() bar_width = 0.8 bar_positions = range(len(max_times)) # Crée une liste d'indices pour chaque barre plt.bar(bar_positions, average_best_route_per_max_time, width=bar_width, color=colors[:len(max_times)]) plt.axhline(y=optimal, color='r') # Ajouter des valeurs au-dessus des barres for i, v in enumerate(average_best_route_per_max_time): plt.text(i - 0.15, v + 0.01, round(v, 2)) plt.xticks(bar_positions, max_times) # Fixe les labels sur l'axe des x aux valeurs de max_time plt.xlabel('Max time') plt.ylabel('Average best route length') title = "" title += "Average best route length ({} iterations) for different max times\n".format(n_runs) title += "Nb cities: " + str(len(cities)) + " / " title += "Ants: " + str(n_ants) + " / " title += "Alpha: " + str(alpha) + " / " title += "Beta: " + str(beta) + " / " title += "Evaporation: " + str(evaporation) + " / " title += "Intensification: " + str(intensification) plt.title(title) plt.show()
The script will take at least : 56.43 seconds
Résultats pour les valeurs optimales de Alpha (1) et Beta (6) pour chaque temps maximum¶
# Variables to edit: max_times = [0.01, 0.1, 0.2, 0.5, 1, 2, 5, 10] n_ants = 10 alpha = 1 beta = 6 evaporation = 0.5 intensification = 2 n_runs = 3 # Number of times each configuration will be run to obtain an average
import matplotlib.pyplot as plt from tests.libs.clustering import split_tour_across_clusters from tests.libs.aco import AntColony, total_distance cities = [[6734, 1453], [2233, 10], [5530, 1424], [401, 841], [3082, 1644], [7608, 4458], [7573, 3716], [7265, 1268], [6898, 1885], [1112, 2049], [5468, 2606], [5989, 2873], [4706, 2674], [4612, 2035], [6347, 2683], [6107, 669], [7611, 5184], [7462, 3590], [7732, 4723], [5900, 3561], [4483, 3369], [6101, 1110], [5199, 2182], [1633, 2809], [4307, 2322], [675, 1006], [7555, 4819], [7541, 3981], [3177, 756], [7352, 4506], [7545, 2801], [3245, 3305], [6426, 3173], [4608, 1198], [23, 2216], [7248, 3779], [7762, 4595], [7392, 2244], [3484, 2829], [6271, 2135], [4985, 140], [1916, 1569], [7280, 4899], [7509, 3239], [10, 2676], [6807, 2993], [5185, 3258], [3023, 1942]] optimal = 33523 average_best_route_per_max_time = [] def calculer_temps_total(max_times, n_runs): temps_total = 0 for max_time in max_times: temps_total += max_time * n_runs return temps_total print("The script will take at least : {} seconds".format(calculer_temps_total(max_times, n_runs))) for max_time in max_times: total_best_route_length = 0 for _ in range(n_runs): clusters = split_tour_across_clusters(cities, 1) total_distance_for_run = 0 for i, cluster_indices in enumerate(clusters.values()): cluster_cities = [cities[index] for index in cluster_indices] ant_colony = AntColony(cluster_cities, n_ants=n_ants, max_time=max_time, alpha=alpha, beta=beta, evaporation=evaporation, intensification=intensification) best_route = ant_colony.run() total_distance_for_run += total_distance(best_route) total_best_route_length += total_distance_for_run average_best_route_length = total_best_route_length / n_runs average_best_route_per_max_time.append(average_best_route_length) # Maintenant, nous avons toutes les valeurs moyennes, créons un histogramme 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 ] plt.figure() bar_width = 0.8 bar_positions = range(len(max_times)) # Crée une liste d'indices pour chaque barre plt.bar(bar_positions, average_best_route_per_max_time, width=bar_width, color=colors[:len(max_times)]) plt.axhline(y=optimal, color='r') # Ajouter des valeurs au-dessus des barres for i, v in enumerate(average_best_route_per_max_time): plt.text(i - 0.15, v + 0.01, round(v, 2)) plt.xticks(bar_positions, max_times) # Fixe les labels sur l'axe des x aux valeurs de max_time plt.xlabel('Max time') plt.ylabel('Average best route length') title = "" title += "Average best route length ({} iterations) for different max times\n".format(n_runs) title += "Nb cities: " + str(len(cities)) + " / " title += "Ants: " + str(n_ants) + " / " title += "Alpha: " + str(alpha) + " / " title += "Beta: " + str(beta) + " / " title += "Evaporation: " + str(evaporation) + " / " title += "Intensification: " + str(intensification) plt.title(title) plt.show()
The script will take at least : 56.43 seconds
On peut conclure que, peu importe la liste de temps de calcul, on obtient à chaque fois de meilleur résultats pour Beta = 6.
Algorithme du recuit simulé¶
Comme l'algorithme par colonies de fourmis, d'après de nombreuses recherches sur internet, les valeurs optimales des paramètres de l'algorithme du recuit simulé sont les suivantes :
- Température initiale : 10000
- Taux de refroidissement : 0.995
- Température finale : 0.1
A l'aide des scripts Python suivants, nous pouvons tester pour une configuration précise et pour chaque limite de temps (max_times), la variation des résultats en fonction des valeurs de la température initiale et du taux de refroidissement.
Note: Pour la réalisation de nos tests, nous avons utilisé la suite de valeur ATT48, donc l'optimal est connu : 33523
Evolution de la distance en fonction du nombre d'itération pour plusieurs taux de refroidissement avec une température initiale de 10000¶
# Variables to edit: nb_ville = 100 max_coords = 1000 nb_truck = 1 temperature = 10000 cooling_rates = [0.999 , 0.99, 0.9 , 0.8] temperature_ok = 0.001
import matplotlib.pyplot as plt import random, time from tests.libs.clustering import split_tour_across_clusters from tests.libs.simulated_annealing_stats import SimulatedAnnealing, total_distance def generate_cities(nb, max_coords=1000): return [random.sample(range(max_coords), 2) for _ in range(nb)] cities = [[6734, 1453], [2233, 10], [5530, 1424], [401, 841], [3082, 1644], [7608, 4458], [7573, 3716], [7265, 1268], [6898, 1885], [1112, 2049], [5468, 2606], [5989, 2873], [4706, 2674], [4612, 2035], [6347, 2683], [6107, 669], [7611, 5184], [7462, 3590], [7732, 4723], [5900, 3561], [4483, 3369], [6101, 1110], [5199, 2182], [1633, 2809], [4307, 2322], [675, 1006], [7555, 4819], [7541, 3981], [3177, 756], [7352, 4506], [7545, 2801], [3245, 3305], [6426, 3173], [4608, 1198], [23, 2216], [7248, 3779], [7762, 4595], [7392, 2244], [3484, 2829], [6271, 2135], [4985, 140], [1916, 1569], [7280, 4899], [7509, 3239], [10, 2676], [6807, 2993], [5185, 3258], [3023, 1942]] cities[0] = [max_coords/2, max_coords/2] optimal = 33523 clusters = split_tour_across_clusters(cities, nb_truck) colors = [ '#1f77b4', # Bleu moyen '#ff7f0e', # Orange '#2ca02c', # Vert '#9467bd', # Violet '#d62728', # Rouge '#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 ] results = [] for cooling_rate in cooling_rates: distances_over_time = [] for i, cluster_indices in enumerate(clusters.values()): cluster_cities = [cities[index] for index in cluster_indices] simulated_annealing = SimulatedAnnealing(cluster_cities, temperature=10000, cooling_rate=cooling_rate, temperature_ok=0.01) best_route, distances = simulated_annealing.run() distances_over_time.extend(distances) # Record results for this cooling rate results.append({ 'cooling_rate': cooling_rate, 'distances': distances_over_time, }) # Plotting total distances for each cooling rate over time plt.figure() for result in results: plt.plot(result['distances'], label=f'Cooling rate: {result["cooling_rate"]}') plt.xlabel('Iteration') plt.ylabel('Total distance') plt.legend(loc='upper right') plt.title('Total distance over iterations for different cooling rates') plt.axhline(y=optimal, color='black', linestyle='--') plt.show()
Evolution de la distance en fonction du nombre d'itération pour plusieurs taux de refroidissement avec une température initiale de 20000¶
# Variables to edit: nb_ville = 100 max_coords = 1000 nb_truck = 1 temperature = 20000 cooling_rates = [0.999 , 0.99, 0.9 , 0.8] temperature_ok = 0.001
import matplotlib.pyplot as plt import random, time from tests.libs.clustering import split_tour_across_clusters from tests.libs.simulated_annealing_stats import SimulatedAnnealing, total_distance def generate_cities(nb, max_coords=1000): return [random.sample(range(max_coords), 2) for _ in range(nb)] cities = [[6734, 1453], [2233, 10], [5530, 1424], [401, 841], [3082, 1644], [7608, 4458], [7573, 3716], [7265, 1268], [6898, 1885], [1112, 2049], [5468, 2606], [5989, 2873], [4706, 2674], [4612, 2035], [6347, 2683], [6107, 669], [7611, 5184], [7462, 3590], [7732, 4723], [5900, 3561], [4483, 3369], [6101, 1110], [5199, 2182], [1633, 2809], [4307, 2322], [675, 1006], [7555, 4819], [7541, 3981], [3177, 756], [7352, 4506], [7545, 2801], [3245, 3305], [6426, 3173], [4608, 1198], [23, 2216], [7248, 3779], [7762, 4595], [7392, 2244], [3484, 2829], [6271, 2135], [4985, 140], [1916, 1569], [7280, 4899], [7509, 3239], [10, 2676], [6807, 2993], [5185, 3258], [3023, 1942]] cities[0] = [max_coords/2, max_coords/2] optimal = 33523 clusters = split_tour_across_clusters(cities, nb_truck) colors = [ '#1f77b4', # Bleu moyen '#ff7f0e', # Orange '#2ca02c', # Vert '#9467bd', # Violet '#d62728', # Rouge '#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 ] results = [] for cooling_rate in cooling_rates: distances_over_time = [] for i, cluster_indices in enumerate(clusters.values()): cluster_cities = [cities[index] for index in cluster_indices] simulated_annealing = SimulatedAnnealing(cluster_cities, temperature=10000, cooling_rate=cooling_rate, temperature_ok=0.01) best_route, distances = simulated_annealing.run() distances_over_time.extend(distances) # Record results for this cooling rate results.append({ 'cooling_rate': cooling_rate, 'distances': distances_over_time, }) # Plotting total distances for each cooling rate over time plt.figure() for result in results: plt.plot(result['distances'], label=f'Cooling rate: {result["cooling_rate"]}') plt.xlabel('Iteration') plt.ylabel('Total distance') plt.legend(loc='upper right') plt.title('Total distance over iterations for different cooling rates') plt.axhline(y=optimal, color='black', linestyle='--') plt.show()
On peut voir qu'avec une temperature initale supérieur tel que 20000, on obtient un meilleur résultat avec un nombre d'itération identique
Test de la variation du coût optimal en faisant varier le paramètre "inertia weight"¶
# List of inertia values to test inertia_values = np.linspace(0.1, 5, 10) # Initialize list to store optimal cost for each inertia value optimal_costs = [] # Loop over each inertia value for inertia_weight in inertia_values: # Initialize particles and the global best cost particles = [] global_best_position = np.random.permutation(range(1, num_cities+1)) global_best_cost = float('inf') # Initialize particles and find the best cost for _ in range(num_particles): particle = Particle(num_cities, num_trucks, distances, time_windows, infinite_distance_value) particles.append(particle) particle_cost = particle.evaluate_cost() if particle_cost < global_best_cost: global_best_cost = particle_cost global_best_position = particle.position.copy() # Iterate to find the optimal solution iteration = 0 best_costs = [] while iteration < max_iterations: for particle in particles: particle.update_velocity(global_best_position, inertia_weight, cognitive_weight, social_weight) particle.update_position() particle_cost = particle.evaluate_cost() if particle_cost < global_best_cost: global_best_cost = particle_cost global_best_position = particle.position.copy() best_costs.append(global_best_cost) iteration += 1 # Store the optimal cost for this inertia value optimal_costs.append(global_best_cost) # Plot the evolution of the optimal cost as a function of inertia plt.figure(figsize=(10, 6)) plt.plot(inertia_values, optimal_costs) plt.title("Evolution of Optimal Cost as a Function of Inertia") plt.xlabel("Inertia") plt.ylabel("Optimal Cost") plt.show()
Test de la variation du coût optimal en faisant varier le paramètre "cognitive weight"¶
# List of cognitive weight values to test cognitive_values = np.linspace(0.1, 5, 10) # Initialize list to store optimal cost for each cognitive weight value optimal_costs = [] # Loop over each cognitive weight value for cognitive_weight in cognitive_values: # Initialize particles and the global best cost particles = [] global_best_position = np.random.permutation(range(1, num_cities+1)) global_best_cost = float('inf') # Initialize particles and find the best cost for _ in range(num_particles): particle = Particle(num_cities, num_trucks, distances, time_windows, infinite_distance_value) particles.append(particle) particle_cost = particle.evaluate_cost() if particle_cost < global_best_cost: global_best_cost = particle_cost global_best_position = particle.position.copy() # Iterate to find the optimal solution iteration = 0 best_costs = [] while iteration < max_iterations: for particle in particles: particle.update_velocity(global_best_position, inertia_weight, cognitive_weight, social_weight) particle.update_position() particle_cost = particle.evaluate_cost() if particle_cost < global_best_cost: global_best_cost = particle_cost global_best_position = particle.position.copy() best_costs.append(global_best_cost) iteration += 1 # Store the optimal cost for this cognitive weight value optimal_costs.append(global_best_cost) # Plot the evolution of the optimal cost as a function of cognitive weight plt.figure(figsize=(10, 6)) plt.plot(cognitive_values, optimal_costs) plt.title("Evolution of Optimal Cost as a Function of Cognitive Weight") plt.xlabel("Cognitive Weight") plt.ylabel("Optimal Cost") plt.show()
La méthode du coude¶
La méthode du coude est utilisée pour trouver le nombre optimal de clusters dans un ensemble de données non étiquetées en analysant un graphique qui représente la qualité de la partition en fonction du nombre de clusters. Le point de coude dans le graphique indique le nombre optimal de clusters. Cela aide à la segmentation et à l'analyse des données dans l'apprentissage automatique non supervisé.
from sklearn.cluster import KMeans import matplotlib.pyplot as plt import numpy as np import random, time, math # Function to generate random coordinates for cities def generate_cities(nb, max_coords=1000): return [random.sample(range(max_coords), 2) for _ in range(nb)] nb_ville = 100 # number of cities max_coords = 1000 # maximum value for coordinates max_clusters = 20 # maximum number of clusters start_time_generate = time.time() cities = generate_cities(nb_ville, max_coords) # generate cities stop_time_generate = time.time() # Function to apply the elbow method for determining optimal number of clusters def elbow_method(cities, max_clusters): inertias = [] # list to store the inertia for each number of clusters for i in range(1, max_clusters+1): # Apply KMeans clustering kmeans = KMeans(n_clusters=i, random_state=0, n_init='auto').fit(cities) # Append the inertia (sum of squared distances to closest centroid) to the list inertias.append(kmeans.inertia_) return inertias cities = generate_cities(nb_ville, max_coords) # generate cities inertias = elbow_method(cities, max_clusters) # apply elbow method plt.figure() plt.plot(range(1, len(inertias)+1), inertias, marker='o') # plot the inertia for each number of clusters plt.title('Elbow Method') # set title of the plot plt.xlabel('Number of clusters') # set x-axis label plt.ylabel('Inertia') # set y-axis label plt.show() # display the plot