Projet BIO V10
Projet BIO V10
Projet BIO V10
UNIVERSITE D’ALGER1
Méthodes Bio-Inespérées
RAPPORT
Réalisé par :
- AICHOUBA Yasmine
- AKKOUCHE Imene
- MERMI Amina
0
SOMMAIRE :
Le problème de satisfiabilité (problème SAT) est un problème de décision NP-complet. Il consiste à déterminer
si une formule propositionnelle mise sous forme de conjonction est satisfaite.
Ce projet sera divisé en trois parties, la première consistera à implémenter deux solutions qui sont la recherche
en profondeur et l’algorithme A* avec une heuristique qui priorise l’instanciation des variables qui sont présentes
dans le nombre maximum de clauses (vue en TD). La deuxième partie sera réservée pour l’implémentation d’une
solution en utilisant les algorithmes génétiques. Finalement, la troisième partie sera dédiée à l’implémentation d’une
solution en utilisant l’algorithme ACS. Les structures utilisées seront expliquées et les résultats de chaque
implémentation commentés. Des tests seront faits sur des fichiers benchmarks afin de comparer entre ses différentes
approches.
2. OBJECTIF :
L’objective principale de notre projet est l’implémentation de différentes approches existant pour la
résolution du problème SAT.
3. ENVIRONEMENT DE TRAVAIL :
Les tests ont été effectués sur une machine PC avec les caractéristiques suivante :
Afin d'implémenter une solution au problème SAT, il est nécessaire de définir des structures de base pour
ce dernier afin de faciliter la tâche d'implémentation des diffèrent algorithmes.
REPRESENTATION DU LITTERALE :
La représentation d’un littéral qu’on a caractérisé par son numéro (un entier entre 1 et 75), sa valeur initialiser
à « -1 » et qu’on va instancier après soit « 0 » ou « 1 » et son état qui est soit positive « 1 » soit négative « 0 ». A
été implémentée dans la classe «Literale». Elle contient :
Des lignes de commentaire identifiées par « c » qui indiquent les caractéristiques de l’instance donnée qu’on
va ignorer.
Une ligne du problème identifiée par « p » qui contient le nombre de variables suivies par le nombre de
clauses qu’on va récupère.
L’ensemble des clauses qui se compose d’un ensemble de littéraux (positifs pour vrai, négatif pour faux)
qu’on va récupérer. On remarque que la clause se termine par la valeur "0" qui indique la fin de celle-ci.
Le caractère "%" qui marque la fin du fichier.
Dans l’application développée la lecture du fichier commence après avoir sélectionné le chemin du fichier, en
appuyant sur le bouton « Valider ». Cette dernière est faite à l’aide d’un lecteur « BufferedReader » qui lit notre
fichier ligne par ligne en utilisant la méthode « readline » jusqu’à la fin du fichier déterminer par le symbole «%».
Lors de la lecture des traitements comme la vérification des lignes si elles sont des lignes de commentaire qu’on va
ignorer ou une ligne de problème d’où on va extraire le nombre de littérale et le nombre de clauses à l’aide des deux
fonction « trim() » et « split() » sinon si notre ligne représente une clause des traitements telle que l’extraction et
la création des littérales et clauses sont effectuées afin de pouvoir les exploiter dans les algorithmes générateurs de
solution. La lecture se termine dès qu’on arrive au symbole « % » et on ferme le lecteur à l’aide de la fonction
«close()» après avoir terminé d’extraire toutes les clauses du fichier.
5. IMPLEMENTATION :
PARTIE 1 :
Dans cette partie ce qui est demandé et d’implémenter une méthode aveugle qui est l’algorithme de recherche
en profondeur et une méthode heuristique à savoir l’algorithme A* pour résoudre le problème de satisfiabilité
(SAT).
RECHERCHE EN PROFONDEUR (DFS) :
STRUCTURES ET IMPLEMENTATION
L’algorithme DFS se base dans notre cas sur la sélection décroissante des littérales pour construire l’arbre de
recherche. On ajoute un seuil (qui représente l’ordre de l’exécution des nœuds) pour ne pas perdre de temps en cas
où on remarque que même si on descend plus profond il n’y aura pas d'amélioration en matière de qualité de solution.
La sélection du seuil se fait d’après le meilleur résultat de plusieurs essayes.
2. On crée un nœud pour garder le littéral courant avec la valeur instanciée (on répète deux fois le même
processus avec les valeurs 0 et 1 mais pas en même temps, cela dépend du résultat du littéral sélectionné) et
après avoir défini leur seuil on les insère dans la liste « Ouverte ».
3. On supprime le dernier élément de la file « Ouverte » et on calcule le nombre de clauses satisfaites par cette
solution avec la méthode « Nombre_Clauses_SAT » pour vérifier si on arrête la recherche (dans le cas où on
a atteint le nombre maximal des clauses) sinon on continue notre recherche.
4. On stocke après chaque vérification la solution et le nombre de clauses satisfaite par cette solution pour pouvoir
retourner la meilleure solution trouvée.
5. On n'oublie pas de vérifier à chaque fois que la file « Ouverte » n’est pas vide et qu’on n’a pas atteint le seuil
déjà précisé.
EXPERIMENTATIONS ET PERFORMANCES
On calcule la performance sur 10 fichiers du benchmark uf75 et uuf75, tout en maintenant un nombre
d’essais qui est égal à 5 et on teste plusieurs seuils. Les résultats sont présentés ci-dessous :
Après avoir testé plusieurs valeurs de seuil on a trouvé que les meilleurs résultats obtenus sont avec
un seuil égal à 50 000 car celui-ci satisfait 91% (uf75) et 90% (uuf75) de clauses en temps moyen égal à
388 ms (uf75) et 542 ms (uuf75) et prend 36 MO (uuf75) et 47 MO (uuf75) de la mémoire.
700
Taux de clause
600 satisfaite (uuf75) (uf75)
Taux de clause
500 satisfaite (uf75)
400 Temps d'execution (en
ms) (uf75)
300 Mémoire utilisées (en
MO) (uf75)
200
90 90 90 90 Temps d'execution (en
87 88 89
100 69 ms) (uuf75)
41 87 88 89 90 90 91 91
41 70 Mémoire utilisées (en
0
MO) (uuf75)
25 50 75 100 500 5000 10000 50000 100000
ALGORITHME A* :
STRUCTURES ET IMPLEMENTATION
L’algorithme A* se base dans notre cas sur la sélection du littéral selon une fonction fitness notée « f(n) =
g(n) + h(n) », tel que :
o g(n) : Le nombre de clauses satisfaites par le nœud courant et déjà satisfaites par son précédent.
o h(n) : Le nombre d’apparition d’un littéral d’un nœud dans l’ensemble des clauses.
Notre algorithme A* est implémenté dans la méthode « Parcours_A_etoile » de la classe « A_etoile ».
Cette méthode prend en entrée l’ensemble des clauses et retourne la solution sous forme de chaine de
caractère.
1) On insère dans la liste « Ouvert » le nœud « SAT » qui est initialisé à un ensemble vide « root ».
2) On vérifie que la liste « Ouverte » n'est pas vide (car si elle est vide on a aucun littéral à instancier), on
retire le premier nœud de la liste « Ouvert » et on l’enfile dans « Fermer ». Après on calcule le nombre
de clauses satisfaites de la solution généré « SAT » avec la méthode « Nombre_Clauses_SAT ».
3) S’il est égal au nombre de clauses on retourne la solution avec l’instanciation des littéraux et la complexité
empirique de l’algorithme.
4) Sinon on sélectionne le littéral avec la meilleure valeur de fitness (noter que ce dernier est instancié avec
la valeur qui a donné le meilleur cout) en appelons la méthode « Select_Best_Literal».
5) On crée un nœud pour le littéral sélectionner en le chainant avec son précédent et on l’ajoute à la liste
« Ouvert ».
6) Enfin si après l’instanciation de tous les littéraux on n’arrive pas à retrouve une solution qui satisfait tous
les clauses on retourne le nombre de clauses satisfiables avec l’instanciation des littéraux et la complexité
empirique de l’algorithme.
EXPERIMENTATIONS ET PERFORMANCES
On calcule la performance sur 10 fichiers du benchmark uf75 et uuf75 toutes en maintenant un nombre
d’essais qui est égal à 5. Les résultats sont présentés ci-dessous :
350
Nombre clause
A* (uuf75) 286 175 22 satisfiable
CONCLUSION :
On remarque que la recherche en profondeur est une méthode efficace mais nécessite une quantité de ressources
énorme et beaucoup de temps d’exécution par contre la recherche heuristique a pu réduire le temps d’exécution et
éviter l’explosion combinatoire et a donné une solution réalisable dans un temps acceptable pour notre problème
SAT.
PARTIE 2 :
Dans cette deuxième partie du projet on a implémenté une solution au problème de satisfiabilité (SAT) en
utilisant les Algorithmes Génétiques (GA).
Algorithme génétique:
STRUCTURES ET IMPLEMENTATION
L’algorithme génétique est implémenté dans la méthode « GA» de la classe «Recherche_GA». Cette
méthode prend en entrée le k-point et retourne la solution sous forme de chaine de caractère. Cette classe a
un constructeur qui initialise les paramètres suivants : la taille de la population, le nombre maximum
d’itérations, la probabilité de croisement, la probabilité de mutation et l’ensemble de clauses de notre
problème. Ces derniers sont entrés par l’utilisateur à partir de l’interface.
1. On créait et on initialise notre population aléatoirement en appelant la méthode
« Inisialisation_Pop_Aleatoire ».
2. Tant qu’on n’est pas arrivé à max_iter, on évalue notre population en calculant la probabilité de chaque
chromosome de la population et on passe à la sélection des parents les plus aptes en utilisant la sélection
par la roulette en faisant tourner la roue n/2 fois, n : étant la taille de notre population (on sélectionne la
moitié de notre population).
3. On génère les enfants grâce à un croisement a k-point suivi par une mutation faite aléatoirement si le nombre
généré par la fonction « random() » est inférieur à la probabilité de mutation et on attribue à chaque enfant
généré une valeur de fitness. Puis on insère les nouveaux individus(les enfants générer) dans la population.
4. On va ordonner les chromosomes de la nouvelle population générée selon la valeur de la fonction fitness
dans un ordre décroissant pour cela la fonction inverser de « collection.sort » est sollicité. Une évaluation
sera faite sur le premier chromosome possédant la meilleure valeur de fitness.
EXPERIMENTATIONS ET PERFORMANCES
On calcule la performance de l’algorithme génétique sur 10 fichiers du benchmark uf75 et uuf75 et on
maintient un nombre d’essais qui est égal à 5 tous en changeant la valeur des différents paramètres. Les résultats
sont présentés ci-dessous :
Fichier Max_iter P_croisement Taille P_mutation K_point Nombre Temps Mémoire Taux
population clause d’exécution utiliser satisfiabilité
satisfiable (en ms) (en MO)
Après plusieurs essais on sélectionne le paramètres qui nous on donner le meilleur nombre des clauses
satisfaites, temps d’exécution ainsi que celui qui utilise le moins d’espace mémoire.
o Max itération : 50
o P croisement : 2.0
o Taille population : 40
o P mutation : 0.2
o K-point : 35
350
324 323 321 322 322 323 322 323 323 323
300 Temps moyen
d’essai
250 209 (en ms)
200
200
139 131 131 129 139
150 110 114 110
100 99 100 10695 Mémoire utilisés
77 83 90
100 (en MO)
54
37
50
0
Nombre de
clauses satisfiable
350
322 322 321 322 320 321 321 321 322 323
300 Temps moyen
d’essai
250 (en ms)
192
200
150 135 126
110 111
100 95 97110 8699
106 100 91 8690
Mémoire utilisés
100 82 (en MO)
62 61
50 33
0
Nombre de
clauses satisfiable
PARTIE 3 :
Dans la troisième partie, une implémentation d’une solution pour le problème de satisfiabilité (SAT) en utilisant
l’algorithme ACS.
Algorithme ACS:
STRUCTURES ET IMPLEMENTATION
L’algorithme ACS a besoin d'un constructeur pour l'initialisation des paramètres empiriques : max
itérations, nombre de fourmis, alpha, beta, taux d’évaporation, q0, et la quantité initiale de la phéromone et
une classe « Fourmis » qui a besoin d'une liste des littéraux pour stocker la solution et un attribut pour
garder sa fitness et une méthode « Execution_ACS » qui va faire le traitement principale.
On commence par l'initialisation de la table de la phéromone après on génère un ensemble de fourmis pour
un certain nombre d'itération.
Chaque fourmis est initialisé par un état choisit aléatoirement avec à la méthode « random() » puis elle
fait appel à la méthode « Construction_solution » qui va tout d'abord sélectionner l'ensemble des littéraux
non instancier puis les envoyer à la méthode « Regle_transition » pour la construction de la solution en se
basant sur la règle de transition. Cette dernière a besoins de la méthode « Vecteur_probabilité » pour
calculer les probabilités.
Après que la construction est terminée une mise à jour en ligne de la phéromone sera faite pour chaque
fourmi.
Ensuite elle sera évaluer en calculant sa fitness (nombre clause satisfait par cette fourmis) puis on l'insert
dans l'ensemble des solutions.
Après la génération des solutions par les fourmis on sélectionne la meilleure fourmi pour faire la mise à
jour hors ligne et on l'inséré dans l'ensemble globale des meilleurs solutions.
Enfin cet ensemble globale sera trier dans l'ordre décroissant et on retourne la première solution contenu
dans cet ensemble.
EXPERIMENTATIONS ET PERFORMANCES
On calcule la performance de l’algorithme ACS sur 10 fichiers du benchmark uf75 et uuf75 et on maintient
un nombre d’essais qui est égal à 5 tous en changeant la valeur des différents paramètres. Les résultats sont présentés
ci-dessous :
ᵝ
Fichier Max Nombre Q0 Phéromone Nombre clause Temps Mémoire Taux
fourmis
⍺ 𝛕
iter initiale satisfiable d’exécution utilisé satisfiabilité
(en ms) (en MO)
Après plusieurs essais on sélectionne le paramètres qui nous on donner le meilleur nombre des clauses satisfaites,
temps d’exécution ainsi que celui qui utilise le moins d’espace mémoire
CONCLUSION :
Après la comparaison avec les précédentes approches. On a conclu que la méthode ASC est une méthode
efficace, ses résultat étaient très bon la majorité des clauses ont été satisfaites mais elle prend plus de temps par
rapport aux autres algorithmes.
6. COMPARAISON DES RESU LTATS:
Dans cette partie on va comparer entre les différents algorithmes implémentés et tester sur les dix premier
fichiers des deux benchmark uf75 et uuf75. Le résultat est présenté dans le schéma ci-dessous :
600
Temps moyen
500 d’essai
(en milliseconde)
400
Mémoire utilise
(en MO)
323 316 322 315
300 296 291 294 286
511,7
445 468,3
200 Nombre clause
388
satisfiable
100 175
164
115110 10196
36 20 50,5 47 22 49,2
0
DFS A* (uf75) GA (uf75) ACS DFS A* (uuf75) GA ACS
(uf75) (uf75) (uuf75) (uuf75) (uuf75)
7. INTERFACE GRAPHIQUE
La mise en œuvre de ce projet nous a permis de nous familiariser avec les méthodes aveugles (algorithmes de
recherche profonde), celles utilisant des heuristiques (algorithme A*) et des méta-heuristiques (algorithme
génétique et algorithme ACS), tout en les implémentant et en les testant. Il a été constaté que les métas heuristiques
sont utilisés afin de trouver un compromis entre les ressources utilisées, le temps d'exécution et la qualité de la
solution dans la résolution de problèmes NP-complets (dans ce cas le problème de satisfiabilité).