Colonies de fourmis (Ant Colony Optimization)
Objectifs
Comprendre le principe des algorithmes de colonies de fourmis, leur inspiration bio-inspirée, et savoir les appliquer à des problèmes d'optimisation combinatoire comme le voyageur de commerce.
Prérequis
Notions d'optimisation combinatoire, algorithmes stochastiques, problème du voyageur de commerce (TSP).
L'optimisation par colonies de fourmis (Ant Colony Optimization, ACO) est une métaheuristique inspirée du comportement des fourmis réelles pour trouver le plus court chemin entre leur nid et une source de nourriture. Développée par Marco Dorigo dans sa thèse en 1992, cette méthode est particulièrement efficace pour les problèmes d'optimisation combinatoire.
Inspiration biologique : le comportement des fourmis
Dans la nature, les fourmis sont capables de trouver le chemin le plus court entre leur nid et une source de nourriture, sans communication directe ni vision globale. Ce phénomène repose sur un mécanisme simple mais puissant : le dépôt de phéromones.
- Les fourmis se déplacent en déposant une substance chimique appelée phéromone sur leur passage.
- Les autres fourmis ont tendance à suivre les chemins avec une concentration plus élevée de phéromones.
- Plus un chemin est emprunté, plus il accumule de phéromones, ce qui le rend encore plus attractif (renforcement positif).
- Les chemins plus courts sont parcourus plus rapidement et reçoivent donc plus de phéromones par unité de temps.
- Les phéromones s'évaporent avec le temps, ce qui évite la convergence prématurée vers des solutions sous-optimales.
Illustration : les fourmis préfèrent le chemin le plus court grâce au dépôt de phéromones
Principe de l'algorithme ACO
L'algorithme ACO transpose ce comportement biologique à l'optimisation combinatoire :
- Fourmis artificielles : agents qui construisent des solutions au problème.
- Phéromone : information numérique déposée sur les composants de la solution (arêtes, villes).
- Chemin : une solution complète (ex: tournée dans le TSP).
- Qualité de la solution : plus la solution est bonne, plus les fourmis déposent de phéromones.
- Évaporation : diminution progressive des phéromones pour éviter la convergence prématurée.
Les composants clés d'un algorithme ACO
домаћинствима chimique домаћинствима chimique + perception
| Composant | Rôle | Analogie biologique |
|---|---|---|
| Fourmis artificielles | Construisent des solutions | Fourmis réelles |
| Phéromone | Guide la recherche (mémoire collective) | |
| Informations heuristiques | Connaissances spécifiques au problème | Vision / distance perçue |
| Règle de transition | Choix probabiliste du prochain élément | |
| Évaporation | Évite la convergence prématurée | Évaporation naturelle des phéromones |
Application au problème du voyageur de commerce (TSP)
Le problème du voyageur de commerce consiste à trouver le plus court cycle passant par un ensemble de villes, chaque ville étant visitée une seule fois.
Formalisation du problème
Soit un graphe complet \(G = (V, E)\) où :
- \(V = \{1, 2, \ldots, n\}\) est l'ensemble des villes
- \(d_{ij}\) est la distance entre les villes \(i\) et \(j\)
On cherche une permutation \(\pi = (\pi_1, \pi_2, \ldots, \pi_n)\) minimisant :
\[ f(\pi) = \sum_{k=1}^{n-1} d_{\pi_k, \pi_{k+1}} + d_{\pi_n, \pi_1} \]
Composants spécifiques au TSP
Quantité de phéromone déposée sur l'arête reliant la ville \(i\) à la ville \(j\). Plus \(\tau_{ij}\) est élevé, plus l'arête est attractive.
Inverse de la distance : \(\eta_{ij} = \frac{1}{d_{ij}}\). Plus la distance est courte, plus cette information est élevée.
Règle de transition probabiliste
Une fourmi située à la ville \(i\) choisit la ville suivante \(j\) (parmi les villes non encore visitées) avec la probabilité :
où :
- \(\alpha\) : importance relative de la phéromone (exploitation de la mémoire collective)
- \(\beta\) : importance relative de l'information heuristique (guidage local)
- \(\alpha\) élevé → les fourmis suivent davantage les traces existantes (exploitation)
- \(\beta\) élevé → les fourmis privilégient les arêtes courtes (guidage glouton)
- Équilibre : un bon compromis entre \(\alpha\) et \(\beta\) est essentiel pour une recherche efficace.
Mise à jour des phéromones
Après que toutes les fourmis ont construit leur tournée, les phéromones sont mises à jour :
Évaporation :
\[ \tau_{ij} \leftarrow (1 - \rho) \cdot \tau_{ij} \]Dépôt :
\[ \tau_{ij} \leftarrow \tau_{ij} + \sum_{k=1}^{m} \Delta \tau_{ij}^k \]où \(\Delta \tau_{ij}^k = \frac{Q}{L_k}\) si la fourmi \(k\) a emprunté l'arête \((i,j)\), avec :
- \(\rho \in [0,1]\) : taux d'évaporation
- \(Q\) : constante de dépôt
- \(L_k\) : longueur de la tournée de la fourmi \(k\)
Algorithme complet
Initialisation :
Pour chaque arête (i,j), initialiser τ_ij = τ_0 (petite constante)
Définir les paramètres : α, β, ρ, Q, m (nombre de fourmis), max_iterations
Pour iteration = 1 à max_iterations :
// Phase de construction
Pour chaque fourmi k = 1 à m :
Départ d'une ville aléatoire
Tant que la tournée n'est pas complète :
Sélectionner la prochaine ville j selon la règle de transition probabiliste
Marquer j comme visitée
Calculer la longueur L_k de la tournée
Mettre à jour la meilleure tournée globale
// Phase de mise à jour des phéromones
Pour chaque arête (i,j) :
τ_ij = (1 - ρ) × τ_ij // Évaporation
Pour chaque fourmi k = 1 à m :
Pour chaque arête (i,j) de la tournée de k :
τ_ij = τ_ij + Q / L_k // Dépôt
Retourner la meilleure tournée trouvée
Implémentation Python
import numpy as np
import random
def calculer_heuristique(distances):
"""
Calcule l'information heuristique (inverse des distances)
Retourne une matrice n x n où η[i][j] = 1 / distances[i][j]
"""
n = len(distances)
heuristique = np.zeros((n, n))
for i in range(n):
for j in range(n):
if i != j:
heuristique[i][j] = 1.0 / distances[i][j]
return heuristique
def calculer_longueur_tournee(tournee, distances):
"""Calcule la longueur totale d'une tournée"""
n = len(tournee)
longueur = 0
for i in range(n - 1):
longueur += distances[tournee[i]][tournee[i + 1]]
longueur += distances[tournee[-1]][tournee[0]]
return longueur
def choisir_prochaine_ville(ville_courante, visitees, pheromones, heuristique, alpha, beta):
"""
Sélectionne la prochaine ville selon la règle de transition probabiliste
Retourne l'indice de la ville choisie
"""
n = len(pheromones)
non_visitees = [v for v in range(n) if v not in visitees]
if not non_visitees:
return None
# Calcul des probabilités
numerateurs = []
for j in non_visitees:
pheromone = pheromones[ville_courante][j] ** alpha
heur = heuristique[ville_courante][j] ** beta
numerateurs.append(pheromone * heur)
total = sum(numerateurs)
probabilites = [num / total for num in numerateurs]
# Choix probabiliste
return random.choices(non_visitees, weights=probabilites)[0]
def construire_tournee(distances, pheromones, heuristique, alpha, beta):
"""
Une fourmi construit une tournée complète
Retourne la tournée et sa longueur
"""
n = len(distances)
depart = random.randint(0, n - 1)
tournee = [depart]
visitees = set([depart])
while len(tournee) < n:
courante = tournee[-1]
prochaine = choisir_prochaine_ville(courante, visitees, pheromones,
heuristique, alpha, beta)
tournee.append(prochaine)
visitees.add(prochaine)
longueur = calculer_longueur_tournee(tournee, distances)
return tournee, longueur
def deposer_pheromones(pheromones, tournees, longueurs, rho, Q):
"""
Met à jour les phéromones (évaporation + dépôt par toutes les fourmis)
"""
n = len(pheromones)
# Évaporation
pheromones *= (1 - rho)
# Dépôt par chaque fourmi
for tournee, longueur in zip(tournees, longueurs):
depot = Q / longueur
for i in range(n - 1):
pheromones[tournee[i]][tournee[i + 1]] += depot
pheromones[tournee[i + 1]][tournee[i]] += depot
# Retour à la ville de départ
pheromones[tournee[-1]][tournee[0]] += depot
pheromones[tournee[0]][tournee[-1]] += depot
def ant_colony_optimization(distances, n_fourmis=10, alpha=1, beta=2,
rho=0.5, Q=100, tau0=0.01, max_iterations=100, verbose=True):
"""
Algorithme d'optimisation par colonies de fourmis (ACO) pour le TSP
Paramètres :
distances : matrice des distances (n x n)
n_fourmis : nombre de fourmis
alpha : importance de la phéromone
beta : importance de l'information heuristique
rho : taux d'évaporation (0 < rho < 1)
Q : constante de dépôt
tau0 : phéromone initiale
max_iterations : nombre maximum d'itérations
verbose : affichage des résultats intermédiaires
Retourne :
meilleure_tournee : liste des villes dans l'ordre optimal
meilleure_longueur : longueur de la tournée optimale
"""
n = len(distances)
# Initialisation des phéromones
pheromones = np.full((n, n), tau0)
# Calcul de l'information heuristique
heuristique = calculer_heuristique(distances)
# Meilleure solution trouvée
meilleure_tournee = None
meilleure_longueur = float('inf')
# Boucle principale
for iteration in range(max_iterations):
# Phase de construction des tournées
tournees = []
longueurs = []
for _ in range(n_fourmis):
tournee, longueur = construire_tournee(distances, pheromones,
heuristique, alpha, beta)
tournees.append(tournee)
longueurs.append(longueur)
# Mise à jour de la meilleure solution
if longueur < meilleure_longueur:
meilleure_longueur = longueur
meilleure_tournee = tournee
# Phase de mise à jour des phéromones
deposer_pheromones(pheromones, tournees, longueurs, rho, Q)
# Affichage
if verbose and iteration % 10 == 0:
print(f"Itération {iteration}: meilleure longueur = {meilleure_longueur:.2f}")
return meilleure_tournee, meilleure_longueur
# Exemple d'utilisation
if __name__ == "__main__":
# Matrice des distances (exemple avec 5 villes)
distances = np.array([
[0, 10, 15, 20, 25],
[10, 0, 35, 30, 20],
[15, 35, 0, 25, 30],
[20, 30, 25, 0, 15],
[25, 20, 30, 15, 0]
])
# Exécution de l'algorithme
meilleure_tournee, meilleure_longueur = ant_colony_optimization(
distances,
n_fourmis=10,
alpha=1,
beta=2,
rho=0.5,
Q=100,
tau0=0.01,
max_iterations=50,
verbose=True
)
print(f"\n=== Résultats ===")
print(f"Meilleure tournée trouvée : {meilleure_tournee}")
print(f"Longueur totale : {meilleure_longueur}")
# Affichage détaillé
print(f"\nDétail du parcours :")
for i in range(len(meilleure_tournee) - 1):
depart = meilleure_tournee[i]
arrivee = meilleure_tournee[i + 1]
print(f" {depart} → {arrivee} : {distances[depart][arrivee]}")
dernier = meilleure_tournee[-1]
premier = meilleure_tournee[0]
print(f" {dernier} → {premier} : {distances[dernier][premier]}")
Itération 0: meilleure longueur = 95.00 Itération 10: meilleure longueur = 85.00 Itération 20: meilleure longueur = 80.00 Itération 30: meilleure longueur = 80.00 Itération 40: meilleure longueur = 80.00 === Résultats === Meilleure tournée trouvée : [0, 1, 4, 3, 2] Longueur totale : 80.00 Détail du parcours : 0 → 1 : 10 1 → 4 : 20 4 → 3 : 15 3 → 2 : 25 2 → 0 : 15
Implémentation en C
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#define N_VILLES 5
#define N_FOURMIS 10
#define MAX_ITERATIONS 50
#define ALPHA 1.0
#define BETA 2.0
#define RHO 0.5
#define Q 100.0
#define TAU0 0.01
// Matrice des distances
double distances[N_VILLES][N_VILLES] = {
{0, 10, 15, 20, 25},
{10, 0, 35, 30, 20},
{15, 35, 0, 25, 30},
{20, 30, 25, 0, 15},
{25, 20, 30, 15, 0}
};
// Phéromones
double pheromones[N_VILLES][N_VILLES];
double heuristique[N_VILLES][N_VILLES];
// Calcul de l'information heuristique
void initialiser_heuristique() {
for (int i = 0; i < N_VILLES; i++) {
for (int j = 0; j < N_VILLES; j++) {
if (i != j) {
heuristique[i][j] = 1.0 / distances[i][j];
} else {
heuristique[i][j] = 0.0;
}
}
}
}
// Initialisation des phéromones
void initialiser_pheromones() {
for (int i = 0; i < N_VILLES; i++) {
for (int j = 0; j < N_VILLES; j++) {
pheromones[i][j] = TAU0;
}
}
}
// Calcul de la longueur d'une tournée
double calculer_longueur(int tournee[N_VILLES]) {
double longueur = 0.0;
for (int i = 0; i < N_VILLES - 1; i++) {
longueur += distances[tournee[i]][tournee[i + 1]];
}
longueur += distances[tournee[N_VILLES - 1]][tournee[0]];
return longueur;
}
// Vérifie si une ville a déjà été visitée
int est_visitee(int ville, int visitees[N_VILLES], int nb_visitees) {
for (int i = 0; i < nb_visitees; i++) {
if (visitees[i] == ville) return 1;
}
return 0;
}
// Sélectionne la prochaine ville selon la règle probabiliste
int choisir_prochaine_ville(int courante, int visitees[N_VILLES], int nb_visitees) {
double probas[N_VILLES];
double somme = 0.0;
// Calcul des numérateurs
for (int j = 0; j < N_VILLES; j++) {
if (!est_visitee(j, visitees, nb_visitees)) {
double pheromone = pow(pheromones[courante][j], ALPHA);
double heur = pow(heuristique[courante][j], BETA);
probas[j] = pheromone * heur;
somme += probas[j];
} else {
probas[j] = 0.0;
}
}
// Normalisation
if (somme == 0.0) return -1;
for (int j = 0; j < N_VILLES; j++) {
probas[j] /= somme;
}
// Choix probabiliste
double r = (double)rand() / RAND_MAX;
double cumul = 0.0;
for (int j = 0; j < N_VILLES; j++) {
if (probas[j] > 0) {
cumul += probas[j];
if (r <= cumul) return j;
}
}
return -1;
}
// Construction d'une tournée par une fourmi
void construire_tournee(int tournee[N_VILLES], double *longueur) {
int visitees[N_VILLES];
int nb_visitees = 0;
// Départ aléatoire
tournee[0] = rand() % N_VILLES;
visitees[0] = tournee[0];
nb_visitees = 1;
// Construction
for (int i = 1; i < N_VILLES; i++) {
int courante = tournee[i - 1];
int prochaine = choisir_prochaine_ville(courante, visitees, nb_visitees);
tournee[i] = prochaine;
visitees[nb_visitees] = prochaine;
nb_visitees++;
}
*longueur = calculer_longueur(tournee);
}
// Mise à jour des phéromones
void mettre_a_jour_pheromones(int tournees[N_FOURMIS][N_VILLES],
double longueurs[N_FOURMIS]) {
// Évaporation
for (int i = 0; i < N_VILLES; i++) {
for (int j = 0; j < N_VILLES; j++) {
pheromones[i][j] *= (1 - RHO);
}
}
// Dépôt
for (int k = 0; k < N_FOURMIS; k++) {
double depot = Q / longueurs[k];
for (int i = 0; i < N_VILLES - 1; i++) {
int a = tournees[k][i];
int b = tournees[k][i + 1];
pheromones[a][b] += depot;
pheromones[b][a] += depot;
}
int a = tournees[k][N_VILLES - 1];
int b = tournees[k][0];
pheromones[a][b] += depot;
pheromones[b][a] += depot;
}
}
// Algorithme ACO principal
void ant_colony_optimization(int meilleure_tournee[N_VILLES], double *meilleure_longueur) {
int tournees[N_FOURMIS][N_VILLES];
double longueurs[N_FOURMIS];
initialiser_heuristique();
initialiser_pheromones();
*meilleure_longueur = 1e9;
for (int iteration = 0; iteration < MAX_ITERATIONS; iteration++) {
// Construction des tournées
for (int k = 0; k < N_FOURMIS; k++) {
construire_tournee(tournees[k], &longueurs[k]);
if (longueurs[k] < *meilleure_longueur) {
*meilleure_longueur = longueurs[k];
for (int i = 0; i < N_VILLES; i++) {
meilleure_tournee[i] = tournees[k][i];
}
}
}
// Mise à jour des phéromones
mettre_a_jour_pheromones(tournees, longueurs);
if (iteration % 10 == 0) {
printf("Itération %d: meilleure longueur = %.2f\n", iteration, *meilleure_longueur);
}
}
}
int main() {
int meilleure_tournee[N_VILLES];
double meilleure_longueur;
srand(time(NULL));
ant_colony_optimization(meilleure_tournee, &meilleure_longueur);
printf("\n=== Résultats ===\n");
printf("Meilleure tournée trouvée : ");
for (int i = 0; i < N_VILLES; i++) {
printf("%d ", meilleure_tournee[i]);
}
printf("\n");
printf("Longueur totale : %.2f\n", meilleure_longueur);
return 0;
}
Itération 0: meilleure longueur = 95.00 Itération 10: meilleure longueur = 85.00 Itération 20: meilleure longueur = 80.00 Itération 30: meilleure longueur = 80.00 Itération 40: meilleure longueur = 80.00 Meilleure tournée trouvée : [0, 1, 4, 3, 2] Longueur totale : 80.00
Variantes de l'algorithme ACO
Plusieurs variantes de l'algorithme ACO ont été développées pour améliorer les performances :
домаћинствима confinée entre \([ \tau_{min}, \tau_{max} ]\) pour éviter la stagnation домаћинствима phéromone supplémentaire pour la meilleure tournée globale
| Variante | Description | Caractéristique principale |
|---|---|---|
| Ant System (AS) | Version originale (Dorigo, 1992) | Toutes les fourmis déposent des phéromones |
| Ant Colony System (ACS) | Version améliorée | Seule la meilleure fourmi dépose des phéromones ; règle de transition pseudo-aléatoire |
| Max-Min Ant System (MMAS) | Contrôle des bornes de phéromone | |
| Elitist Ant System | Renforcement des meilleures solutions |
Applications des algorithmes ACO
Applications
- Problèmes de routage : TSP, problèmes de tournées de véhicules (VRP), routage dans les réseaux.
- Ordonnancement : flow shop, job shop, planification de tâches.
- Optimisation de réseaux : routage dans les télécommunications, équilibrage de charge.
- Bioinformatique : alignement de séquences, repliement des protéines.
- Intelligence artificielle : classification, clustering, apprentissage.
- Ingénierie : conception de circuits, optimisation de structures.
Avantages et limites
- Robustesse : efficace pour une large classe de problèmes d'optimisation combinatoire.
- Adaptabilité : peut s'adapter à des environnements dynamiques.
- Exploration efficace : équilibre entre exploration et exploitation via les paramètres \(\alpha\) et \(\beta\).
- Mémoire collective : les phéromones représentent une mémoire distribuée de la qualité des solutions.
- Parallélisable : les fourmis peuvent travailler indépendamment.
- Convergence prématurée : risque de stagnation si les phéromones deviennent trop dominantes.
- Réglage des paramètres : performance sensible aux valeurs de \(\alpha, \beta, \rho, m\).
- Complexité temporelle : plus coûteux que certaines heuristiques gloutonnes.
- Problèmes de grande taille : peut nécessiter des améliorations (MMAS, ACS).
Paramètres clés de l'algorithme ACO
| Paramètre | Rôle | Valeurs typiques |
|---|---|---|
| \(\alpha\) (importance phéromone) | Contrôle l'influence des traces existantes | [0.5, 2] (souvent 1) |
| \(\beta\) (importance heuristique) | Contrôle l'influence des distances locales | [1, 5] (souvent 2 ou 3) |
| \(\rho\) (taux d'évaporation) | Vitesse d'oubli des anciennes traces | [0.1, 0.9] (souvent 0.5) |
| \(m\) (nombre de fourmis) | Nombre d'agents constructeurs | \(n\) (nombre de villes) à \(2n\) |
| \(Q\) (constante de dépôt) | Amplitude du dépôt de phéromone | [10, 1000] (souvent 100) |
- \(\beta > \alpha\) : généralement recommandé pour privilégier l'information heuristique (distance) au début.
- Évaporation modérée : \(\rho = 0.5\) est un bon point de départ.
- Nombre de fourmis : environ égal au nombre de villes pour les petits problèmes, plus pour les grands.
- Utiliser MMAS pour éviter la stagnation sur les problèmes difficiles.
Comparaison avec le recuit simulé
| Critère | ACO (Colonies de fourmis) | Recuit simulé |
|---|---|---|
| Inspiration | Comportement social des fourmis | Métallurgie (recuit des métaux) |
| Population | Multiple agents (fourmis) | Solution unique |
| Mémoire | Mémoire collective (phéromones) | Aucune mémoire explicite |
| Parallélisme | Naturellement parallélisable | Séquentiel par nature |
| Exploration | Via paramètres \(\alpha\), \(\beta\) et évaporation | Via la température \(T\) |
| Applications typiques | Routage, TSP, problèmes de graphes | Optimisation combinatoire générale |
L'essentiel en bref
- L'optimisation par colonies de fourmis (ACO) est une métaheuristique inspirée du comportement des fourmis réelles.
- Les fourmis déposent des phéromones sur les chemins parcourus ; plus un chemin est court, plus il reçoit de phéromones.
- La règle de transition probabiliste combine phéromone (\(\tau_{ij}\)) et information heuristique (\(\eta_{ij} = 1/d_{ij}\)).
- L'évaporation des phéromones évite la convergence prématurée.
- Les paramètres clés : \(\alpha\) (importance phéromone), \(\beta\) (importance heuristique), \(\rho\) (taux d'évaporation).
- Variantes : ACS, MMAS, Elitist AS.
- Applications majeures : TSP, routage, ordonnancement, bioinformatique.
L'optimisation par colonies de fourmis a été développée par Marco Dorigo dans sa thèse de doctorat à l'Université Libre de Bruxelles en 1992. Le premier algorithme, appelé Ant System (AS), a été appliqué au problème du voyageur de commerce. Depuis, cette approche a été étendue à de nombreux problèmes d'optimisation et a donné naissance à tout un champ de recherche sur les algorithmes à comportement social (swarm intelligence). En 2016, Marco Dorigo a reçu le prestigieux prix ERC Advanced Grant pour ses travaux sur l'intelligence collective.
Discussion (0)
Soyez le premier à laisser un commentaire !
Laisser un commentaire
Votre commentaire sera visible après modération.