LYCÉE → PRÉPA · L17

Module L17 · Partie H · Intelligence artificielle

L’IA qui raisonne : recherche, contraintes, logique, planification.

Avant l’apprentissage profond, l’IA c’était ceci : représenter un problème par des états et des règles, puis chercher une solution — un coup d’échecs, un emploi du temps, un plan d’actions pour un robot. Ces méthodes n’ont pas disparu : elles sont dans les solveurs SAT qui vérifient les processeurs, dans les planificateurs de ROS, dans AlphaGo (couplées à un réseau) et dans les « raisonneurs » des LLM. Ce module les construit.

Durée : 3 séances · Prérequis : L04 (backtracking), L05 (graphes), L09 (logique). Objectifs : jeux à deux joueurs (minimax, alpha-bêta, heuristiques, tables de transposition), satisfaction de contraintes (propagation, heuristiques, arc-consistance), SAT (DPLL, apprentissage de clauses, encodages), planification classique (STRIPS, recherche dans l’espace des états, heuristiques relaxées), programmation logique (unification, résolution).

Ce que vous saurez faire à la fin
  • Écrire un joueur de puissance 4 imbattable en profondeur raisonnable.
  • Résoudre sudoku, coloration, emploi du temps par CSP avec propagation.
  • Encoder un problème en SAT et le résoudre avec votre propre DPLL.
  • Planifier une séquence d’actions pour un robot à partir d’un but.

Références : Artificial Intelligence: A Modern Approach (Russell & Norvig) chapitres 3-11, Handbook of Satisfiability, cours CS221 (Stanford).

Fiche de cours · Définitions

Recherche et IA symbolique : définitions

Définition (problème de recherche). État initial, fonction de successeurs (actions et coûts), test de but. Une solution est un chemin de l’état initial à un but ; elle est optimale si son coût est minimal. L’espace d’états est un graphe implicite, souvent trop grand pour être construit.
Définition (stratégies non informées). Largeur (BFS : optimal pour coûts unitaires, mémoire O(bd)), profondeur (mémoire O(bd), ni complet ni optimal), approfondissement itératif (les deux avantages), coût uniforme (Dijkstra). b : facteur de branchement, d : profondeur de la solution.
Définition (heuristique, A*). h(n) estime le coût restant de n au but. A* explore par f(n) = g(n) + h(n) croissant (g : coût depuis le départ). h est admissible si h(n) ≤ h*(n) (jamais surestimer) ; consistante (monotone) si h(n) ≤ c(n, n′) + h(n′) pour tout successeur n′.
Définition (jeux à deux joueurs). Arbre de jeu, fonction d’utilité aux feuilles. Minimax : MAX maximise, MIN minimise la valeur. Alpha-bêta : élaguer les branches qui ne peuvent pas influencer la décision (α : meilleure valeur garantie à MAX, β : à MIN). Évaluation heuristique à profondeur bornée.
Définition (problème de satisfaction de contraintes, CSP). Variables, domaines, contraintes. Résolution par backtracking + propagation (arc-consistance AC-3) + heuristiques (variable la plus contrainte, valeur la moins contraignante). SAT : CSP booléen en forme normale conjonctive ; DPLL : propagation unitaire + littéraux purs + branchement ; CDCL : apprentissage de clauses.
Définition (logique propositionnelle : conséquence, résolution). KB ⊨ φ si tout modèle de KB est modèle de φ. Règle de résolution : de (A ∨ x) et (B ∨ ¬x) déduire (A ∨ B). Réfutation : KB ⊨ φ ssi KB ∧ ¬φ est insatisfiable.

Fiche de cours · Formules

Formules et complexités

StratégieComplèteOptimaleTempsMémoire
BFSouicoûts unitairesO(bd)O(bd)
DFSnon (infini)nonO(bm)O(bm)
Approfondissement itératifouicoûts unitairesO(bd)O(bd)
Coût uniformeouiouiO(b1+C*/ε)idem
A* (h consistante)ouiouiexponentiel en l’erreur de htous les nœuds ouverts
Minimax / alpha-bêtaO(bm) / O(bm/2) au mieuxO(bm)
A* : f(n) = g(n) + h(n) ; Dijkstra = A* avec h = 0 ; glouton = A* avec g = 0
Heuristiques classiques : distance de Manhattan |Δx| + |Δy| (grille 4-connexe), euclidienne (mouvement libre), Chebyshev max(|Δx|, |Δy|) (8-connexe) ; taquin : nombre de tuiles mal placées ≤ somme des Manhattan ≤ h*
Alpha-bêta avec ordre parfait : bm/2 nœuds — doubler la profondeur pour le même coût
Seuil de satisfiabilité de 3-SAT aléatoire : m/n ≈ 4,26 clauses par variable (pic de difficulté)
Encodage « au plus un parmi x₁…xk » : C(k, 2) clauses (¬xi ∨ ¬xj), ou 3k clauses avec variables auxiliaires (séquentiel)

Fiche de cours · Théorèmes et démonstrations

Démonstrations à savoir refaire (1/2)

Théorème 1 (A* est optimal avec une heuristique admissible, version arbre). Si h est admissible, la première solution que A* extrait de la file de priorité est optimale.
Soit G un but sous-optimal extrait, de coût g(G) > C* (coût optimal), et soit G* un but optimal non encore extrait. Sur un chemin optimal vers G*, il existe un nœud n dans la file (le premier non encore développé — le chemin part de la racine qui l’a été). Alors f(n) = g(n) + h(n) ≤ g(n) + h*(n) = C* (admissibilité et n sur un chemin optimal). Et f(G) = g(G) + 0 > C* ≥ f(n). A* extrait le f minimal, donc aurait extrait n avant G — contradiction. Avec un ensemble fermé (version graphe), il faut la consistance pour ne jamais rouvrir un nœud : elle garantit que f est croissante le long de tout chemin, donc que la première extraction d’un nœud se fait avec son g optimal.
Théorème 2 (consistance ⇒ admissibilité, et f croissante). Si h(n) ≤ c(n, n′) + h(n′) pour tout successeur et h(but) = 0, alors h est admissible et f(n′) ≥ f(n) le long de tout chemin.
Récurrence sur la longueur du plus court chemin de n à un but : h(but) = 0 ≤ 0 ; si n′ est le successeur de n sur un chemin optimal, h(n) ≤ c(n, n′) + h(n′) ≤ c(n, n′) + h*(n′) = h*(n). Croissance : f(n′) = g(n) + c(n, n′) + h(n′) ≥ g(n) + h(n) = f(n). Toutes les heuristiques de distance (Manhattan sur grille 4-connexe, euclidienne) sont consistantes car ce sont des distances vérifiant l’inégalité triangulaire.
Théorème 3 (une heuristique dominante développe moins de nœuds). Si h₁ ≤ h₂ ≤ h* (h₂ domine h₁), tout nœud développé par A* avec h₂ l’est aussi avec h₁ (à égalités près).
A* développe tous les nœuds avec f(n) < C* (et certains avec f = C*). Si g(n) + h₂(n) < C*, alors g(n) + h₁(n) ≤ g(n) + h₂(n) < C* : n est aussi développé avec h₁. Donc plus h est proche de h* (sans le dépasser), moins A* travaille — jusqu’à h = h* où A* suit directement le chemin optimal. Justification du « relâchement » : l’heuristique optimale d’un problème simplifié (taquin sans collisions → Manhattan) est admissible pour le problème d’origine.

Fiche de cours · Théorèmes et démonstrations

Démonstrations à savoir refaire (2/2)

Théorème 4 (alpha-bêta calcule la valeur minimax). L’élagage ne change jamais la valeur renvoyée à la racine.
Invariant : à un nœud MAX, α est une valeur que MAX peut déjà garantir (par une alternative déjà explorée sur le chemin) ; à un nœud MIN, β est ce que MIN peut déjà garantir. Si, en un nœud MIN, un enfant renvoie v ≤ α, alors la valeur du nœud MIN sera ≤ v ≤ α : MAX, qui dispose d’une alternative valant ≥ α, ne choisira jamais ce nœud (ou bien y est indifférent en cas d’égalité) ; sa valeur exacte est sans influence sur la racine, on coupe. Symétriquement en un nœud MAX avec v ≥ β. Par récurrence sur la hauteur, la valeur de la racine est celle de minimax. Le nombre de nœuds visités dépend de l’ordre : avec les meilleurs coups d’abord, chaque niveau sur deux est réduit à un enfant, d’où bm/2 (Knuth-Moore 1975).
Théorème 5 (correction et complétude de la résolution). Un ensemble de clauses est insatisfiable si et seulement si la clause vide est dérivable par résolution.
Correction : si une valuation satisfait (A ∨ x) et (B ∨ ¬x), l’une des deux de x, ¬x est fausse, donc A ou B est vrai : la résolvante est satisfaite ; par récurrence, toute clause dérivée est conséquence ; la clause vide n’étant jamais satisfaite, sa dérivation prouve l’insatisfiabilité. Complétude (idée) : par récurrence sur le nombre de variables ; éliminer une variable x en formant toutes les résolvantes sur x (« élimination de Davis-Putnam ») préserve l’insatisfiabilité ; sur 0 variable, l’insatisfiabilité est la présence de la clause vide. Application : un prouveur automatique est un moteur de résolution ; les solveurs SAT modernes (CDCL) apprennent des clauses par résolution à chaque conflit.
Proposition 6 (propagation unitaire). Si une clause est réduite à un seul littéral non affecté (les autres sont faux), ce littéral doit être vrai dans toute solution étendant l’affectation courante.
La clause doit être satisfaite ; tous ses autres littéraux sont faux ; il ne reste que celui-là. DPLL l’applique jusqu’au point fixe avant chaque branchement : c’est ce qui résout la plupart des instances industrielles sans explorer.

Fiche de cours · Méthodes

Méthodes et pièges

Méthode — concevoir une heuristique. Relâcher le problème (supprimer une contrainte) et prendre le coût exact du problème relâché : admissible et souvent consistante. Combiner plusieurs heuristiques admissibles par max (encore admissible). Bases de données de motifs : précalculer h* sur un sous-problème. Vérifier empiriquement l’admissibilité sur des instances résolues.
Méthode — encoder en SAT. Une variable booléenne par « fait » possible (case i contient la valeur v). Contraintes : au moins un (une clause), au plus un (paires), implications (¬a ∨ b). Tester l’encodage sur une instance triviale et vérifier que les modèles correspondent aux solutions ; compter les clauses (souvent des millions : préférer les encodages compacts).
Méthode — programmer un moteur de jeu. Négamax + alpha-bêta ; ordre des coups (captures, coup tueur, meilleur coup de l’itération précédente) ; approfondissement itératif avec limite de temps ; table de transposition (hachage de Zobrist) ; recherche de quiescence pour éviter l’effet d’horizon ; évaluation = combinaison pondérée de traits, apprise ou réglée.

Pièges : heuristique non admissible qui rend A* sous-optimal sans prévenir ; oublier l’ensemble fermé (explosion exponentielle sur les grilles) ; état non canonique (le même état représenté différemment n’est pas reconnu) ; profondeur fixe sans quiescence ; DFS sans détection de cycle ; encodage SAT avec des « au plus un » quadratiques sur 1 000 variables (500 000 clauses).

Fiche de cours · Exercices corrigés

Exercices corrigés

Exercice 1. Grille 4-connexe avec obstacles, coût 1 par déplacement. Montrer que la distance de Manhattan est admissible et consistante ; que se passe-t-il si l’on autorise les diagonales à coût 1 ?
Correction. Admissible : tout chemin de (x, y) à (xb, yb) doit changer x de |Δx| unités et y de |Δy| unités, chaque pas ne changeant qu’une coordonnée d’une unité : au moins |Δx| + |Δy| pas, les obstacles ne peuvent qu’allonger. Consistante : un pas déplace d’une unité une coordonnée, donc h varie d’au plus 1 = c(n, n′) ; inégalité triangulaire de la norme 1. Avec diagonales à coût 1, un pas peut réduire h de 2 : Manhattan n’est plus admissible (surestime : h(1, 1) = 2 mais h* = 1). Utiliser Chebyshev max(|Δx|, |Δy|), admissible et consistante pour ce mouvement ; si les diagonales coûtent √2, l’heuristique octile |Δx| + |Δy| + (√2 − 2)min(|Δx|, |Δy|).
Exercice 2. Arbre de jeu de profondeur 2, MAX à la racine avec 3 enfants MIN de feuilles [3, 12, 8], [2, 4, 6], [14, 5, 2]. Dérouler alpha-bêta de gauche à droite et indiquer les feuilles non visitées.
Correction. Racine α = −∞, β = +∞. Enfant 1 (MIN) : feuilles 3, 12, 8 → valeur 3 ; racine α = 3. Enfant 2 : première feuille 2 ≤ α = 3 → coupure : MIN aura ≤ 2, MAX préfère 3 ; feuilles 4 et 6 non visitées. Enfant 3 : 14 (β devient 14), puis 5 (β = 5), puis 2 → valeur 2 ≤ 3 : pas de coupure possible avant la dernière feuille ici (la coupure arrive dès qu’une feuille ≤ α : 5 > 3, puis 2 ≤ 3 mais c’est la dernière). Valeur de la racine : 3, coup 1. Deux feuilles évitées sur neuf ; avec un meilleur ordre (enfant 3 exploré avec 2 d’abord), on en éviterait quatre.
Exercice 3. Encoder en SAT « colorier un graphe à n sommets avec 3 couleurs » ; compter les variables et clauses pour un graphe à 100 sommets et 300 arêtes ; expliquer pourquoi 2-coloration est facile et 3-coloration difficile.
Correction. Variables xv,c (v sommet, c ∈ {1, 2, 3}) : 3n = 300. Clauses : chaque sommet a une couleur (xv,1 ∨ xv,2 ∨ xv,3) : 100 ; au plus une (facultatif, 3 par sommet) : 300 ; arêtes : pour chaque (u, v) et chaque c, (¬xu,c ∨ ¬xv,c) : 900. Total ≈ 1 300 clauses, trivial pour un solveur. 2-coloration = biparti, décidable par BFS en O(n + m) (L05) ; 3-coloration est NP-complète (réduction depuis 3-SAT, L23) : aucun algorithme polynomial connu, mais les solveurs SAT résolvent des instances de milliers de sommets en pratique — la difficulté est celle du pire cas, pas de l’instance typique.

01 / Jeux

Minimax : jouer contre un adversaire parfait

L’arbre de jeu

Chaque nœud est une position, chaque arête un coup ; MAX (nous) choisit le maximum des valeurs des enfants, MIN (l’adversaire) le minimum. La valeur remonte des feuilles (positions finales). Le morpion a ~550 000 nœuds : on explore tout. Les échecs en ont 10120 : on coupe à une profondeur d et on estime les feuilles avec une fonction d’évaluation (matériel, mobilité…). Deep Blue (1997) explorait 200 millions de positions par seconde à profondeur 12-14 ; Stockfish aujourd’hui fait mieux avec une évaluation apprise (NNUE) — le mariage recherche + apprentissage.

01 / Jeux

Alpha-bêta : le même résultat, une fraction des nœuds

Pourquoi ça marche

α = la meilleure valeur que MAX est déjà sûr d’obtenir ; β = la meilleure que MIN est sûr d’obtenir. Si dans une branche MIN trouve un coup qui donne ≤ α, MAX n’y viendra jamais : inutile d’explorer le reste. Avec un bon ordre des coups (les meilleurs d’abord), alpha-bêta explore ≈ √(nœuds de minimax) : profondeur doublée à coût égal. Améliorations standard : approfondissement itératif, table de transposition (mémoïser les positions, module L04), recherche de quiescence, coups tueurs. Le puissance 4 a été résolu en 1988 (le premier joueur gagne en jouant au centre).

02 / Contraintes

CSP : variables, domaines, contraintes, propagation

Le triptyque du CSP

1) Backtracking (module L04) : affecter, vérifier, revenir. 2) Propagation : après chaque affectation, réduire les domaines des voisins (forward checking) ; plus loin, l’arc-consistance (AC-3) propage jusqu’au point fixe : chaque valeur d’une variable doit être compatible avec au moins une valeur de chaque voisine. 3) Heuristiques : MRV (variable au plus petit domaine), degré (variable la plus connectée), LCV (valeur qui contraint le moins). Les solveurs de contraintes (OR-Tools, MiniZinc) résolvent emplois du temps, ordonnancement d’usines, allocation de fréquences — des problèmes à des millions de variables.

02 / Contraintes

SAT : le problème universel, et l’algorithme DPLL

Pourquoi SAT est central

Tout problème NP se réduit à SAT (Cook-Levin, module L23) : sudoku, coloration, planification, vérification de circuits, cryptanalyse. Les solveurs modernes (MiniSat, CaDiCaL, Kissat) ajoutent à DPLL l’apprentissage de clauses (CDCL : à chaque conflit, déduire une clause qui l’explique et l’ajouter), des heuristiques de choix de variables (VSIDS), des redémarrages, et résolvent des instances à des millions de variables — Intel et AMD vérifient leurs processeurs ainsi. La transition de phase vers 4,26 clauses/variable, où les instances aléatoires deviennent brutalement difficiles, est un lien remarquable avec la physique statistique.

02 / Contraintes

Encoder un problème en SAT : le sudoku

Encoder, c’est traduire chaque règle en clauses : « au moins un », « au plus un » (paires exclues), et les indices comme clauses unitaires. La propagation unitaire de DPLL fait alors l’essentiel du travail : c’est le « raisonnement » d’un humain (« cette case ne peut plus être que 7 ») automatisé. Le même schéma encode un emploi du temps, un circuit logique ou un plan de robot.

03 / Planification

Planification classique : de l’état initial au but par des actions

Ce qu’un planificateur ajoute

La recherche aveugle explose avec le nombre d’objets (10 blocs : millions d’états). Les planificateurs (Fast Downward, LAMA) utilisent A* avec des heuristiques relaxées : résoudre le problème en ignorant les retraits (« delete relaxation ») donne une borne inférieure calculable en temps polynomial. Le langage standard est PDDL. En robotique, la planification de tâches (« prendre la tasse, ouvrir la porte… ») s’articule avec la planification de mouvement (module L21) : c’est le « task and motion planning », un domaine de recherche où les LLM commencent à servir de générateurs de plans candidats vérifiés par un planificateur symbolique.

03 / Planification

Programmation logique : unification et résolution (Prolog en 50 lignes)

Ce que Prolog a inventé

L’unification (Robinson, 1965) est l’algorithme qui fait correspondre deux motifs avec variables — c’est aussi le cœur de l’inférence de types d’OCaml (module L07) ! La résolution enchaîne les règles en arrière depuis le but. Un programme Prolog est une base de faits et de règles ; « exécuter » = prouver. La dernière requête montre la réversibilité : app écrit pour concaténer sert à découper. Les systèmes experts des années 1980, Datalog (bases de données déductives), les vérificateurs de types et les assistants de preuve descendent de là. Limites : la recherche en profondeur peut boucler, et le raisonnement sous incertitude a exigé les réseaux bayésiens (module L11) puis l’apprentissage.

04 / Synthèse

Symbolique et neuronal : deux moitiés d’une IA

Symbolique (recherche, logique)Neuronal (apprentissage)
ForcesExact, vérifiable, explicable, généralise hors des données, composeApprend de données brutes (pixels, sons), robuste au bruit, perception
FaiblessesIl faut écrire les règles ; fragile face au bruit et à l’ambiguïtéBoîte noire, hallucine, raisonnement multi-étapes fragile, faim de données
ExemplesSolveurs SAT, planificateurs, Prolog, échecs classiquesVision, parole, LLM, RL profond
HybridesAlphaGo (MCTS + réseaux), AlphaGeometry (LLM + moteur de déduction, médaille d’or IMO 2024), LLM + outils (Python, solveurs), vérification neuronale-symbolique, programmes synthétisés par LLM puis vérifiés

Le débat « symbolique contre connexionniste » a 60 ans. La position dominante aujourd’hui : la perception et l’intuition sont neuronales, le raisonnement fiable a besoin de structure symbolique, et la question de recherche est comment les combiner. Un ingénieur qui maîtrise les deux est rare et précieux.

Cours

Cours 1 — Recherche dans un espace d’états : le cadre unifié

Un problème de recherche = (état initial, fonction successeurs(état) → [(action, état′, coût)], test de but, éventuellement heuristique h). Tout ce module (jeux, CSP, planification) et le module L05 (BFS, Dijkstra, A*) sont des instances de ce cadre.

AlgorithmeFrontièreComplet ?Optimal ?Temps / mémoire
Largeur (BFS)FileOuiOui (coût unitaire)O(bd) / O(bd)
Profondeur (DFS)PileNon (cycles, infini)NonO(bm) / O(bm) — mémoire linéaire
Approfondissement itératifDFS bornée, borne croissanteOuiOui (unitaire)O(bd) / O(bd) — le meilleur des deux
Coût uniforme (Dijkstra)Tas par gOuiOuiO(b1+C*/ε)
Gloutonne (best-first par h)Tas par hNonNonRapide, myope
A*Tas par g + hOuiOui si h admissibleOptimalement efficace parmi les algorithmes utilisant h
IDA*DFS bornée par fOuiOuiMémoire linéaire : taquin, Rubik’s cube

b : facteur de branchement, d : profondeur de la solution, m : profondeur maximale. Le choix se fait sur la mémoire : BFS/A* gardent tous les états visités (10⁷ états = quelques Go) ; IDA* et l’approfondissement itératif n’en gardent qu’un chemin.

Cours

Cours 2 — Exemple travaillé : minimax avec évaluation, en profondeur bornée, sur une position d’échecs simplifiée

Sans aller jusqu’aux échecs complets, le raisonnement se voit sur un jeu à information parfaite : le Nim (des tas d’allumettes ; retirer 1 à 3 d’un tas ; qui prend la dernière gagne). Il a une solution mathématique (XOR des tas) qui sert d’oracle pour vérifier minimax.

Ce qu’il faut retenir. 1) La formulation negamax (ma valeur = − valeur adverse) évite de dupliquer le code MAX/MIN. 2) La table de transposition (lru_cache) transforme un arbre exponentiel en graphe : les mêmes positions reviennent par des ordres de coups différents. 3) Quand une théorie existe (Nim, puissance 4 résolu), elle sert d’oracle de test ; sinon, on teste par cohérence (symétries, jouer contre soi-même). 4) Aux échecs, on coupe à profondeur d et on évalue ; l’effet d’horizon (un désastre juste après la coupure) se traite par la recherche de quiescence (prolonger tant qu’il y a des prises).

Cours

Cours 3 — Modéliser un problème en CSP ou en SAT : la méthode

  1. Variables et domaines. Une variable par décision élémentaire (case du sudoku, créneau d’un cours, couleur d’une région), domaine fini. Astuce : préférer des domaines petits et des variables nombreuses (SAT : tout est booléen).
  2. Contraintes. Traduire chaque règle. Les contraintes globales (« toutes différentes », « somme = 10 ») ont des propagateurs spécialisés dans les solveurs CP ; en SAT, on les encode : « exactement un » = « au moins un » (une clause) + « au plus un » (paires, ou encodage séquentiel en O(n)).
  3. Symétries. Les casser (imposer un ordre) : sans cela, un solveur explore n! solutions équivalentes.
  4. Objectif. Un CSP n’a pas d’objectif ; pour optimiser, on ajoute une contrainte « coût ≤ K » et on cherche le plus petit K (dichotomie), ou on passe à un solveur MILP/CP-SAT.
  5. Choisir l’outil. Petit et pédagogique : votre backtracking. Réel : OR-Tools CP-SAT (Python, gratuit, très rapide), MiniZinc (langage de modélisation), PySAT/Z3 (SAT et SMT : SAT + arithmétique, théories).

TP guidé

TP — Solveurs industriels : OR-Tools, Z3, et votre puissance 4 en tournoi (sur PC, 3 h)

Exercices

Exercices auto-corrigés — jeux et recherche

Exercice 1 — Negamax avec alpha-bêta sur le morpion

Réécrivez le morpion en negamax alpha-bêta : negamax(p, joueur, alpha, beta) renvoie la valeur du point de vue du joueur au trait. Comptez les nœuds et vérifiez : même valeur que le minimax du cours (0 pour la position vide), et beaucoup moins de nœuds.

Correction
def negamax(p, joueur, alpha=-2, beta=2):
    global noeuds_nm; noeuds_nm += 1
    g = gagnant(p)
    if g: return 1 if g == joueur else -1
    if not coups(p): return 0
    autre = "O" if joueur == "X" else "X"; v = -2
    for i in coups(p):
        v = max(v, -negamax(jouer(p, i, joueur), autre, -beta, -alpha)); alpha = max(alpha, v)
        if alpha >= beta: break
    return v

Exercice 2 — Approfondissement itératif avec limite de temps

meilleur_coup_temps(p, joueur, budget_s) : profondeur 1, 2, 3… avec alpha-bêta borné en profondeur (évaluation 0 aux feuilles non finales), tant que le temps écoulé reste sous le budget ; renvoie le coup de la dernière profondeur complète et cette profondeur. Sur le morpion, avec 0,5 s, il doit atteindre la profondeur 9 depuis la position vide ; avec un budget minuscule, il doit quand même renvoyer un coup légal.

Correction
def meilleur_coup_temps(p, joueur, budget_s):
    t0 = time.perf_counter(); meilleur, prof_ok = coups(p)[0], 0
    def nm(p, j, d, alpha, beta):
        g = gagnant(p)
        if g: return 1 if g == j else -1
        if not coups(p) or d == 0: return 0
        autre = "O" if j == "X" else "X"; v = -2
        for i in coups(p):
            v = max(v, -nm(jouer(p, i, j), autre, d - 1, -beta, -alpha)); alpha = max(alpha, v)
            if alpha >= beta: break
        return v
    for d in range(1, 10):
        autre = "O" if joueur == "X" else "X"
        c = max(coups(p), key=lambda i: -nm(jouer(p, i, joueur), autre, d - 1, -2, 2))
        if time.perf_counter() - t0 > budget_s: break
        meilleur, prof_ok = c, d
    return meilleur, max(prof_ok, 1) if prof_ok else (meilleur, 1)

Exercices

Exercices auto-corrigés — contraintes et logique

Exercice 3 — Un CSP générique avec contraintes binaires et AC-3

Écrivez resoudre_csp(variables, domaines, contraintes)contraintes est un dictionnaire {(u, v): fonction(x, y) → bool} (ajoutez automatiquement la contrainte symétrique (v, u)). Backtracking + MRV + forward checking. Testez sur : coloration d’une carte, puis le cryptarithme « TO + GO = OUT » (contraintes binaires seulement : toutes différentes) avec vérification de l’addition en post-traitement.

Correction
def resoudre_csp(variables, domaines, contraintes):
    C = dict(contraintes); C.update({(v, u): (lambda f: (lambda x, y: f(y, x)))(f) for (u, v), f in contraintes.items()})
    dom = {v: set(d) for v, d in domaines.items()}; aff = {}
    def rec(dom):
        if len(aff) == len(variables): return dict(aff)
        v = min((x for x in variables if x not in aff), key=lambda x: len(dom[x]))
        for val in sorted(dom[v]):
            aff[v] = val; d2 = {u: set(d) for u, d in dom.items()}; ok = True
            for (a, b), f in C.items():
                if a == v and b not in aff:
                    d2[b] = {y for y in d2[b] if f(val, y)}
                    if not d2[b]: ok = False; break
            if ok:
                r = rec(d2)
                if r: return r
            del aff[v]
        return None
    return rec(dom)
def toutes(variables, domaines, contraintes, acc):
    C = dict(contraintes); C.update({(v, u): (lambda f: (lambda x, y: f(y, x)))(f) for (u, v), f in contraintes.items()})
    aff = {}
    def rec(dom):
        if len(aff) == len(variables): acc.append(dict(aff)); return
        v = min((x for x in variables if x not in aff), key=lambda x: len(dom[x]))
        for val in sorted(dom[v]):
            aff[v] = val; d2 = {u: set(d) for u, d in dom.items()}; ok = True
            for (a, b), f in C.items():
                if a == v and b not in aff:
                    d2[b] = {y for y in d2[b] if f(val, y)}
                    if not d2[b]: ok = False; break
            if ok: rec(d2)
            del aff[v]
    rec({v: set(d) for v, d in domaines.items()})

Énumérer toutes les affectations « toutes différentes » (10·9·8·…·3 = 1 814 400 avec S, M ≠ 0 : ≈ 1,45 million) prend quelques secondes ; la vraie résolution encode l’addition comme contrainte (non binaire) et propage : c’est ce que fait CP-SAT en millisecondes.

05 / Défis

Défi ★ — Puissance 4 jouable et table de transposition

Consigne

1) Ajoutez une table de transposition (dictionnaire position → valeur, profondeur) à alpha-bêta et mesurez la réduction du nombre de nœuds à profondeur 7. 2) Approfondissement itératif : profondeur 1, 2, 3… tant que le temps reste sous 1 s ; utilisez le meilleur coup de l’itération précédente en premier. 3) Faites jouer profondeur 4 contre profondeur 6 sur 10 parties (alternez qui commence) : qui gagne ?

Piste

Clé de transposition : tuple("".join(col) for col in p). Ne stocker que si la recherche n’a pas été coupée (valeur exacte), ou stocker avec un drapeau (borne inférieure / supérieure / exacte) comme les vrais moteurs. Pour la question 3, la profondeur 6 doit gagner presque toutes les parties — sauf si l’évaluation est mauvaise : c’est l’occasion de l’améliorer.

05 / Défis

Défi ★★ — Emploi du temps par CSP et par SAT

Consigne

6 cours, 3 créneaux, 2 salles ; contraintes : deux cours d’un même professeur pas au même créneau ; deux cours d’un même groupe d’élèves pas au même créneau ; un cours de TP dans la salle 2 seulement ; le cours 5 doit précéder le cours 6. 1) Modélisez et résolvez avec csp (variables = (créneau, salle) par cours ; il faudra généraliser la fonction aux contraintes binaires quelconques). 2) Encodez en SAT et résolvez avec dpll. 3) Rendez le problème insatisfiable (ajoutez une contrainte) : lequel des deux solveurs le détecte le plus vite, et pourquoi ? 4) Implémentez AC-3 et comptez les nœuds gagnés.

Piste (AC-3)
def ac3(domaines, contraintes):
    """contraintes : {(u, v): fonction(x, y) → bool}. Réduit les domaines jusqu'au point fixe."""
    from collections import deque
    file = deque(contraintes)
    while file:
        u, v = file.popleft(); ok = contraintes[(u, v)]
        avant = len(domaines[u])
        domaines[u] = {x for x in domaines[u] if any(ok(x, y) for y in domaines[v])}
        if not domaines[u]: return False
        if len(domaines[u]) < avant:
            file.extend((w, u) for (w, z) in contraintes if z == u and w != v)
    return True

05 / Défis

Défi ★★★ — Esprit prépa : CDCL et la planification par SAT

Consigne

1) Ajoutez à DPLL l’apprentissage de clauses : gardez pour chaque affectation son niveau de décision et la clause qui l’a impliquée ; à un conflit, analysez le graphe d’implication pour produire une clause « 1-UIP » et sautez en arrière au bon niveau (backjumping). Mesurez sur 3-SAT n = 80 au seuil. 2) Planification par SAT (Kautz & Selman) : encodez le monde des blocs avec des variables indexées par le temps (fait_t, action_t), les axiomes d’état (un fait persiste sauf si une action le retire), une seule action par pas ; cherchez le plan de longueur k = 1, 2, 3… Comparez à la BFS. 3) Discutez : pourquoi la « delete relaxation » donne-t-elle une heuristique admissible pour A* ? Prouvez-le.

Piste

Pour 1) : représentez l’état du solveur par une pile d’affectations (variable, valeur, niveau, raison) ; lors d’un conflit sur la clause C, remplacez itérativement dans C le littéral affecté le plus récemment au niveau courant par sa clause raison (résolution), jusqu’à ce qu’un seul littéral du niveau courant reste : c’est la clause 1-UIP. Pour 3) : tout plan du problème original est un plan du problème relaxé (les retraits ne peuvent qu’empêcher des actions) ; donc le coût optimal relaxé minore le coût optimal réel — admissible. Le calculer exactement est NP-difficile, mais hmax et hadd s’obtiennent en temps polynomial.

06 / Vérification

Par rapport à minimax, alpha-bêta :

Deux questions supplémentaires

1. Que fait la propagation unitaire ? Si une clause n’a plus qu’un littéral non affecté, ce littéral doit être vrai ; on l’affecte et on continue jusqu’au point fixe.

2. Pourquoi encode-t-on « au plus un » par des paires exclues ? Parce que ¬(a ∧ b) = (¬a ∨ ¬b) est une clause ; n valeurs donnent n(n−1)/2 clauses — il existe des encodages plus compacts (séquentiels, en O(n)).

Référence

Les mots à retenir

MotDéfinition
MinimaxValeur d’une position si les deux jouent parfaitement.
Alpha-bêtaMinimax avec coupures ; même résultat, √ des nœuds.
Fonction d’évaluationEstimation heuristique d’une position non finale.
Table de transpositionMémoïsation des positions.
CSPVariables, domaines, contraintes ; backtracking + propagation + heuristiques.
Arc-consistance (AC-3)Chaque valeur compatible avec au moins une valeur de chaque voisine.
SAT / DPLL / CDCLSatisfiabilité ; propagation unitaire + branchement ; + apprentissage de clauses.
STRIPS / PDDLActions par préconditions et effets ; langage de planification.
Delete relaxationHeuristique admissible obtenue en ignorant les effets négatifs.
Unification / résolutionFaire correspondre des termes / enchaîner des règles (Prolog).
Neuro-symboliqueCombiner apprentissage et raisonnement.

Pour continuer

Fin de la partie H : vous connaissez les deux IA

Partie I : robotique et systèmes embarqués avancés. Module suivant : les systèmes embarqués — interruptions, temps réel, RTOS, registres, C bas niveau — ce qui se passe dans le microcontrôleur de votre robot.

À faire chez soi

← L16SommaireL18 : Systèmes embarqués et temps réel →