Graphe - Cycle et voyageur de commerce
Exercice 1
- Une première version naïve serait d'utiliser un algortihme de type brute force. On part des sommets et à chaque itération on appelle l'algorithme sur les arêtes possibles etc. Cela nous donne possibilités, soit la pire complexité possible. Pour résoudre cet algorithme il faut un moyen de générer les permutations, l'algorithme de heap itératif est utilisable.
// Pour la version itérative, on simule le processus récursif en mémorisant les indices de boucle dans un tableau compteur.
// compteur[k] représente, dans cette simulation, l'indice courant dans la boucle de Heap(k,A).
Heap_iteratif(n : entier, A : tableau) :
compteur := tableau de n entiers, initialisés à 0
écrire A
// i indique le niveau de la boucle en cours d'incrémentation
i := 0;
Tant que i < n :
Si compteur[i] < i :
Si i est pair :
échanger A[0] et A[i]
Sinon :
échanger A[compteur[i]], A[i]
Fin Si
écrire A
compteur[i] += 1 // on incrémente l'indice de boucle après avoir effectué une itération
i := 0 // on retourne au cas de base de la version récursive
Sinon :
// la boucle de niveau i est terminée, on peut donc réinitialiser l'indice et retourner au niveau supérieur
compteur[i] := 0
i += 1
Fin Si
Fin Tant que
- On peut appliquer la programmation dynamique pour tenter de diminuer la complexité. L'idée est de décomposer le graphe en des sous ensemble de sommet, pour chaque sous ensemble on vérifie pour chaque sommet de chaque sous ensemble s'il existe une chaine couvrant les sommets de et se terminant en
Exercice 2
Nous pouvons utiliser le cours et le théorème de Dirac pour le montrer.
Théorème de Dirac
: Un graphe à sommets (avec ) dont chaque sommet
est au moins de degré est hamiltonien.
Pour rappel un graphe complet est un graphe simple dont tous les sommets sont adjacents deux à deux. Chaque sommet à donc un degrés de pour . Ainsi tout graphe complet est hamiltonien.
Exercice 3
Comme dans l'exercice 1 nous avons discriminé deux méthodes pour déterminer si
un cycle est hamiltonien nous pouvons aussi discriminer deux méthodes exactes
pour résoudre le problème du voyageur de commerce. Dans un premier temps nous
pouvons utilser un pseudo code naïf. Cet algorithme à une complexité de O(n!)
,
plus précisément (n-1)!/2
si on pense à ne pas recalculer ce que l'on a déjà
calculé. On admet qu'il existe une fonction permutation
permettant de fournir une permuttation d'un vecteur si il en existe une, zéro si elle ont toutes déjà été donnée.
Entrée : N vecteur de villes de taille n et une liste de coûts c(i,j)
Sortie : Liste de villes et coût total
Cout=0 //Cout total du chemin
ordre_permutation=0 //permet de retrouver le numéro de permutation pour retrouver la permutation
pour i allant de 0 à n
tant que permutation(N)!=0
poids_chemin_courant=0
k=0
pour i allant de 0 à n
poids_chemin_courant=c(k,N[i])
k=N[i]
poid_chemin_courant+=c(k,0)
cout=min(cout,poids_chemin_courant)
ordre_permutation+=1;
retourner cout et ordre_permutation
Un autre algorithme pouvant être mis en place est un algorithme de programmation dynamique, pour comprendre comment cela est possible il faut réecrire l'équation de récurrence qui nous permet de choisir une ville. Soit un ensemble de ville à visiter . On note le chemin optimisé traversant toutes les villes de se terminant par la ville et que nous voulons trouver la meilleur ville suivante nous devons trouver
\begin{equation} chemin(C,j)=\min\{chemin(C\backslash\{j\},k)+c(k,j) \; \text{avec} \; k \in C\}\end{equation}
A partir de cela nous pouvons remarquer une autre conséquence de la construction du problème en effet le coût optimal pour résoudre le problème consiste désormais à trouver la valeur de, notant l'ensemble des villes privées du point de départ
\begin{equation}solution = \min\{chemin(N,j)+c(j,x)\}\end{equation}
A partir de ces valeurs l'équation récursive peut être utilisée pour construire les valeurs pour tous les et , en parcourant l'ensemble à deux éléments, puis des ensemble à trois éléments etc jusqu'à obtenir un ensemble à éléments. Nous pouvons ensuite calculer grâce à ce procédé. Ensuite nous pouvons dérouler l'algorithme en trouvant une ville telle que , puis identifier une ville etc
Entrée : Un ensemble de ville V, un point de départ v et un tableau c(i,j) permettant de donner le coup de la ville i à j
Sortie : Un chemin de coût minimal
Data[]=infty
Path[]
pour tout w dans V
Data({w},w)=c(v,w)
P({w},w)=v
pour tout i allant de 2 à n
pour tout S inclus dans V tel que |S|=i
pour tout w dans S
pour tout u dans S
z=Data(S\{w},u)+c(u,w)
si z<Data(S,w)
D(S,w)=z
Path(S,w)=u
retourner Path
La complexité de ce problème est en O(n^2 2^n)
Exercice 4
On se place dans le cas où les coûts sont des déplacements et donc
Entrée : G=(V,E) un graphe complet et c(i,j) une fonction donnant le coup entre les villes i et j
Sortie : un chemin de coût minimal
Choisir un sommet r dans V comme ville initiale
Construire un arbre couvrant minimal T dans G à partir de r via l'algorithme de Krustal
Retourner le cycle hamiltonien H qui visite les sommets dans l'ordre trouvé par l'algorithme de Krustal
On veut maintenant prouver que l'algorithme est un algorithme d'approximation de facteur 2. On note la tournée optimale et la tournée courante. On cherche donc à montrer que . Soit l'arbre couvrant minimal, on a (puisque C est construit avec l'algorithme de Krustal, C privé de l'arète qui forme le cycle est un arbre). Si l'on note la liste formé par le parcours de on obtient car chaque arête est visitée deux fois. On remarque que l'on peut supprimer un sommet de (facilement mis en évidence avec un arbre à 3 sommets). Ainsi et donc . L'algorithme est donc un algorithme d'approximation de facteur 2.