You are on page 1of 2

Devoir - Voyageur de commerce et arbre couvrant de poids minimum

Les graphes considérés dans ce sujet sont non orientés.

1 Approche du problème
1.1 Problème du voyageur de commerce
On se donne n villes v0 , . . . , vn−1 , chaque ville étant reliée à chaque autre ville par une route dont la longueur est
connue. L’objectif est de construire un circuit élementaire : un chemin qui passe par chaque ville une fois et une seule
et qui revient à la ville de départ. On représente informatiquement le circuit par un tableau des n entiers distincts de
J0, n − 1K, la ville de départ n’est pas répétée à la fin du circuit dans la représentation.
Exemple. Avec n = 4, le circuit v2 → v0 → v1 → v3 → v2 sera représenté par le tableau [2, 0, 1, 3]
On s’intéresse, dans ce sujet, à la version planaire, donc les villes ont exactement deux coordonnées, et euclidienne
du problème c’est-à-dire qu’on suppose que toutes les villes sont connectées deux à deux par une route de longueur
égale à la distance euclidienne qui les sépare.
On représente une instance du problème par le tableau des coordonnées des villes.
1. Calculer le nombre de circuits élémentaires différents en fonction de n. Est-il envisageable d’écrire un programme
qui teste tous les circuits possibles pour n ≈ 100 ?

1.2 Génération aléatoire d’une instance du problème et d’une solution


2. En utilisant la fonction random() du module random qui renvoie un nombre flottant tiré uniformément dans l’in-
tervalle [0,1[, écrire une fonction villes_aleatoires(n) qui renvoie un tableau de n villes, chaque ville étant repré-
sentée par un couple de coordonnées aléatoires chacune tirée uniformément dans l’intervalle [0,1[.
3. En utilisant la fonction randrange(n) du module random qui renvoie un entier dans l’intervalle J0, n − 1K, écrire
une fonction circuit_aleatoire qui prend en argument le nombre de villes et qui renvoie un circuit aléatoire tiré
équiprobablement parmi l’ensemble des circuits possibles.

2 Fonctions outils
2.1 Calcul de longueur
4. Ecrire une fonction distance(ville1, ville2) qui prend en argument deux villes (représentées chacune par un
couple de coordonnées) et qui renvoie la distance euclidienne qui les sépare.
5. Ecrire une fonction matrice_des_distances qui prend en argument un tableau de villes et qui renvoie un tableau
de tableaux de distance matrice tel que matrice[i][j] contienne la distance euclidienne entre la ville d’indice i et
la ville d’indice j. Déterminer la complexité de la fonction en fonction du nombre de villes.
6. Ecrire une fonction longueur(matrice, circuit) qui prend en argument la matrice des distances et un circuit
et qui renvoie la longueur du circuit, c’est-à-dire la somme des longueurs des routes empruntées. Déterminer la
complexité de la fonction en fonction du nombre de villes.
7. Programmer un algorithme de tri (si possible de complexité quasi-linéaire).

3 Algorithme glouton
On cherche à construire une solution en effectuant localement le choix qui semble le meilleur. Plus précisément,
on construit le circuit de façon itérative en partant de la première ville puis en choisissant la prochaine ville qui est la
plus proche parmi les villes restantes à visiter.
Pour programmer cette fonction, on définira un tableau des indices des villes non visitées duquel on supprimera
au fur et à mesure les villes que l’on visite. Pour supprimer efficacement un indice de ville présent dans le tableau, on
remplacera cet indice par le dernier du tableau et on supprimera à la place la dernière case du tableau.
8. Ecrire une fonction glouton(distances) qui prend en argument la matrice des distances et renvoie le circuit
généré par l’algorithme décrit.

1
Devoir - Voyageur de commerce et arbre couvrant de poids minimum

9. Le résultat de l’algorithme précédent peut faire apparaître des croisements (comme sur la figure). En considérant
les 4 villes qui sont aux extrémités de deux segments du circuit qui se croisent, justifier qu’on peut reconstruire un
circuit plus court (en décroisant le croisement considéré). Quantifier le gain total de longueur en fonction des distances
entre les 4 villes considérées et déduire de cette quantification un critère pour savoir si deux segments se croisent.
10. Ecrire une fonction renverse_entre_indices(tableau, i, j) qui renverse une partie du tableau en argument :
la partie entre les indices i et j inclus. La fonction doit procéder par effet de bords.
Exemple. Si tableau = [2, 3, 1, 5, 6, 4], i = 1, j = 4, la fonction doit modifier le tableau en : [2, 6, 5,
1, 3, 4].

11. Ecrire une fonction decroise(distances, circuit) qui décroise un circuit tant qu’il possède des croisements.

4 Arbre couvrant de poids minimum


Un arbre couvrant pour un graphe connexe donné G = (S,A) est un sous-graphe G0 = (S,A0 ) tel que A0 ⊂ A et
G0 est connexe et sans cycle. Le poids de l’arbre couvrant est la somme des poids des arêtes de A0 . On cherche dans
cette partie à déterminer l’arbre couvrant de poids minimum.
Pour déterminer un tel arbre, on va utiliser un algorithme glouton qui est optimal pour le problème (ie il donne
effectivement l’arbre couvrant de poids minimum). On part d’un graphe G0 = (S,∅) et on considère les arêtes appar-
tenant à A en les parcourant par poids croissants. Lorsqu’on examine une arête de A, on l’ajoute à G0 si et seulement
si elle ne crée pas de cycle.
Pour déterminer la présence de cycles, on itère sur chaque sommet. Si un sommet n’est pas encore visité, on
réalise un parcours en profondeur en marquant comme visités tous les sommets rencontrés durant le parcours. Si au
cours du parcours, on rencontre un sommet voisin du sommet en cours de visite (et qui n’est pas le sommet précédent)
et qui a déjà été visité, alors on a détecté un cycle.
Dans cette partie, on représente un graphe par des listes d’adjacence.
12. Ecrire une fonction qui détermine si un graphe est acyclique. Donner la complexité en justifiant rapidement.
13. Montrer qu’un arbre couvrant n sommets a exactement n − 1 arêtes.
14. Programmer l’algorithme de construction de l’arbre couvrant de poids minimum.
15. Déterminer sa complexité.
16. Justifier qu’il est optimal.
Remarque. L’arbre couvrant de poids minimum peut être utilisé de deux manières pour le problème du voyageur
de commerce. Il peut servir d’heuristique pour un algorithme exact dit Séparer et évaluer permettant de limiter le
nombre de circuits concrètement considérés pour la recherche du plus court circuit. Il peut également être utilisé
pour un algorithme d’approximation pour le voyageur de commerce euclidien appelé Algorithme de Christofides :
l’algorithme garantit alors que le circuit obtenu est au pire 50 % plus long que le circuit optimal.

You might also like