Evaluer un algorithme approché

Nous avons déjà parlé de correction, de terminaison, de complexité… Mais pour les algorithmes approchés une autre notion importante est celle de précision.

Pourquoi des algorithmes approchés ?

Quand un algorithme doit répondre à un problème, il ne peut qu’avoir raison ou tort. Un algorithme approché ne donne pas nécessairement la réponse au problème, mais peut-être une réponse “acceptable”. En pratique trouver une réponse acceptable peut parfois se faire en un temps beaucoup moins important que de trouver la bonne réponse.

Les algorithmes approchés posent le problème du compromis performance-précision. Autrement dit : à quel point je gagne en vitesse d’exécution par rapport à ce que je perds en qualité de ma réponse ?

Précision d’un algorithme approché

Du fait de la très grande diversité des algorithmes et des données qu’ils manipulent, il est très difficile de donner un cadre formel universel à la notion de précision.

Prenons l’exemple d’un problème dont la solution est un entier. La précision d’un algorithme approché répondant à ce problème peut alors être l’écart moyen entre sa réponse et la bonne réponse attendue.

Comment évaluer la précision d’un algorithme approché ?

Déjà, pour évaluer la précision, il est nécessaire d’avoir une idée de la réponse attendue qui peut-être donnée par un algorithme exact. Ensuite l’idée est de tester un grand nombre de cas et d’en tirer des informations statistiques utiles.

Bien évidemment, il n’est pas forcément possible de se comparer à la réponse exacte (sinon on n’utiliserait pas d’algorithme approché mais l’algorithme exact correspondant). On peut alors évaluer la précision de l’algorithme approché en se basant sur des critères mathématiques, comme par exemple la connaissance de l’appartenance du résultat à un certain intervalle.

Exemple de la coloration de graphes

Algorithmes

Nous allons considérer un exemple. On appelle coloration d’un graphe la donnée d’un entier associé à chacun de ses sommets. Ces entiers sont appelés les couleurs des sommets. Ces entiers peuvent être identiques. Une coloration est dite propre si tous sommets reliés par une arête sont de couleurs différentes. Le nombre chromatique d’un graphe est le nombre minimum de couleurs qui apparaissent dans une coloration propre.

Ce problème est un exemple connu de problème NP-Complet. Une façon de le résoudre de façon exacte consiste à énumérer l’ensemble des colorations possibles à 1 couleur, puis 2 couleurs etc jusqu’à trouver une coloration propre.

Le code Python suivant résout le problème :

Dans cet exemple, le graphe utilisé est le suivant (représenté sous forme d’un tableau de listes) :

012345

Si 0 correspond à bleu, 1 correspond à rouge et 2 correspond à vert, on a comme résultat la coloration suivante :

012345

Cet algorithme est très complexe et une solution approchée bien connue consiste à trier les sommets par nombre de voisins décroissant, à les colorer dans cet ordre en choisissant la plus petite couleur positive laissant la coloration propre. Cet algorithme approché est décrit ci-dessous :

Cet algorithme est beaucoup moins complexe que le précédent, et permet de considérer de très grands graphes.

Simulations

Afin d’évaluer la qualité de notre algorithme approché, nous allons nous intéresser à deux choses : le temps de calcul d’un côté, et la précision de l’autre. Pour tester ces deux quantités, nous allons générer un grand nombre de graphes aléatoires en utilisant le programme suivant :

Un programme similaire est utilisé pour le test exhaustif.

Temps de calcul

Pour connaître les performances en temps, nous utilisons la fonction “time” de linux :

La première ligne : “3.37” est le résultat de notre algorithme. Ensuite on peut lire différents temps. Le premier : “15.88s” correspond au temps consommé par l’utilisateur sur votre machine pour cette tâche, le second : “0.00s” au temps utilisé par le système et le dernier “0:15.89″ au temps vraiment écoulé. C’est le premier qui nous intéresse ici. Sur beaucoup de systèmes, la commande time peut être utilisée directement et est équivalente à “/usr/bin/time -p”. Le résultat est alors présenté autrement :

Notons que ce temps prend en compte la génération des graphes. Une autre façon de procéder est d’utiliser directement des primitives de python.

Pour utiliser directement Python, une solution est de prendre le temps avant de lancer les simulations, et une fois simulé.

On modifie donc notre code :

Génération des courbes

Afin d’automatiser tout cela, nous allons passer les arguments par ligne de commande au programme python. Il suffit de modifier les lignes :

Maintenant on peut directement fournir la taille du graphe et le nombre de tests au programme :

On va maintenant lancer un script pour générer notre courbe :

Il ne reste plus qu’à l’afficher !

Exemple avec Gnuplot :

On obtient ceci :

On remarque que la courbe n’est pas très lisse, c’est parce qu’un nombre insuffisant de simulations ont été faites.

Au final, on obtient les courbes suivantes :

precision

On remarque que ces courbes ne mettent pas vraiment en avant l’erreur faite par l’algorithme approché. Le soucis étant que faire tourner l’algorithme exhaustif sur de gros graphes demanderait trop de temps de calcul.

Pour mieux évaluer l’erreur de l’algorithme approché, on se propose donc de le faire tourner sur des graphes dont on connaît mathématiquement le nombre chromatique : des graphes bipartis. En effet les graphes bipartis possédant au moins une arête ont un nombre chromatique égal à deux.

On obtient les courbes suivantes :

Ces courbes ont été générées avec le script Gnuplot suivant :

Publié le

Laisser un commentaire