\item[nearest\_neig.py] Une gloutonne qui consiste à partir d'un noeud aléatoire, choisir le plus proche voisin de ce noeud, et itérer ce procédé (en considérant seulement les voisins non encore visité).
\item[heuristic\_kruskal.py] Une autre qui part d'un arbre couvrant minimal et qui construit le cycle associé au parcours en profondeur de cet arbre.
La première des deux heuristiques est la meilleure dans la grande majorité des exemples que nous avons testé. C'est donc elle que nous utilisons pour initialiser la recherche dans le programme linéaire dual.
En dehors de cela, nous avons également codé~:
\begin{description}
\item[parser.py] S'occupe de charger les données de TSPLIB et de produire les matrices d'adjacence.
\item[exact.py] Résolution exacte du voyageur de commerce en $O(n^22^n)$, fonctionne pour les instances de taille inférieure ou égale à $24$ sans problème.
\item[min\_cut.py] Implémente l'algorithme de Stoer-Wagner pour calculer une coupe minimum.
\item[separation.py] Implémente la méthode du primal pour trouver une borne inférieure.
\item[separation\_dual.py] Implémente la méthode du dual.
* : pour ces deux tests, le solver LP que nous utilisons (scipy.otimize) a eu un problème. La valeur que nous donnons est donc la dernière borne inférieure prouvée que nous avons.
Même avec les contraintes supplémentaires sur les coupes, le programme linéaire n'est pas entier~: en effet, considérons le graphe suivant~:
\begin{center}
\begin{tikzpicture}[,auto ,node distance =2 cm and 3cm ,on grid ,
semithick ,
state/.style ={ circle ,top color =white , bottom color = white ,
draw,, minimum width =1 cm}]
\node[state] (C) {$1$};
\node[state] (A) [above left=of C] {$0$};
\node[state] (B) [above right =of C] {$2$};
\node[state] (D) [below=of C] {$3$};
\node[state] (E) [below left=of D] {$4$};
\node[state] (F) [below right=of D] {$5$};
\path (C) edge node {$1 | 1/2$} (A);
\path (C) edge node {$1 | 1/2$} (B);
\path (B) edge node {$1 | 1/2$} (A);
\path (C) edge node {$0 | 1$} (D);
\path (A) edge node {$0 | 1$} (E);
\path (B) edge node {$0 | 1$} (F);
\path (E) edge node {$0 | 1/2$} (D);
\path (F) edge node {$0 | 1/2$} (E);
\path (D) edge node {$0 | 1/2$} (F);
\end{tikzpicture}
\end{center}
Les annotations $a|b$ sur les arêtes siginifient que le coût de l'arête est de $a$, et que la variable $x_e$ correspondante a pour valeur $b$ dans la solution optimale du programme linéaire. On s'aperçoit que la valeur optimale de ce programme linéaire est donc de $3/2$~: il ne peut donc pas être entier.
On peut également remarquer que pour toute coupe $S$ de $V$, et pour toute solution entière $x$ du problème initial, $\sum_{e\in\delta(S)} x_e$ est paire. En particulier, si $|\delta(S)|$ est impair, $\sum_{e\in\delta(S)} x_e \leq |\delta(S)| -1$. On vient donc d'obtenir une classe d'inégalités linéaires satisfaites par toute solution entière du problème, mais pas par les solutions fractionnaires comme on a pu le constater avec l'exemple ci-dessus.