mini projet: résolveur de problèmes

10
NAWAR Oussema & SKHIRI Adel GL 4 GROUPE2 | Mini projet: Résolveur de problèmes INTELLIGENCE ARTIFICIELLE

Upload: independent

Post on 13-Mar-2023

0 views

Category:

Documents


0 download

TRANSCRIPT

NAWAR Oussema & SKHIRI Adel GL 4 GROUPE2 |

Mini projet: Résolveur de problèmes INTELLIGENCE ARTIFICIELLE

Avant-propos Les buts de ce projet étaient d'implémenter :

Un résolveur de problème de logique des prédicats d’ordre 1 pouvant fonctionner avec n'importe quelle base de connaissances en entrée.

un algorithme de recherche de solution sur un arbre en profondeur limité itérative

un algorithme de recherche de solution sur un arbre (ou graphe) de type A*

utiliser différentes heuristiques de façon à pouvoir comparer leur efficacité.

Réalisation Base de connaissances

La réalisation du projet résolveur nécessite tout d’abord de préparer la structure de données permettant de représenter le problème : base de règles, base de faits… Ainsi que de préparer les fonctions permettant de charger la base de connaissances du problème en mémoire dans notre structure de données.

L’approche adoptée pour représenter la base de connaissance :

La structure de données développées pour représenter la base de connaissance était conçus comme

suit :

Remarque : un attribut complexe doit être formé comme suit : x+(y-(3+z))

cruchesAetB (?x, ?y) et ?x + ?y ≥ 4 et ?y > 0 alors cruchesAetB (4, ?y − (4 − ?x))

Conditions (type : ensemble de GenericPredicats)

Résultat (type : predicat)

cruchesAetB (?x, ?y) et ?x + ?y ≥ 4

Predicat Item

?x , ?x + ?y

Attribut Simple Attribut

complexe

class baseDeConnaissances

Fait

~ explication: String

~ fait: GenericPredicat

~ indexer: int = 1

~ numero: String

+ equals(Object) : boolean

+ extraireFait(String) : Fait

+ Fait(String, GenericPredicat, String)

+ Fait()

+ getExplication() : String

+ getFait() : GenericPredicat

+ getIndexer() : int

+ getNumero() : String

+ hashCode() : int

+ profondeur() : int

+ setExplication(String) : void

+ setFait(GenericPredicat) : void

+ setIndexer(int) : void

+ setNumero(String) : void

+ toString() : String

Regle

~ conditions: GenericPredicat ([])

~ numero: String

~ resultat: GenericPredicat

+ extraireRegle(String) : Regle

+ getConditions() : GenericPredicat[]

+ getNumero() : String

+ getResultat() : GenericPredicat

+ Regle(String, GenericPredicat[], GenericPredicat)

+ setConditions(Predicat[]) : void

+ setNumero(String) : void

+ setResultat(Predicat) : void

+ toString() : String

class predicat

GenericPredicat

+ equals(Object) : boolean

+ extraire_predicat(String) : GenericPredicat

+ getAllAttributs() : List<Simple>

Item

~ attribut: Attribut

~ operateur: String

~ valeurs: String ([])

+ compare_item(Item) : boolean

+ getAttribut() : Attribut

+ getOperateur() : String

+ getValeurs() : String[]

+ Item(Attribut, String, String[])

+ setAttribut(Attribut) : void

+ setOperateur(String) : void

+ setValeurs(String[]) : void

+ toString() : String

Predicat

- attribut: List<Attribut>

- nom: String

+ getAttribut() : List<Attribut>

+ getNom() : String

+ Predicat(String, List<Attribut>)

+ setAttribut(List<Attribut>) : void

+ setNom(String) : void

+ toString() : String

class attribut

Attribut

~ principal: boolean

+ extraire_attribut(String, boolean) : Attribut

+ getAllAttributs() : List<Simple>

+ isPrincipal() : boolean

+ setPrincipal(boolean) : void

Complexe

~ attd: Attribut

~ attg: Simple

~ operateur: String

+ Complexe(boolean)

+ Complexe(Simple, Attribut, String)

+ getAttd() : Attribut

+ getAttg() : Simple

+ getOperateur() : String

+ setAttd(Attribut) : void

+ setAttg(Simple) : void

+ setOperateur(String) : void

+ toString() : String

Simple

~ attribut: String

+ getAttribut() : String

+ setAttribut(String) : void

+ Simple(String)

+ toString() : String~attg

~attd

class baseDeConnaissances

Extraire

~ source: String

+ AddFait(Fait) : void

+ ExtractionFaits() : ArrayList<Fait>

+ ExtractionRegles() : ArrayList<Regle>

+ Extraire(String)

+ getSource() : String

+ setSource(String) : void

Le chargement de la structure suit l’ordre suivant :

Trace d’exécution lors du chargement de la base de connaissances en mémoire :

Moteur d’inférence : Algorithme de recherche

Le moteur d’inférence développé implémente 2 algorithmes de recherche :

algorithme de recherche en profondeur limité itérative

algorithme de recherche A*

Les algorithmes de recherche font appel aux fonctions d’unifications

Declenchable.OperateursApplicables(regles, fait_a_applique) qui retourne une liste de

régles instanciées à partir du fait sélectionner

Exemple : OperateursApplicables (pbCruches, cruchesAetB(0, 0)) donne {R1(x/0,y/0),

R2(x/0,y/0)}

Elle fait appel à la méthode unifier(Predicat,Predicat) pour générer les opérateurs

applicables

ExtractionFaits()

Classe « Extraire »

ExtraireFait()

ExtraireRegles()

Classe « Fait » ExtrairePredicat()

Classe « GenericPredicat »

ExtraireAttribut()

Classe « Attribut »

InstancierFait.instancier_Fait(regle_instanciee, regles, parent) qui retourne un fait construit

en remplaçant les variables de la conclusion de la régle séléctionnée avec celles définies

dans la régle regle_instanciee

Exemple instancier_Fait(R1(x/0,y/0), regles, ‘’R2(x/2,(y/0)’’) donne Fait :{ numéro :F3,

Fait=cruchesAetB(2,0), explication=R1(x/0,y/0);R2(x/2,(y/0) }

Algorithme de recherche en profondeur limité itérative :

Stratégie: étendre un nœud jusqu’à une limite de profondeur d'exploration L puis passer au suivant

approfondissement itératif = répéter pour toutes les valeurs possibles de L = 0, 1, 2 ⋯

Implémentation:

o Les nœuds de profondeur L n'ont pas de successeurs

o Insertion des successeurs en tête de la file d'attente

Pour éviter les boucles infinies, on élimine les nœuds déjà rencontrés.

function Iterative-Deepening-Search (problem) returns a

solution

for depth = 0 to infinity do

result = Depth-Limited-Search (problem, depth)

if result != cutoff then return result

end

Construction de l’arbre de recherche :

Trace d’exécution :

Remarque : L’explication du fait contient la description du chemin emprunté pour atteindre cet état.

Algorithme de recherche A* :

L'algorithme A* est un algorithme de recherche de chemin dans un graphe entre un nœud initial

et un nœud final. Il utilise une évaluation heuristique f() sur chaque nœud pour estimer le

meilleur chemin y passant, et visite ensuite les nœuds par ordre de cette évaluation heuristique.

f(n) = g(n) + h(n)

où n est un nœud représentant un état dans l`espace d`états du problème et g(n) et h(n) ont les significations utilisées dans le cas de ces deux algorithmes. Cette heuristique estime le coût total du chemin entre l`état initial et l`état solution qui passe par n. On remarque qu`il s`agit d`une estimation f(n) obtenue par la somme d`une valeur exacte g(n) issue du chemin parcouru de l`état initial jusqu`au nœud n et d`une estimation h(n) du coût du chemin optimal qui lie le nœud n avec au nœud solution.

Pour chaque nœud exploré n on va étendre le successeur x qui minimise la fonction heuristique f()

f(x) = min { f(s) / s dans Successeurs( n ) }

Algorithme général :

function A*(start,goal) closedset := the empty set openset := set containing the initial node g_score[start] := 0 h_score[start] := heuristic_estimate_of_distance(start, goal) f_score[start] := h_score[start] while openset is not empty

x := the node in openset having the lowest f_score[] value if x = goal

return reconstruct_path(came_from,goal) remove x from openset add x to closedset foreach y in neighbor_nodes(x)

if y in closedset continue

tentative_g_score := g_score[x] + dist_between(x,y) tentative_is_better := false if y not in openset

add y to openset h_score[y] := heuristic_estimate_of_distance(y, goal) tentative_is_better := true

elseif tentative_g_score < g_score[y] tentative_is_better := true

if tentative_is_better = true came_from[y] := x g_score[y] := tentative_g_score f_score[y] := g_score[y] + h_score[y]

return failure

Application dans le problème de cruches :

- La fonction g(n) n’existe pas car le cout chemin parcouru de l`état initial jusqu`au nœud n

n’est pas calculable.

- La fonction dist_between(x,y) return 1

- La fonction f(n) devient égale à h(n) : coût du chemin optimal qui lie le nœud n avec au nœud solution.

- La fonction heuristique est

Application : Jeu du Taquin (Optionel)

- dist_between(x,y) return1

- Chaque noeud possède un coût f=g+h, où f est la somme entre :

• g : le coût pour atteindre un noeud depuis l'état initial ;

• h : le coût pour atteindre l'état final à partir de ce même noeud.

• Le cout est une heuristique : les heuristiques choisies sont la distance de

Manhattan et Misplaced.

- distance de Manhattan : somme des distances entre chaque tuile et sa position finale.

- Misplaced : chaque pièce mal placée par rapport à son état final reçoit une pénalité, ici de 1

Remarque : L’algorithme de recherche en profondeur est à éviter avec le jeu de taquin car elle

nécessite un temps d’exécution infinie (n’est pas optimal).

Démo d’exécution :

Cruches :

But à chercher Etat des cruches à un moment

donné

Simultaion étape à étape du résultat

obtenu

Taquin :

L’éxécution montre que l’heuristique manhattan est plus rapide MisPlaced.

Exemple :

:

Etat initial Manhattan MisPlaced