a3-algorithmique-avancee/Projet_algo.ipynb
2023-06-13 13:38:18 +02:00

680 KiB
Raw Blame History

Projet algorithme

Contexte du projet:

L'ADEME a lancé un appel à manifestation d'intérêt pour développer des solutions de mobilité adaptées à différents territoires. CesiCDP, en collaboration avec des partenaires, s'est spécialisé dans la Mobilité Multimodale Intelligente. Dans le cadre de cet appel, l'équipe de CesiCDP travaille sur la gestion de tournées de livraison pour minimiser les trajets et optimiser l'efficacité. L'objectif est d'obtenir de nouveaux marchés et des financements pour poursuivre le développement de l'entreprise. Des contraintes supplémentaires seront ajoutées pour rendre le problème plus réaliste et intéressant pour l'ADEME.

Notre but est de réaliser un algorithme permettant de passer par tous les points de livraisons avec un temps optimisé.

Contraintes choisies :

Les contraintes que nous avons choisi sont les suivantes:

  • Avoir plusieurs camions disponible simultanément pour effectuer les livraisons.
  • Fenêtre de temps de livraison pour chaque object

Formulation du problème

Soit 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. Chaque ville $v∈V$ a une fenêtre de temps de livraison $[a_v,b_v]$, où $a_v$ est le début de la fenêtre et $b_v$ est la fin de la fenêtre. Il y a $k$ camions disponibles pour effectuer les livraisons.

Le problème consiste à trouver une tournée pour chaque camion, de manière à ce que toutes les livraisons soient effectuées dans leurs fenêtres de temps respectives et que la date de retour du dernier camion à la base soit minimisée.

Le problème que nous avons avec les contraintes ci-dessus est le problème du VRPTW (Vehicule Routing Problem avec la contrainte supplémentaire de fenêtres d'ouverture).

Contraintes du problème

Liste des contraintes du problème:

  • Tous les clients doivent être déservis
  • Un client ne peut être servi que par un et un seul véhicule.
  • En quittant un client, un véhicule peut aller que vers un seul autre client.
  • Un véhicule ne peut servir un client que s'il a assez de capacité pour servir le client.

On va donc affecter chaque client à une tournée effectué par un seul véhicule.

Modélisation mathématique

Ensemble et paramètres:

$V=\{0,1,2,...,n\}$ : l'ensemble des villes, où 0 est la base (ou le dépôt) et $1,2,...,n$ sont les villes de livraison.
$K=\{1,2,...,k\}$ : l'ensemble des camions.
$d_{ij}$ : la distance (ou le temps de trajet) de la ville ii à la ville $j$.
$[a_i,b_i]$ : la fenêtre de temps de livraison pour la ville $i$.
$M$ : une grande constante.

Variables de décision:

$x_{ijk}$ : variable binaire qui vaut 1 si le camion $k$ se déplace de la ville $i$ à la ville $j$, et 0 sinon.
$t_{ik}$ : le moment où le camion $k$ arrive à la ville $i$.

Fonction objective:

Minimiser $Z=max_{k∈K}t_{0k}$

Contraintes:

Chaque ville est visitée une fois et une seule fois : $$\sum_{k∈K} \sum_{j∈V} x_{ijk} = 1, ∀i \in V \{0\}$$

Si un camion se déplace de la ville ii à la ville $j$, alors le moment d'arrivée à la ville $j$ doit être plus grand que le moment d'arrivée à la ville $i$ plus le temps de trajet : $$t_{ik}+d_{ij} \leq t_{jk}+M(1x_{ijk}),∀i,j \in V,i \ne j,∀k \in K$$

Les fenêtres de temps de livraison doivent être respectées : $$a_i \leq t_{ik} \leq b_i, ∀i \in V \{0 \},∀k \in K$$

Les contraintes de flux pour garantir que si un camion entre dans une ville, il doit également en sortir : $$i \in V,i \ne j∑xijk=i∈V,i \ne j∑xjik=yjk,∀j∈V,∀k∈K$$

Nous allons représenter notre problème par un graphe
$G=(V,E)$

$V$ représente les sommets du graphe qui correspondent aux clients

$E$ représente les arcs entre deux clients $i,j \in V$

On a un ensemble $C=\{1,2,...,n_c\}$ de clients qui doivent obtenir leur livraison qui provient du dépot.

L'ensemble des emplacements des clients est défini comme : $V = C \cup \{0, n_c+1\} $

$0$ et $n_c+1$ représente le dépot, puisqu'on doit revenir au point de départ le dépots est le dernier client plus un.

On a un ensemble $V=\{1,2,...,n_v\}$ de véhicule disponible et chaque véhicule possède une capacité $Q$.

Variables de décisions:

Variables de décision:

  • Un ensemble de k véhicule

Initialisation de la matrice

In [17]:
import random
import networkx as nx
import matplotlib.pyplot as plt
import numpy as np

def generate_graph(num_nodes):
    G = nx.Graph()
    G.add_nodes_from(range(num_nodes + 1))
    
    for node in G.nodes():
        connected_nodes = sorted(set(G.nodes()) - {node})
        if len(connected_nodes) < 2:
            continue
        distance1 = random.randint(1, 10)
        distance2 = random.randint(1, 10)
        random_nodes = random.sample(connected_nodes, 2)
        G.add_edges_from([(node, random_nodes[0], {'distance': distance1}),
                          (node, random_nodes[1], {'distance': distance2})])

    while not nx.is_connected(G):
        node1, node2 = random.sample(G.nodes(), 2)
        if not G.has_edge(node1, node2):
            distance = random.randint(1, 10)
            G.add_edge(node1, node2, distance=distance)

    return G

graph = generate_graph(10)
A = nx.adjacency_matrix(graph).todense()

def generate_distance_matrix(graph):
    distance_matrix = dict(nx.floyd_warshall(graph))
    num_nodes = graph.number_of_nodes()
    distance_array = np.full((num_nodes, num_nodes), np.inf)
    for i, row in distance_matrix.items():
        for j, distance in row.items():
            if i != j:
                distance_array[i][j] = distance
    return distance_array



# Générer la matrice de distances
distance_matrix = generate_distance_matrix(graph)

# Afficher la matrice de distances
print(distance_matrix)


print(A)

# Dessiner le graphe
nx.draw(graph, with_labels=True)
plt.show()
[[inf  1.  1.  2.  1.  1.  2.  1.  2.  2.  2.]
 [ 1. inf  2.  1.  2.  2.  3.  2.  3.  1.  3.]
 [ 1.  2. inf  3.  2.  2.  1.  2.  1.  1.  2.]
 [ 2.  1.  3. inf  1.  3.  4.  2.  3.  2.  4.]
 [ 1.  2.  2.  1. inf  2.  3.  1.  2.  1.  3.]
 [ 1.  2.  2.  3.  2. inf  2.  1.  1.  3.  1.]
 [ 2.  3.  1.  4.  3.  2. inf  3.  2.  2.  1.]
 [ 1.  2.  2.  2.  1.  1.  3. inf  1.  2.  2.]
 [ 2.  3.  1.  3.  2.  1.  2.  1. inf  2.  1.]
 [ 2.  1.  1.  2.  1.  3.  2.  2.  2. inf  3.]
 [ 2.  3.  2.  4.  3.  1.  1.  2.  1.  3. inf]]
[[0 1 1 0 1 1 0 1 0 0 0]
 [1 0 0 1 0 0 0 0 0 1 0]
 [1 0 0 0 0 0 1 0 1 1 0]
 [0 1 0 0 1 0 0 0 0 0 0]
 [1 0 0 1 0 0 0 1 0 1 0]
 [1 0 0 0 0 0 0 1 1 0 1]
 [0 0 1 0 0 0 0 0 0 0 1]
 [1 0 0 0 1 1 0 0 1 0 0]
 [0 0 1 0 0 1 0 1 0 0 1]
 [0 1 1 0 1 0 0 0 0 0 0]
 [0 0 0 0 0 1 1 0 1 0 0]]
No description has been provided for this image

Définition des données du problème VRPTW

In [18]:
#k Nombre de camion disponible
num_truck = 4

# 2. Attribuer les fenêtres de temps aux clients
def assign_time_windows(graph):
    # Créer un dictionnaire pour stocker les fenêtres de temps des clients
    time_windows = {}

    # Définir la fenêtre de temps pour le dépôt central (nœud 0)
    time_windows[0] = (0, float('inf'))

    # Assigner une fenêtre de temps à chaque client
    for node in graph.nodes():
        if node !=0 and node != graph.number_of_nodes() :
            # Générer une fenêtre de temps aléatoire pour chaque client
            start_time = random.randint(0, 100)
            end_time = start_time + random.randint(10, 50)
            time_windows[node] = (start_time, end_time)
           

    return time_windows

# Attribuer les fenêtres de temps aux clients
time_windows = assign_time_windows(graph)

print(max(graph.nodes()))
# Afficher les fenêtres de temps assignées
for node, window in time_windows.items():
    print("Client", node, ":", window)
    
#paramètres ACO

print(graph.nodes())
print(graph.edges())
10
Client 0 : (0, inf)
Client 1 : (35, 66)
Client 2 : (48, 88)
Client 3 : (87, 137)
Client 4 : (45, 67)
Client 5 : (36, 69)
Client 6 : (57, 97)
Client 7 : (24, 38)
Client 8 : (21, 50)
Client 9 : (50, 83)
Client 10 : (100, 119)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[(0, 7), (0, 1), (0, 2), (0, 4), (0, 5), (1, 9), (1, 3), (2, 9), (2, 6), (2, 8), (3, 4), (4, 7), (4, 9), (5, 7), (5, 8), (5, 10), (6, 10), (7, 8), (8, 10)]

Définition de l'algorithme des fourmis (marche pas encore)

In [19]:
# Nombre de fourmis
num_ants = 10
num_iterations = 100
alpha = 1   #importance de la phéromone
beta = 2    #importance de la distance
evaporation_rate = 0.5

deposit_amount = 1

pheromones = np.ones((max(graph.nodes()), max(graph.nodes()))) #matrice de phéromones


def evaporate_pheromones(pheromones, evaporation_rate):
    pheromones *= (1 - evaporation_rate)

def deposit_pheromones(pheromones, path, path_lengh, deposit_amount):
    for i in range(len(path) - 1):
        pheromones[path[i], path[i+1]] += deposit_amount / path_length

    pheromones[path[-1], 0] += deposit_amount / path_length


def check_time_window(neighbor_node, visited_nodes, time_windows, truck):
    if neighbor_node ==0:
        return True
        
    current_time = truck['current_time']
    start_time, end_time = time_windows[neighbor_node -1]
    if start_time <= current_time <= end_time:
        return True

    else: return False
    


def calculate_probabilities(graph, pheromones, current_node, visited_nodes, remaining_nodes, alpha, beta, time_windows, truck):
    probabilities = []
    total_probability = 0

    for neighbor_node in remaining_nodes:
        if graph.has_edge(current_node, neighbor_node) and check_time_window(neighbor_node, visited_nodes, time_windows, truck):
            distance = graph[current_node][neighbor_node]['distance']

            if not np.isinf(distance):
                pheromone = pheromones[current_node][neighbor_node]
                heuristic = 1 / distance
                probability = (pheromone ** alpha) * (heuristic ** beta)
                probabilities.append((neighbor_node, probability))
                total_probability += probability

    # Normalisation des probabilités
    probabilities = [(neighbor_node, probability / total_probability) for neighbor_node, probability in probabilities]

    return probabilities


def select_next_node(probabilities, remaining_nodes):
    random_number = random.random()
    cumulative_probability = 0

    for neighbor_node, probability in probabilities:
        cumulative_probability += probability
        if random_number <= cumulative_probability:
            return neighbor_node

        
    if probabilities:
        return random.choice([neighbor_node for neighbor_node, _ in probabilities])
    else:
        return random.choice(list(remaining_nodes))




def generate_solution(graph, pheromones, alpha, beta, time_windows, num_truck):
    num_nodes = graph.number_of_nodes()
    num_vehicles = min(num_nodes - 1, num_truck)  # Nombre de camions à utiliser

    solution = [[] for _ in range(num_vehicles)]  # Liste des chemins pour chaque camion

    for truck in range(num_vehicles):
        current_node = 0  # Dépôt central comme point de départ pour chaque camion
        visited_nodes = set([current_node])  # Ensemble des nœuds visités par le camion
        remaining_nodes = set(graph.nodes()) - visited_nodes  # Ensemble des nœuds restants

        start_time = 0
        current_truck = {'current_time': start_time}

        while remaining_nodes:
            probabilities = calculate_probabilities(graph, pheromones, current_node, visited_nodes, remaining_nodes, alpha, beta, time_windows, current_truck)

            next_node = select_next_node(probabilities, remaining_nodes)

            edge_data = graph.get_edge_data(current_node, next_node)
            if edge_data is not None:
                distance = edge_data['distance']
                start_time = current_truck['current_time'] + distance
                current_truck['current_time'] = start_time
                solution[truck].append((current_node, next_node))
                visited_nodes.add(next_node)
                remaining_nodes.remove(next_node)
                current_node = next_node


        # Ajouter le retour au dépôt central à la fin du trajet du camion
        solution[truck].append((current_node, 0))

    solution_length = calculate_solution_length(graph, solution)

    


    return solution, solution_length


def calculate_solution_length(graph, solution):
    length = 0
    for truck_path in solution:
        for i in range(len(truck_path) - 1):
            current_node, next_node = truck_path[i]
            distance = graph[current_node][next_node]['distance']
            if not np.isinf(distance):

                length += distance
    return length

    
    

# Initialiser la meilleure solution trouvée
best_solution = None
best_solution_length = float('inf')

for iteration in range(num_iterations):
    for ant in range(num_ants):
        solution , solution_length = generate_solution(graph, pheromones, alpha, beta, time_windows, num_truck)

        deposit_pheromones(pheromones, solution, solution_length, deposit_amount)

        if solution_length < best_solution_length:
            best_solution = solution
            best_solution_length = solution_length


    evaporate_pheromones(pheromones, evaporation_rate)

print("Meilleure solution trouvée:", best_solution)
print("Longueur de la meilleure solution:", best_solution_length)
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
Cell In[19], line 136
    134 for iteration in range(num_iterations):
    135     for ant in range(num_ants):
--> 136         solution , solution_length = generate_solution(graph, pheromones, alpha, beta, time_windows, num_truck)
    138         deposit_pheromones(pheromones, solution, solution_length, deposit_amount)
    140         if solution_length < best_solution_length:

Cell In[19], line 92, in generate_solution(graph, pheromones, alpha, beta, time_windows, num_truck)
     89 while remaining_nodes:
     90     probabilities = calculate_probabilities(graph, pheromones, current_node, visited_nodes, remaining_nodes, alpha, beta, time_windows, current_truck)
---> 92     next_node = select_next_node(probabilities, remaining_nodes)
     94     edge_data = graph.get_edge_data(current_node, next_node)
     95     if edge_data is not None:

Cell In[19], line 67, in select_next_node(probabilities, remaining_nodes)
     63     if random_number <= cumulative_probability:
     64         return neighbor_node
---> 67 if probabilities:
     68     return random.choice([neighbor_node for neighbor_node, _ in probabilities])
     69 else:

KeyboardInterrupt: 

Test 2 fourmis

In [1]:
import random
import numpy as np
import networkx as nx
import matplotlib.pyplot as plt

def generate_graph(num_nodes):
    G = nx.Graph()
    G.add_nodes_from(range(num_nodes + 1))
    
    for node in G.nodes():
        connected_nodes = sorted(set(G.nodes()) - {node})
        if len(connected_nodes) < 2:
            continue
        distance1 = random.randint(1, 10)
        distance2 = random.randint(1, 10)
        random_nodes = random.sample(connected_nodes, 2)
        G.add_edges_from([(node, random_nodes[0], {'distance': distance1}),
                          (node, random_nodes[1], {'distance': distance2})])

    while not nx.is_connected(G):
        node1, node2 = random.sample(G.nodes(), 2)
        if not G.has_edge(node1, node2):
            distance = random.randint(1, 10)
            G.add_edge(node1, node2, distance=distance)

    # Assigner des positions aléatoires aux nœuds
    pos = {node: (random.uniform(0, 10), random.uniform(0, 10)) for node in G.nodes()}
    nx.set_node_attributes(G, pos, 'pos')

    return G

graph = generate_graph(999)
A = nx.adjacency_matrix(graph).todense()

def generate_distance_matrix(graph):
    distance_matrix = dict(nx.floyd_warshall(graph))
    num_nodes = graph.number_of_nodes()
    distance_array = np.zeros((num_nodes, num_nodes))
    for i, row in distance_matrix.items():
        for j, distance in row.items():
            
            distance_array[i][j] = distance
    return distance_array



# Générer la matrice de distances
distance_matrix = generate_distance_matrix(graph)

# Afficher la matrice de distances
print(distance_matrix)


print(A)

pos = nx.get_node_attributes(graph, 'pos')
# Dessiner le graphe
nx.draw(graph, with_labels=True)
plt.show()
[[0. 6. 7. ... 5. 6. 6.]
 [6. 0. 5. ... 6. 8. 6.]
 [7. 5. 0. ... 6. 5. 3.]
 ...
 [5. 6. 6. ... 0. 5. 5.]
 [6. 8. 5. ... 5. 0. 7.]
 [6. 6. 3. ... 5. 7. 0.]]
[[0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]
 ...
 [0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]]
No description has been provided for this image
In [4]:
import time
#Nombre de camion
num_trucks = 2

# Fonction d'évaluation de la qualité d'une solution (ici, la distance totale)
def evaluate_solution(solution, distances):
    total_distance = 0
    num_nodes = len(solution)

    for i in range(num_nodes - 1):
        current_node = solution[i]
        next_node = solution[i + 1]
        total_distance += distances[current_node][next_node]

    # Ajouter la distance de retour au dépôt
    total_distance += distances[solution[-1]][solution[0]]

    return total_distance


# Algorithme ACO
def ant_colony_optimization(distances, num_ants, num_iterations, evaporation_rate, alpha, beta):
    num_nodes = len(distances)
    
    pheromone = np.ones((num_nodes, num_nodes))  # Matrice de phéromones initiale
    best_solution = None
    best_distance = float('inf')

    start_time = time.time()


    for iteration in range(num_iterations):
        # Construction de solutions par les fourmis
        solutions = []

        for ant in range(num_ants):
            visited = set()
            current_node = random.randint(0, num_nodes - 1)
            visited.add(current_node)
            solution = [current_node]

            while len(visited) < num_nodes:
                next_node = None
                probabilities = []

                # Calcul des probabilités de choisir chaque prochain nœud
                for node in range(num_nodes):
                    if node not in visited:
                        pheromone_value = pheromone[current_node][node]
                        distance_value = distances[current_node][node]
                        probability = (pheromone_value ** alpha) * ((1 / distance_value) ** beta)
                        probabilities.append((node, probability))

                total_probability = sum(prob for _, prob in probabilities)
                probabilities = [(node, prob / total_probability) for node, prob in probabilities]

                # Choix du prochain nœud basé sur les probabilités
                roulette_wheel = random.random()
                probability_sum = 0

                for node, probability in probabilities:
                    probability_sum += probability
                    if probability_sum >= roulette_wheel:
                        next_node = node
                        break

                visited.add(next_node)
                solution.append(next_node)
                current_node = next_node


            # Ajouter le retour au dépôt central à la fin du trajet
            solution.append(0)

            solutions.append(solution)

        # Évaluation des solutions et mise à jour de la meilleure solution
        for solution in solutions:
            distance = evaluate_solution(solution, distances)
            if distance < best_distance:
                best_solution = solution
                best_distance = distance

        # Mise à jour des phéromones
        pheromone *= evaporation_rate  # Évaporation des phéromones existantes

        for solution in solutions:
            delta_pheromone = 1 / evaluate_solution(solution, distances)
            for i in range(num_nodes - 1):
                node1 = solution[i]
                node2 = solution[i + 1]
                pheromone[node1][node2] += delta_pheromone
                pheromone[node2][node1] += delta_pheromone




    return best_solution, best_distance


 
num_ants = 10
num_iterations = 50
evaporation_rate = 0.5
alpha = 1
beta = 1


best_solution, best_distance = ant_colony_optimization(distance_matrix, num_ants, num_iterations, evaporation_rate, alpha, beta)



print("Meilleure solution :", best_solution)
print("Distance totale :", best_distance)
Meilleure solution : [29, 454, 56, 249, 934, 996, 547, 229, 170, 907, 483, 453, 958, 84, 559, 415, 756, 428, 19, 890, 533, 477, 774, 511, 642, 136, 777, 797, 630, 325, 899, 782, 7, 337, 236, 16, 318, 179, 743, 586, 684, 113, 647, 600, 852, 368, 514, 176, 117, 662, 536, 451, 73, 152, 691, 862, 223, 478, 543, 738, 452, 50, 173, 532, 193, 706, 991, 427, 984, 243, 521, 191, 849, 905, 275, 322, 13, 665, 66, 854, 104, 343, 970, 6, 735, 253, 74, 139, 882, 464, 928, 319, 190, 309, 461, 690, 147, 291, 276, 989, 535, 966, 106, 431, 699, 930, 280, 576, 356, 643, 866, 573, 683, 802, 475, 976, 936, 707, 429, 47, 565, 764, 333, 745, 9, 257, 28, 245, 435, 838, 950, 994, 634, 785, 940, 815, 627, 701, 417, 102, 300, 228, 37, 715, 503, 729, 975, 742, 574, 588, 953, 875, 230, 472, 120, 468, 667, 432, 768, 670, 15, 668, 675, 923, 463, 391, 987, 792, 349, 375, 167, 737, 426, 34, 676, 305, 348, 795, 131, 833, 493, 123, 199, 303, 207, 595, 485, 405, 25, 70, 148, 281, 210, 530, 972, 182, 625, 697, 566, 423, 763, 912, 399, 163, 902, 437, 889, 603, 761, 296, 277, 678, 138, 597, 365, 140, 932, 860, 510, 791, 169, 92, 904, 301, 954, 846, 8, 60, 931, 49, 537, 525, 723, 46, 933, 959, 346, 80, 839, 225, 177, 751, 40, 604, 411, 584, 482, 563, 581, 320, 783, 258, 915, 943, 422, 479, 842, 920, 76, 71, 75, 731, 967, 841, 807, 384, 352, 711, 285, 302, 57, 531, 314, 825, 529, 460, 734, 222, 489, 776, 949, 980, 620, 938, 393, 151, 944, 637, 474, 353, 339, 244, 196, 524, 447, 380, 879, 726, 494, 38, 269, 334, 809, 853, 937, 121, 677, 661, 373, 626, 878, 403, 945, 858, 154, 231, 445, 188, 209, 500, 705, 822, 736, 638, 652, 672, 336, 840, 110, 688, 293, 409, 674, 721, 557, 304, 143, 865, 926, 918, 844, 288, 458, 857, 355, 41, 414, 335, 636, 252, 12, 757, 556, 103, 202, 856, 522, 471, 52, 189, 948, 814, 801, 186, 402, 175, 312, 553, 973, 951, 88, 197, 397, 247, 446, 999, 570, 548, 673, 203, 59, 550, 264, 506, 235, 759, 897, 611, 111, 749, 771, 476, 725, 130, 969, 456, 635, 594, 466, 516, 263, 505, 513, 780, 527, 94, 434, 770, 956, 385, 492, 824, 868, 788, 388, 805, 178, 896, 748, 118, 361, 784, 639, 371, 248, 187, 778, 369, 164, 379, 836, 96, 922, 17, 156, 800, 992, 430, 523, 916, 457, 561, 695, 260, 696, 279, 374, 215, 773, 421, 172, 947, 587, 599, 623, 158, 254, 455, 212, 26, 618, 818, 767, 283, 942, 251, 997, 541, 685, 129, 664, 605, 727, 577, 703, 367, 859, 981, 450, 181, 310, 323, 11, 911, 730, 957, 558, 87, 850, 694, 401, 520, 507, 713, 67, 519, 893, 512, 698, 832, 387, 444, 578, 883, 396, 941, 656, 804, 681, 44, 473, 894, 2, 390, 161, 286, 960, 971, 487, 819, 83, 268, 651, 552, 758, 98, 459, 442, 449, 194, 51, 861, 517, 499, 155, 704, 887, 27, 845, 86, 663, 508, 881, 490, 657, 848, 162, 498, 900, 299, 702, 90, 835, 724, 908, 504, 282, 816, 632, 351, 89, 198, 633, 32, 515, 294, 978, 648, 669, 545, 3, 544, 255, 741, 206, 619, 491, 21, 22, 821, 4, 607, 146, 316, 317, 810, 448, 628, 982, 221, 564, 168, 404, 408, 961, 892, 100, 1, 205, 986, 345, 796, 501, 869, 615, 344, 266, 653, 806, 39, 877, 567, 955, 837, 327, 718, 847, 990, 787, 271, 964, 127, 54, 488, 224, 486, 160, 646, 876, 14, 716, 382, 119, 811, 398, 708, 963, 109, 183, 728, 560, 108, 542, 469, 126, 180, 219, 338, 624, 114, 870, 354, 591, 895, 608, 551, 358, 909, 710, 753, 687, 400, 760, 769, 518, 43, 995, 125, 441, 308, 572, 270, 363, 579, 546, 609, 983, 347, 324, 395, 267, 988, 781, 58, 831, 55, 410, 585, 903, 218, 77, 562, 0, 133, 61, 211, 465, 621, 898, 48, 42, 332, 192, 689, 124, 443, 134, 273, 644, 714, 424, 569, 261, 166, 968, 812, 370, 534, 307, 610, 284, 91, 540, 82, 33, 830, 979, 64, 144, 439, 436, 239, 820, 150, 105, 813, 686, 418, 885, 823, 185, 851, 631, 329, 495, 101, 919, 63, 195, 755, 700, 79, 659, 297, 287, 914, 278, 880, 946, 554, 660, 654, 290, 315, 746, 137, 750, 350, 153, 921, 99, 568, 386, 962, 298, 128, 722, 682, 232, 874, 717, 85, 467, 5, 589, 794, 828, 786, 69, 658, 331, 719, 174, 328, 886, 406, 917, 582, 274, 826, 843, 240, 313, 220, 641, 201, 484, 376, 381, 740, 871, 680, 808, 132, 863, 233, 571, 754, 834, 747, 901, 650, 413, 692, 360, 171, 242, 583, 272, 45, 227, 24, 78, 965, 803, 592, 378, 389, 366, 913, 311, 497, 200, 509, 36, 666, 208, 935, 184, 601, 416, 974, 31, 115, 265, 204, 157, 612, 246, 762, 775, 793, 602, 733, 640, 262, 649, 539, 165, 985, 765, 321, 622, 295, 929, 306, 924, 481, 392, 829, 772, 732, 289, 590, 671, 53, 241, 234, 528, 952, 226, 425, 20, 62, 993, 141, 292, 238, 93, 538, 709, 613, 739, 744, 149, 256, 575, 910, 817, 580, 377, 645, 616, 629, 364, 23, 480, 655, 330, 977, 720, 383, 340, 555, 617, 679, 341, 372, 888, 65, 906, 712, 864, 438, 407, 939, 462, 789, 35, 470, 790, 122, 873, 81, 342, 18, 145, 526, 693, 433, 549, 112, 213, 606, 593, 116, 250, 779, 496, 884, 412, 214, 135, 394, 752, 925, 872, 362, 799, 30, 614, 10, 998, 107, 97, 596, 237, 326, 357, 420, 359, 259, 72, 927, 95, 142, 217, 216, 798, 766, 855, 867, 68, 891, 159, 827, 440, 598, 419, 502, 0]
Distance totale : 2245.0

Calcul de l'évolution du temps en fonction du nombre de villes

In [96]:
x_values = [graph.nodes[x]['pos'][0] for x in best_solution]
y_values = [graph.nodes[x]['pos'][1] for x in best_solution]

# Création de la figure et des axes
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.set_xlabel('Coordonnée X')
ax.set_ylabel('Coordonnée Y')
ax.set_title("Tracé du trajet emprunté par les fourmis")

# Tracer le graphe sans les arêtes
nx.draw_networkx_nodes(graph, pos=nx.get_node_attributes(graph, 'pos'), ax=ax)
nx.draw_networkx_labels(graph, pos=nx.get_node_attributes(graph, 'pos'), ax=ax)

# Tracer uniquement le trajet emprunté par les fourmis
ax.plot(x_values, y_values, linestyle='-', marker='o')

# Tracer une ligne du dernier nœud au premier nœud pour fermer la boucle
ax.plot([x_values[-1], x_values[0]], [y_values[-1], y_values[0]], linestyle='-', color='r')

# Afficher le graphique
plt.show()
No description has been provided for this image

Avec un nombre de camion disponible

In [2]:
import concurrent.futures
num_trucks = 1  # Nombre de camions disponibles


# Fonction d'évaluation de la qualité d'une solution (ici, la distance totale)
def evaluate_solution(solution, distances):
    total_distance = 0
    num_nodes = len(solution)

    for i in range(num_nodes - 1):
        current_node = solution[i]
        next_node = solution[i + 1]
        total_distance += distances[current_node][next_node]


    #if solution[-1] == 0 and len(solution) < num_nodes:
        #return float('inf')

    # Ajouter la distance de retour au dépôt
    total_distance += distances[solution[-1]][solution[0]]

    return total_distance

    
def ant_colony_optimization(distances, num_ants, num_iterations, evaporation_rate, alpha, beta, num_trucks):
    num_nodes = len(distances)
    
    pheromone = np.ones((num_nodes, num_nodes))  # Matrice de phéromones initiale
    best_solution = None
    best_distance = float('inf')

    start_time = time.time()

    for iteration in range(num_iterations):
        # Construction de solutions par les fourmis
        solutions = []

        for ant in range(num_ants):
            visited = set()
            current_node = random.randint(0, num_nodes - 1)
            visited.add(current_node)
            solution = [current_node]

            while len(visited) < num_nodes:
                next_node = None
                probabilities = []

                # Calcul des probabilités de choisir chaque prochain nœud
                for node in range(num_nodes):
                    if node not in visited and (node !=0 or len(visited) == num_nodes - 1):
                        pheromone_value = pheromone[current_node][node]
                        distance_value = distances[current_node][node]
                        probability = (pheromone_value ** alpha) * ((1 / distance_value) ** beta)
                        probabilities.append((node, probability))

                total_probability = sum(prob for _, prob in probabilities)
                probabilities = [(node, prob / total_probability) for node, prob in probabilities]

            
                roulette_wheel = random.random()
                probability_sum = 0


                for node, probability in probabilities:
                    
                    probability_sum += probability
                    if probability_sum >= roulette_wheel:
                        next_node = node
                        break

                visited.add(next_node)
                solution.append(next_node)
                current_node = next_node

            # Ajouter le retour au dépôt central à la fin du trajet
            solution.append(0)

            solutions.append(solution)

        # Évaluation des solutions et mise à jour de la meilleure solution
        for solution in solutions:
            distance = evaluate_solution(solution, distances)
            if distance < best_distance:
                best_solution = solution
                best_distance = distance

        # Mise à jour des phéromones
        pheromone *= evaporation_rate  # Évaporation des phéromones existantes

        for solution in solutions:
            delta_pheromone = 1 / evaluate_solution(solution, distances)
            for i in range(num_nodes - 1):
                node1 = solution[i]
                node2 = solution[i + 1]
                pheromone[node1][node2] += delta_pheromone
                pheromone[node2][node1] += delta_pheromone

    # Séparer la meilleure solution en trajets pour chaque camion
    truck_solutions = []
    num_nodes_per_truck = num_nodes // num_trucks

    for i in range(num_trucks):
        start_index = i * num_nodes_per_truck
        end_index = start_index + num_nodes_per_truck
        truck_solution = best_solution[start_index:end_index]
        truck_solutions.append(truck_solution + [0])
        

    
    return truck_solutions, best_distance


def trucks_thread(i, num_nodes_per_truck, best_solution, truck_solutions):
    start_index = i * num_nodes_per_truck
    end_index = start_index + num_nodes_per_truck
    truck_solution = best_solution[start_index:end_index]
    truck_solutions.append(truck_solution)
    return truck_solutions




num_ants = 10
num_iterations = 100
evaporation_rate = 0.5
alpha = 1
beta = 1

best_truck_solutions, best_distance = ant_colony_optimization(distance_matrix, num_ants, num_iterations, evaporation_rate, alpha, beta, num_trucks)



print("Meilleure solution :", best_truck_solutions)
print("Distance totale :", best_distance)
    
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[2], line 129
    126 alpha = 1
    127 beta = 1
--> 129 best_truck_solutions, best_distance = ant_colony_optimization(distance_matrix, num_ants, num_iterations, evaporation_rate, alpha, beta, num_trucks)
    133 print("Meilleure solution :", best_truck_solutions)
    134 print("Distance totale :", best_distance)

Cell In[2], line 32, in ant_colony_optimization(distances, num_ants, num_iterations, evaporation_rate, alpha, beta, num_trucks)
     29 best_solution = None
     30 best_distance = float('inf')
---> 32 start_time = time.time()
     34 for iteration in range(num_iterations):
     35     # Construction de solutions par les fourmis
     36     solutions = []

NameError: name 'time' is not defined
In [203]:
# Créer une liste de couleurs pour chaque véhicule
colors = ['red', 'blue', 'green', 'orange', 'purple']  # Ajoutez plus de couleurs si nécessaire

# Création de la figure et des axes
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.set_xlabel('Coordonnée X')
ax.set_ylabel('Coordonnée Y')
ax.set_title("Tracé du trajet emprunté par les fourmis")

# Tracer le graphe sans les arêtes
nx.draw_networkx_nodes(graph, pos=nx.get_node_attributes(graph, 'pos'), ax=ax)
nx.draw_networkx_labels(graph, pos=nx.get_node_attributes(graph, 'pos'), ax=ax)

# Tracer chaque trajet de chaque véhicule avec une couleur différente
for i, truck_solution in enumerate(best_truck_solutions):
    x_values = [graph.nodes[x]['pos'][0] for x in truck_solution]
    y_values = [graph.nodes[x]['pos'][1] for x in truck_solution]
    ax.plot(x_values, y_values, linestyle='-', marker='o', color=colors[i % len(colors)])

    # Tracer une ligne du dernier nœud au premier nœud pour fermer la boucle
    ax.plot([x_values[-1], x_values[0]], [y_values[-1], y_values[0]], linestyle='-', color=colors[i % len(colors)])

# Afficher le graphique
plt.show()
No description has been provided for this image