diff --git a/MyIA.AI.Notebooks/Search/Part1-Foundations/Search-03-Informed-Csharp.ipynb b/MyIA.AI.Notebooks/Search/Part1-Foundations/Search-03-Informed-Csharp.ipynb index 1ee2085498..1810b37571 100644 --- a/MyIA.AI.Notebooks/Search/Part1-Foundations/Search-03-Informed-Csharp.ipynb +++ b/MyIA.AI.Notebooks/Search/Part1-Foundations/Search-03-Informed-Csharp.ipynb @@ -25,7 +25,7 @@ "La recherche informée exploite une connaissance du problème — une **heuristique** $h(n)$ qui estime le coût restant vers le but — pour guider l'exploration vers les régions prometteuses de l'espace. Ce notebook montre, en C#, comment chaque algorithme combine différemment le coût déjà accumulé $g(n)$ et l'estimation $h(n)$ :\n", "\n", "- **Greedy Best-First** : fonce vers le but en minimisant $h(n)$ seul — rapide mais **sous-optimal**.\n", - "- **A\\*** : équilibre $f(n) = g(n) + h(n)$ — **optimal** si $h$ est admissible.\n", + "- **A\\*** : équilibre $f(n) = g(n) + h(n)$ — **optimal** si $h$ est consistante (la variante Graph-Search implémentée ici ne ré-ouvre pas les états fermés ; contre-exemple à quatre nœuds : jumeau Python §6).\n", "- **IDA\\*** : A* à approfondissement itéré par borne sur $f$ — optimal et **borné en mémoire** $O(bd)$.\n", "\n", "### Objectifs d'apprentissage\n", @@ -846,7 +846,7 @@ "| Optimalité | **Oui** | si $h$ **consistante** (et Graph-Search, pas de re-open) |\n", "| Mémoire | $O(b^d)$ | exponentielle |\n", "\n", - "> **Théorème (Hart, Nilsson, Raphael 1968)** : si $h$ est admissible, A* trouve un chemin de coût minimal." + "> **Théorème (Hart, Nilsson, Raphael 1968)** : si $h$ est admissible, A* trouve un chemin de coût minimal — à condition de **ré-ouvrir** un état fermé quand un meilleur $g$ arrive. La variante Graph-Search **sans ré-ouverture** de ce notebook exige $h$ **consistante** (contre-exemple à quatre nœuds : jumeau Python §6)." ] }, { @@ -1956,7 +1956,7 @@ "|----------|--------|\n", "| **Mémoire** | $O(bd)$ — linéaire en profondeur (vs $O(b^d)$ pour A*) |\n", "| **Pas de file de priorité** | DFS récursif simple |\n", - "| **Même optimalité** | A* avec la même heuristique admissible |\n", + "| **Même optimalité** | que A* ; $h$ admissible suffit à IDA* (pas d'ensemble fermé), la variante A* sans re-open exige $h$ consistante |\n", "\n", "**Inconvénient** : re-exploration des nœuds à chaque itération (comme IDDFS), mais l'overhead est modeste car la borne $f$ augmente peu à chaque cycle." ] @@ -2247,7 +2247,7 @@ "\n", "| Heuristique | Définition | Admissible ? | Consistante ? |\n", "|-------------|------------|--------------|---------------|\n", - "| **$h_1$ Tuiles mal placées** | Nombre de tuiles hors de leur position but | Oui | Pas toujours |\n", + "| **$h_1$ Tuiles mal placées** (case vide exclue) | Nombre de tuiles hors de leur position but | Oui | **Oui** (un mouvement déplace une seule tuile numérotée) |\n", "| **$h_2$ Distance Manhattan** | Somme des distances $L_1$ de chaque tuile à sa position but | Oui | **Oui** |\n", "\n", "De plus, $h_2$ **domine** $h_1$ : $h_2(n) \\geq h_1(n)$ partout — donc A* avec $h_2$ explore **au moins aussi peu** de nœuds qu'avec $h_1$." @@ -2830,13 +2830,13 @@ "| Algorithme | Critère | Optimal | Mémoire | Quand ? |\n", "|------------|---------|---------|---------|---------|\n", "| **Greedy** | minimiser $h(n)$ | Non | $O(b^m)$ | Vitesse prioritaire, solution « assez bonne » |\n", - "| **A\\*** | minimiser $g(n) + h(n)$ | **Oui** (h admissible) | $O(b^d)$ | Référence, optimalité requise |\n", + "| **A\\*** | minimiser $g(n) + h(n)$ | **Oui** (h consistante, variante sans re-open) | $O(b^d)$ | Référence, optimalité requise |\n", "| **IDA\\*** | DFS itéré par $f(n)$ | **Oui** | $O(bd)$ | Mémoire limitée, espace profond |\n", "\n", "### Ce qu'il faut retenir\n", "\n", "1. **Greedy vs A* sur Lille → Toulouse** : Greedy (1210 km) est ~14% sous-optimal vs A* (1055 km) — la divergence est **structurelle**, pas accidentelle.\n", - "2. **A* est l'algorithme de référence** : optimal avec heuristique admissible, c'est le choix par défaut.\n", + "2. **A* est l'algorithme de référence** : optimal avec heuristique consistante (admissible seule ne suffit pas sans ré-ouverture), c'est le choix par défaut.\n", "3. **IDA* pour la mémoire** : même optimalité qu'A*, consommation **linéaire** au lieu d'exponentielle.\n", "4. **La qualité de l'heuristique est déterminante** : Manhattan (consistante, dominante) explore bien moins de nœuds que « tuiles mal placées » sur le 8-puzzle.\n", "\n", diff --git a/MyIA.AI.Notebooks/Search/Part1-Foundations/Search-03-Informed.ipynb b/MyIA.AI.Notebooks/Search/Part1-Foundations/Search-03-Informed.ipynb index 66df05d826..262eb2daab 100644 --- a/MyIA.AI.Notebooks/Search/Part1-Foundations/Search-03-Informed.ipynb +++ b/MyIA.AI.Notebooks/Search/Part1-Foundations/Search-03-Informed.ipynb @@ -5,10 +5,10 @@ "id": "nav-header", "metadata": { "papermill": { - "duration": 0.011021, - "end_time": "2026-08-23T03:34:45.107418", + "duration": 0.00722, + "end_time": "2026-09-06T22:12:19.532584", "exception": false, - "start_time": "2026-08-23T03:34:45.096397", + "start_time": "2026-09-06T22:12:19.525364", "status": "completed" }, "tags": [] @@ -40,16 +40,16 @@ "id": "imports", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:45.118926Z", - "iopub.status.busy": "2026-08-23T03:34:45.118926Z", - "iopub.status.idle": "2026-08-23T03:34:45.771885Z", - "shell.execute_reply": "2026-08-23T03:34:45.771336Z" + "iopub.execute_input": "2026-09-06T22:12:19.548614Z", + "iopub.status.busy": "2026-09-06T22:12:19.548614Z", + "iopub.status.idle": "2026-09-06T22:12:22.040560Z", + "shell.execute_reply": "2026-09-06T22:12:22.040560Z" }, "papermill": { - "duration": 0.658983, - "end_time": "2026-08-23T03:34:45.772401", + "duration": 2.501465, + "end_time": "2026-09-06T22:12:22.041566", "exception": false, - "start_time": "2026-08-23T03:34:45.113418", + "start_time": "2026-09-06T22:12:19.540101", "status": "completed" }, "tags": [] @@ -95,10 +95,10 @@ "id": "intro-section", "metadata": { "papermill": { - "duration": 0.006179, - "end_time": "2026-08-23T03:34:45.784891", + "duration": 0.006015, + "end_time": "2026-09-06T22:12:22.054579", "exception": false, - "start_time": "2026-08-23T03:34:45.778712", + "start_time": "2026-09-06T22:12:22.048564", "status": "completed" }, "tags": [] @@ -147,10 +147,10 @@ "id": "framework-section", "metadata": { "papermill": { - "duration": 0.006068, - "end_time": "2026-08-23T03:34:45.796190", + "duration": 0.00601, + "end_time": "2026-09-06T22:12:22.066223", "exception": false, - "start_time": "2026-08-23T03:34:45.790122", + "start_time": "2026-09-06T22:12:22.060213", "status": "completed" }, "tags": [] @@ -169,16 +169,16 @@ "id": "node-class", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:45.809786Z", - "iopub.status.busy": "2026-08-23T03:34:45.808800Z", - "iopub.status.idle": "2026-08-23T03:34:45.819103Z", - "shell.execute_reply": "2026-08-23T03:34:45.818565Z" + "iopub.execute_input": "2026-09-06T22:12:22.079225Z", + "iopub.status.busy": "2026-09-06T22:12:22.079225Z", + "iopub.status.idle": "2026-09-06T22:12:22.092258Z", + "shell.execute_reply": "2026-09-06T22:12:22.092258Z" }, "papermill": { - "duration": 0.017302, - "end_time": "2026-08-23T03:34:45.819103", + "duration": 0.020021, + "end_time": "2026-09-06T22:12:22.092258", "exception": false, - "start_time": "2026-08-23T03:34:45.801801", + "start_time": "2026-09-06T22:12:22.072237", "status": "completed" }, "tags": [] @@ -333,10 +333,10 @@ "id": "problem-example-intro", "metadata": { "papermill": { - "duration": 0.010327, - "end_time": "2026-08-23T03:34:45.840449", + "duration": 0.006001, + "end_time": "2026-09-06T22:12:22.105530", "exception": false, - "start_time": "2026-08-23T03:34:45.830122", + "start_time": "2026-09-06T22:12:22.099529", "status": "completed" }, "tags": [] @@ -353,16 +353,16 @@ "id": "graph-problem", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:45.856005Z", - "iopub.status.busy": "2026-08-23T03:34:45.856005Z", - "iopub.status.idle": "2026-08-23T03:34:45.862078Z", - "shell.execute_reply": "2026-08-23T03:34:45.862078Z" + "iopub.execute_input": "2026-09-06T22:12:22.119976Z", + "iopub.status.busy": "2026-09-06T22:12:22.119976Z", + "iopub.status.idle": "2026-09-06T22:12:22.126995Z", + "shell.execute_reply": "2026-09-06T22:12:22.125991Z" }, "papermill": { - "duration": 0.014221, - "end_time": "2026-08-23T03:34:45.863626", + "duration": 0.015018, + "end_time": "2026-09-06T22:12:22.126995", "exception": false, - "start_time": "2026-08-23T03:34:45.849405", + "start_time": "2026-09-06T22:12:22.111977", "status": "completed" }, "tags": [] @@ -442,10 +442,10 @@ "id": "heuristic-intro", "metadata": { "papermill": { - "duration": 0.014846, - "end_time": "2026-08-23T03:34:45.886544", + "duration": 0.006752, + "end_time": "2026-09-06T22:12:22.141747", "exception": false, - "start_time": "2026-08-23T03:34:45.871698", + "start_time": "2026-09-06T22:12:22.134995", "status": "completed" }, "tags": [] @@ -465,16 +465,16 @@ "id": "heuristic-function", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:45.906855Z", - "iopub.status.busy": "2026-08-23T03:34:45.905848Z", - "iopub.status.idle": "2026-08-23T03:34:45.911870Z", - "shell.execute_reply": "2026-08-23T03:34:45.911367Z" + "iopub.execute_input": "2026-09-06T22:12:22.156635Z", + "iopub.status.busy": "2026-09-06T22:12:22.156103Z", + "iopub.status.idle": "2026-09-06T22:12:22.161015Z", + "shell.execute_reply": "2026-09-06T22:12:22.160384Z" }, "papermill": { - "duration": 0.016138, - "end_time": "2026-08-23T03:34:45.912873", + "duration": 0.013062, + "end_time": "2026-09-06T22:12:22.161534", "exception": false, - "start_time": "2026-08-23T03:34:45.896735", + "start_time": "2026-09-06T22:12:22.148472", "status": "completed" }, "tags": [] @@ -534,10 +534,10 @@ "id": "greedy-section", "metadata": { "papermill": { - "duration": 0.007311, - "end_time": "2026-08-23T03:34:45.926693", + "duration": 0.006899, + "end_time": "2026-09-06T22:12:22.175257", "exception": false, - "start_time": "2026-08-23T03:34:45.919382", + "start_time": "2026-09-06T22:12:22.168358", "status": "completed" }, "tags": [] @@ -571,16 +571,16 @@ "id": "greedy-impl", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:45.939998Z", - "iopub.status.busy": "2026-08-23T03:34:45.939998Z", - "iopub.status.idle": "2026-08-23T03:34:45.946015Z", - "shell.execute_reply": "2026-08-23T03:34:45.946015Z" + "iopub.execute_input": "2026-09-06T22:12:22.189739Z", + "iopub.status.busy": "2026-09-06T22:12:22.189739Z", + "iopub.status.idle": "2026-09-06T22:12:22.197165Z", + "shell.execute_reply": "2026-09-06T22:12:22.196154Z" }, "papermill": { - "duration": 0.013321, - "end_time": "2026-08-23T03:34:45.946015", + "duration": 0.015518, + "end_time": "2026-09-06T22:12:22.197165", "exception": false, - "start_time": "2026-08-23T03:34:45.932694", + "start_time": "2026-09-06T22:12:22.181647", "status": "completed" }, "tags": [] @@ -670,10 +670,10 @@ "id": "340234eb", "metadata": { "papermill": { - "duration": 0.004978, - "end_time": "2026-08-23T03:34:45.957844", + "duration": 0.006018, + "end_time": "2026-09-06T22:12:22.213067", "exception": false, - "start_time": "2026-08-23T03:34:45.952866", + "start_time": "2026-09-06T22:12:22.207049", "status": "completed" }, "tags": [] @@ -688,16 +688,16 @@ "id": "greedy-test", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:45.970453Z", - "iopub.status.busy": "2026-08-23T03:34:45.970453Z", - "iopub.status.idle": "2026-08-23T03:34:45.973760Z", - "shell.execute_reply": "2026-08-23T03:34:45.973760Z" + "iopub.execute_input": "2026-09-06T22:12:22.228512Z", + "iopub.status.busy": "2026-09-06T22:12:22.227513Z", + "iopub.status.idle": "2026-09-06T22:12:22.232093Z", + "shell.execute_reply": "2026-09-06T22:12:22.232093Z" }, "papermill": { - "duration": 0.010855, - "end_time": "2026-08-23T03:34:45.974767", + "duration": 0.013753, + "end_time": "2026-09-06T22:12:22.233121", "exception": false, - "start_time": "2026-08-23T03:34:45.963912", + "start_time": "2026-09-06T22:12:22.219368", "status": "completed" }, "tags": [] @@ -719,7 +719,7 @@ " Noeuds explores : 2\n", " Noeuds generes : 8\n", " Frontiere max : 5\n", - " Temps : 0.06 ms\n" + " Temps : 0.10 ms\n" ] } ], @@ -737,10 +737,10 @@ "id": "dcjquopv3vv", "metadata": { "papermill": { - "duration": 0.007001, - "end_time": "2026-08-23T03:34:45.990788", + "duration": 0.005998, + "end_time": "2026-09-06T22:12:22.245552", "exception": false, - "start_time": "2026-08-23T03:34:45.983787", + "start_time": "2026-09-06T22:12:22.239554", "status": "completed" }, "tags": [] @@ -767,10 +767,10 @@ "id": "greedy-interpretation", "metadata": { "papermill": { - "duration": 0.006619, - "end_time": "2026-08-23T03:34:46.003918", + "duration": 0.009627, + "end_time": "2026-09-06T22:12:22.261696", "exception": false, - "start_time": "2026-08-23T03:34:45.997299", + "start_time": "2026-09-06T22:12:22.252069", "status": "completed" }, "tags": [] @@ -797,10 +797,10 @@ "id": "astar-section", "metadata": { "papermill": { - "duration": 0.006561, - "end_time": "2026-08-23T03:34:46.015986", + "duration": 0.006193, + "end_time": "2026-09-06T22:12:22.274889", "exception": false, - "start_time": "2026-08-23T03:34:46.009425", + "start_time": "2026-09-06T22:12:22.268696", "status": "completed" }, "tags": [] @@ -848,16 +848,16 @@ "id": "astar-impl", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:46.034126Z", - "iopub.status.busy": "2026-08-23T03:34:46.034126Z", - "iopub.status.idle": "2026-08-23T03:34:46.040158Z", - "shell.execute_reply": "2026-08-23T03:34:46.040158Z" + "iopub.execute_input": "2026-09-06T22:12:22.289163Z", + "iopub.status.busy": "2026-09-06T22:12:22.289163Z", + "iopub.status.idle": "2026-09-06T22:12:22.296212Z", + "shell.execute_reply": "2026-09-06T22:12:22.295679Z" }, "papermill": { - "duration": 0.017552, - "end_time": "2026-08-23T03:34:46.041163", + "duration": 0.015283, + "end_time": "2026-09-06T22:12:22.296212", "exception": false, - "start_time": "2026-08-23T03:34:46.023611", + "start_time": "2026-09-06T22:12:22.280929", "status": "completed" }, "tags": [] @@ -950,10 +950,10 @@ "id": "8a172994", "metadata": { "papermill": { - "duration": 0.004502, - "end_time": "2026-08-23T03:34:46.054012", + "duration": 0.006001, + "end_time": "2026-09-06T22:12:22.309023", "exception": false, - "start_time": "2026-08-23T03:34:46.049510", + "start_time": "2026-09-06T22:12:22.303022", "status": "completed" }, "tags": [] @@ -968,16 +968,16 @@ "id": "astar-test", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:46.066216Z", - "iopub.status.busy": "2026-08-23T03:34:46.066216Z", - "iopub.status.idle": "2026-08-23T03:34:46.069381Z", - "shell.execute_reply": "2026-08-23T03:34:46.069381Z" + "iopub.execute_input": "2026-09-06T22:12:22.322782Z", + "iopub.status.busy": "2026-09-06T22:12:22.322782Z", + "iopub.status.idle": "2026-09-06T22:12:22.325751Z", + "shell.execute_reply": "2026-09-06T22:12:22.325751Z" }, "papermill": { - "duration": 0.011704, - "end_time": "2026-08-23T03:34:46.071413", + "duration": 0.012254, + "end_time": "2026-09-06T22:12:22.326784", "exception": false, - "start_time": "2026-08-23T03:34:46.059709", + "start_time": "2026-09-06T22:12:22.314530", "status": "completed" }, "tags": [] @@ -1018,10 +1018,10 @@ "id": "1mddry1upc6", "metadata": { "papermill": { - "duration": 0.006175, - "end_time": "2026-08-23T03:34:46.086590", + "duration": 0.006573, + "end_time": "2026-09-06T22:12:22.340376", "exception": false, - "start_time": "2026-08-23T03:34:46.080415", + "start_time": "2026-09-06T22:12:22.333803", "status": "completed" }, "tags": [] @@ -1030,7 +1030,7 @@ "***\n", "### Test de l'algorithme A*\n", "\n", - "L'algorithme **A\\*** (A-Star) est l'un des algorithmes de recherche les plus utilisés en IA. Il garantit de trouver le chemin optimal si l'heuristique est **admissible** (ne surestime jamais le coût réel).\n", + "L'algorithme **A\\*** (A-Star) est l'un des algorithmes de recherche les plus utilisés en IA. Il garantit de trouver le chemin optimal si l'heuristique est **consistante** (ne surestime jamais le coût réel et ne diminue pas plus vite que le coût des arcs) — la condition qui correspond à la variante Graph-Search implémentée ici, qui ne ré-ouvre jamais un état fermé (contre-exemple avec une heuristique seulement admissible : §6).\n", "\n", "**Fonction d'évaluation** : `f(n) = g(n) + h(n)`\n", "- `g(n)` : coût du chemin depuis le départ\n", @@ -1045,10 +1045,10 @@ "id": "astar-interpretation", "metadata": { "papermill": { - "duration": 0.009003, - "end_time": "2026-08-23T03:34:46.104649", + "duration": 0.007247, + "end_time": "2026-09-06T22:12:22.353638", "exception": false, - "start_time": "2026-08-23T03:34:46.095646", + "start_time": "2026-09-06T22:12:22.346391", "status": "completed" }, "tags": [] @@ -1074,10 +1074,10 @@ "id": "comparison-section", "metadata": { "papermill": { - "duration": 0.008974, - "end_time": "2026-08-23T03:34:46.120244", + "duration": 0.007098, + "end_time": "2026-09-06T22:12:22.368035", "exception": false, - "start_time": "2026-08-23T03:34:46.111270", + "start_time": "2026-09-06T22:12:22.360937", "status": "completed" }, "tags": [] @@ -1094,16 +1094,16 @@ "id": "comparison-greedy-astar", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:46.134381Z", - "iopub.status.busy": "2026-08-23T03:34:46.133860Z", - "iopub.status.idle": "2026-08-23T03:34:46.146191Z", - "shell.execute_reply": "2026-08-23T03:34:46.145668Z" + "iopub.execute_input": "2026-09-06T22:12:22.382607Z", + "iopub.status.busy": "2026-09-06T22:12:22.382607Z", + "iopub.status.idle": "2026-09-06T22:12:22.403492Z", + "shell.execute_reply": "2026-09-06T22:12:22.403492Z" }, "papermill": { - "duration": 0.0209, - "end_time": "2026-08-23T03:34:46.147228", + "duration": 0.029249, + "end_time": "2026-09-06T22:12:22.404505", "exception": false, - "start_time": "2026-08-23T03:34:46.126328", + "start_time": "2026-09-06T22:12:22.375256", "status": "completed" }, "tags": [] @@ -1116,7 +1116,7 @@ "Comparaison : Greedy vs A*\n", "======================================================================\n", "Algorithme Chemin Cout (km) Longueur Noeuds explores Temps (ms)\n", - " Greedy Bordeaux -> Paris -> Strasbourg 1075 2 2 0.04\n", + " Greedy Bordeaux -> Paris -> Strasbourg 1075 2 2 0.03\n", " A* Bordeaux -> Paris -> Strasbourg 1075 2 3 0.02\n", "\n", "======================================================================\n", @@ -1172,10 +1172,10 @@ "id": "6ab7895c", "metadata": { "papermill": { - "duration": 0.006819, - "end_time": "2026-08-23T03:34:46.160360", + "duration": 0.007267, + "end_time": "2026-09-06T22:12:22.419052", "exception": false, - "start_time": "2026-08-23T03:34:46.153541", + "start_time": "2026-09-06T22:12:22.411785", "status": "completed" }, "tags": [] @@ -1201,16 +1201,16 @@ "id": "ca315179", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:46.176330Z", - "iopub.status.busy": "2026-08-23T03:34:46.176330Z", - "iopub.status.idle": "2026-08-23T03:34:46.182867Z", - "shell.execute_reply": "2026-08-23T03:34:46.182867Z" + "iopub.execute_input": "2026-09-06T22:12:22.434243Z", + "iopub.status.busy": "2026-09-06T22:12:22.433929Z", + "iopub.status.idle": "2026-09-06T22:12:22.440940Z", + "shell.execute_reply": "2026-09-06T22:12:22.440292Z" }, "papermill": { - "duration": 0.015945, - "end_time": "2026-08-23T03:34:46.183873", + "duration": 0.016014, + "end_time": "2026-09-06T22:12:22.441555", "exception": false, - "start_time": "2026-08-23T03:34:46.167928", + "start_time": "2026-09-06T22:12:22.425541", "status": "completed" }, "tags": [] @@ -1282,10 +1282,10 @@ "id": "comparison-interpretation", "metadata": { "papermill": { - "duration": 0.00653, - "end_time": "2026-08-23T03:34:46.196402", + "duration": 0.007021, + "end_time": "2026-09-06T22:12:22.454865", "exception": false, - "start_time": "2026-08-23T03:34:46.189872", + "start_time": "2026-09-06T22:12:22.447844", "status": "completed" }, "tags": [] @@ -1297,7 +1297,7 @@ "\n", "| Aspect | Greedy | A* |\n", "|--------|--------|-----|\n", - "| Optimalité | Non garantie | Garantie (avec heuristique admissible) |\n", + "| Optimalité | Non garantie | Garantie (avec heuristique consistante) |\n", "| Nœuds explorés | Généralement moins | Peut être plus |\n", "| Qualité solution | Variable | Toujours optimale |\n", "\n", @@ -1309,10 +1309,10 @@ "id": "ida-section", "metadata": { "papermill": { - "duration": 0.006121, - "end_time": "2026-08-23T03:34:46.209502", + "duration": 0.006026, + "end_time": "2026-09-06T22:12:22.467632", "exception": false, - "start_time": "2026-08-23T03:34:46.203381", + "start_time": "2026-09-06T22:12:22.461606", "status": "completed" }, "tags": [] @@ -1346,16 +1346,16 @@ "id": "idastar-impl", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:46.223816Z", - "iopub.status.busy": "2026-08-23T03:34:46.223816Z", - "iopub.status.idle": "2026-08-23T03:34:46.230105Z", - "shell.execute_reply": "2026-08-23T03:34:46.229592Z" + "iopub.execute_input": "2026-09-06T22:12:22.487209Z", + "iopub.status.busy": "2026-09-06T22:12:22.487209Z", + "iopub.status.idle": "2026-09-06T22:12:22.494259Z", + "shell.execute_reply": "2026-09-06T22:12:22.493253Z" }, "papermill": { - "duration": 0.014463, - "end_time": "2026-08-23T03:34:46.230659", + "duration": 0.020119, + "end_time": "2026-09-06T22:12:22.494763", "exception": false, - "start_time": "2026-08-23T03:34:46.216196", + "start_time": "2026-09-06T22:12:22.474644", "status": "completed" }, "tags": [] @@ -1449,10 +1449,10 @@ "id": "40f8a8e5", "metadata": { "papermill": { - "duration": 0.005999, - "end_time": "2026-08-23T03:34:46.244341", + "duration": 0.007354, + "end_time": "2026-09-06T22:12:22.510632", "exception": false, - "start_time": "2026-08-23T03:34:46.238342", + "start_time": "2026-09-06T22:12:22.503278", "status": "completed" }, "tags": [] @@ -1467,16 +1467,16 @@ "id": "idastar-test", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:46.257373Z", - "iopub.status.busy": "2026-08-23T03:34:46.257373Z", - "iopub.status.idle": "2026-08-23T03:34:46.261068Z", - "shell.execute_reply": "2026-08-23T03:34:46.260588Z" + "iopub.execute_input": "2026-09-06T22:12:22.535981Z", + "iopub.status.busy": "2026-09-06T22:12:22.535981Z", + "iopub.status.idle": "2026-09-06T22:12:22.539492Z", + "shell.execute_reply": "2026-09-06T22:12:22.538964Z" }, "papermill": { - "duration": 0.011224, - "end_time": "2026-08-23T03:34:46.261602", + "duration": 0.018523, + "end_time": "2026-09-06T22:12:22.540013", "exception": false, - "start_time": "2026-08-23T03:34:46.250378", + "start_time": "2026-09-06T22:12:22.521490", "status": "completed" }, "tags": [] @@ -1500,7 +1500,7 @@ " Noeuds explores : 8\n", " Noeuds generes : 24\n", " Frontiere max : 4\n", - " Temps : 0.10 ms\n" + " Temps : 0.09 ms\n" ] } ], @@ -1518,10 +1518,10 @@ "id": "0gzwacp0hfbq", "metadata": { "papermill": { - "duration": 0.004576, - "end_time": "2026-08-23T03:34:46.272292", + "duration": 0.005531, + "end_time": "2026-09-06T22:12:22.552115", "exception": false, - "start_time": "2026-08-23T03:34:46.267716", + "start_time": "2026-09-06T22:12:22.546584", "status": "completed" }, "tags": [] @@ -1545,10 +1545,10 @@ "id": "idastar-interpretation", "metadata": { "papermill": { - "duration": 0.004998, - "end_time": "2026-08-23T03:34:46.283809", + "duration": 0.005519, + "end_time": "2026-09-06T22:12:22.564651", "exception": false, - "start_time": "2026-08-23T03:34:46.278811", + "start_time": "2026-09-06T22:12:22.559132", "status": "completed" }, "tags": [] @@ -1576,10 +1576,10 @@ "id": "752d920b", "metadata": { "papermill": { - "duration": 0.006031, - "end_time": "2026-08-23T03:34:46.295372", + "duration": 0.012192, + "end_time": "2026-09-06T22:12:22.589947", "exception": false, - "start_time": "2026-08-23T03:34:46.289341", + "start_time": "2026-09-06T22:12:22.577755", "status": "completed" }, "tags": [] @@ -1605,16 +1605,16 @@ "id": "b119ca90", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:46.307909Z", - "iopub.status.busy": "2026-08-23T03:34:46.306909Z", - "iopub.status.idle": "2026-08-23T03:34:46.310453Z", - "shell.execute_reply": "2026-08-23T03:34:46.310453Z" + "iopub.execute_input": "2026-09-06T22:12:22.606986Z", + "iopub.status.busy": "2026-09-06T22:12:22.606986Z", + "iopub.status.idle": "2026-09-06T22:12:22.610976Z", + "shell.execute_reply": "2026-09-06T22:12:22.609962Z" }, "papermill": { - "duration": 0.010725, - "end_time": "2026-08-23T03:34:46.311629", + "duration": 0.011498, + "end_time": "2026-09-06T22:12:22.610976", "exception": false, - "start_time": "2026-08-23T03:34:46.300904", + "start_time": "2026-09-06T22:12:22.599478", "status": "completed" }, "tags": [] @@ -1645,10 +1645,10 @@ "id": "all-comparison-section", "metadata": { "papermill": { - "duration": 0.005504, - "end_time": "2026-08-23T03:34:46.323130", + "duration": 0.00659, + "end_time": "2026-09-06T22:12:22.626627", "exception": false, - "start_time": "2026-08-23T03:34:46.317626", + "start_time": "2026-09-06T22:12:22.620037", "status": "completed" }, "tags": [] @@ -1665,16 +1665,16 @@ "id": "all-comparison", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:46.336196Z", - "iopub.status.busy": "2026-08-23T03:34:46.335196Z", - "iopub.status.idle": "2026-08-23T03:34:46.341236Z", - "shell.execute_reply": "2026-08-23T03:34:46.341236Z" + "iopub.execute_input": "2026-09-06T22:12:22.641238Z", + "iopub.status.busy": "2026-09-06T22:12:22.641238Z", + "iopub.status.idle": "2026-09-06T22:12:22.648849Z", + "shell.execute_reply": "2026-09-06T22:12:22.647908Z" }, "papermill": { - "duration": 0.01204, - "end_time": "2026-08-23T03:34:46.341236", + "duration": 0.016754, + "end_time": "2026-09-06T22:12:22.649383", "exception": false, - "start_time": "2026-08-23T03:34:46.329196", + "start_time": "2026-09-06T22:12:22.632629", "status": "completed" }, "tags": [] @@ -1772,10 +1772,10 @@ "id": "all-comparison-interpretation", "metadata": { "papermill": { - "duration": 0.004999, - "end_time": "2026-08-23T03:34:46.353674", + "duration": 0.007517, + "end_time": "2026-09-06T22:12:22.663906", "exception": false, - "start_time": "2026-08-23T03:34:46.348675", + "start_time": "2026-09-06T22:12:22.656389", "status": "completed" }, "tags": [] @@ -1804,10 +1804,10 @@ "id": "heuristic-design-section", "metadata": { "papermill": { - "duration": 0.006018, - "end_time": "2026-08-23T03:34:46.365693", + "duration": 0.008347, + "end_time": "2026-09-06T22:12:22.678943", "exception": false, - "start_time": "2026-08-23T03:34:46.359675", + "start_time": "2026-09-06T22:12:22.670596", "status": "completed" }, "tags": [] @@ -1823,10 +1823,10 @@ "id": "admissibility-section", "metadata": { "papermill": { - "duration": 0.005546, - "end_time": "2026-08-23T03:34:46.377246", + "duration": 0.008249, + "end_time": "2026-09-06T22:12:22.693949", "exception": false, - "start_time": "2026-08-23T03:34:46.371700", + "start_time": "2026-09-06T22:12:22.685700", "status": "completed" }, "tags": [] @@ -1847,7 +1847,9 @@ "| Distance / 2 | Oui | Si l'heuristique originale est admissible, la diviser aussi |\n", "| Distance × 2 | Non | Peut surestimer le coût réel |\n", "\n", - "**Théorème** : A* avec une heuristique admissible est optimal (trouve une solution de coût minimal).\n" + "**Théorème** : A* avec une heuristique **consistante** est optimal (trouve une solution de coût minimal) — c'est la condition qui correspond à la variante Graph-Search implémentée dans ce notebook, qui ferme définitivement les états explorés.\n", + "\n", + "Avec une heuristique seulement **admissible**, la garantie d'optimalité exige une variante qui **ré-ouvre** un état fermé lorsqu'un meilleur $g$ arrive. Le contre-exemple ci-dessous le montre en pratique : une heuristique admissible partout, et pourtant un chemin sous-optimal.\n" ] }, { @@ -1855,10 +1857,10 @@ "id": "consistency-section", "metadata": { "papermill": { - "duration": 0.006005, - "end_time": "2026-08-23T03:34:46.388384", + "duration": 0.00721, + "end_time": "2026-09-06T22:12:22.708102", "exception": false, - "start_time": "2026-08-23T03:34:46.382379", + "start_time": "2026-09-06T22:12:22.700892", "status": "completed" }, "tags": [] @@ -1878,22 +1880,158 @@ "|-----------|------------|-------------|\n", "| Définition | $h(n) \\leq h^*(n)$ | $h(n) \\leq c(n, n') + h(n')$ |\n", "| Relation | Consistante ⇒ Admissible | Admissible n'implique pas consistante |\n", - "| Effet sur A* | Optimalité | Optimalité + pas de *re-open* |\n", + "| Effet sur A* | Insuffisante pour cette variante (contre-exemple ci-dessous) | Optimalité + pas de *re-open* |\n", "\n", "**Exemple** : Pour le 8-puzzle :\n", - "- « Tuiles mal placées » est admissible mais pas nécessairement consistante\n", + "- « Tuiles mal placées » (**case vide exclue**) est admissible **et** consistante : un mouvement fait glisser une seule tuile numérotée, donc le compte varie d'au plus 1\n", "- « Distance Manhattan » est admissible **et** consistante\n" ] }, + { + "cell_type": "markdown", + "id": "counterexample-intro", + "metadata": { + "papermill": { + "duration": 0.007448, + "end_time": "2026-09-06T22:12:22.723361", + "exception": false, + "start_time": "2026-09-06T22:12:22.715913", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Contre-exemple : admissible ne suffit pas (pour cette variante de A*)\n", + "\n", + "L'implémentation `a_star_search` (section 4) ferme définitivement chaque état exploré : un meilleur $g$ arrivant plus tard sur un état fermé est ignoré. Pour **cette** variante, la consistance est la condition qui **achète** l'optimalité — l'admissibilité seule ne suffit pas.\n", + "\n", + "Le graphe à quatre nœuds ci-dessous le démontre : l'heuristique est admissible **partout** (vérifiée exhaustivement contre $h^*$), deux arcs violent la consistance, et A* rend un chemin de coût **4** là où l'optimum est **3,5**.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "counterexample-demo", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-06T22:12:22.738745Z", + "iopub.status.busy": "2026-09-06T22:12:22.738745Z", + "iopub.status.idle": "2026-09-06T22:12:22.746297Z", + "shell.execute_reply": "2026-09-06T22:12:22.746297Z" + }, + "papermill": { + "duration": 0.016758, + "end_time": "2026-09-06T22:12:22.747604", + "exception": false, + "start_time": "2026-09-06T22:12:22.730846", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "admissible partout ? True {'S': (3.5, 3.5), 'B': (2.5, 2.5), 'A': (0.0, 2.0), 'G': (0.0, 0.0)}\n", + "arcs violant la consistance : [('S', 'A', 'c=2.0', 'h(S)=3.5 > 2.0'), ('B', 'A', 'c=0.5', 'h(B)=2.5 > 0.5')]\n", + "A* du notebook -> S -> A -> G 4.0\n", + "Optimum (Dijkstra/UCS) -> S -> B -> A -> G 3.5\n", + "Ecart : A* rend 4.0 pour un optimum de 3.5 (14% de trop)\n" + ] + } + ], + "source": [ + "# --- Contre-exemple : h admissible mais NON consistante -> A* (sans re-open) sous-optimal ---\n", + "\n", + "# Graphe oriente 4 noeuds. Optimum : S -> B -> A -> G = 1 + 0.5 + 2 = 3.5\n", + "counter_graph = {\n", + " 'S': {'A': 2.0, 'B': 1.0},\n", + " 'B': {'A': 0.5},\n", + " 'A': {'G': 2.0},\n", + " 'G': {},\n", + "}\n", + "\n", + "def h_inconsistent(state):\n", + " \"\"\"h(S)=3.5, h(B)=2.5, h(A)=0, h(G)=0 : admissible partout, non consistante.\"\"\"\n", + " return {'S': 3.5, 'B': 2.5, 'A': 0.0, 'G': 0.0}[state]\n", + "\n", + "def dijkstra_hstar(graph, goal):\n", + " \"\"\"Couts optimaux exacts h*(n) vers le but (Dijkstra sur le graphe inverse).\"\"\"\n", + " reverse = {n: {} for n in graph}\n", + " for u, nbrs in graph.items():\n", + " for v, c in nbrs.items():\n", + " reverse.setdefault(v, {})[u] = c\n", + " dist = {goal: 0.0}\n", + " heap = [(0.0, goal)]\n", + " while heap:\n", + " d, u = heapq.heappop(heap)\n", + " if d > dist.get(u, float('inf')):\n", + " continue\n", + " for v, c in reverse.get(u, {}).items():\n", + " nd = d + c\n", + " if nd < dist.get(v, float('inf')):\n", + " dist[v] = nd\n", + " heapq.heappush(heap, (nd, v))\n", + " return dist\n", + "\n", + "h_star = dijkstra_hstar(counter_graph, 'G')\n", + "\n", + "# 1) Admissibilite : verifiee EXHAUSTIVEMENT sur les 4 etats (pas un echantillon)\n", + "adm = {n: h_inconsistent(n) <= h_star[n] for n in counter_graph}\n", + "print(\"admissible partout ?\", all(adm.values()),\n", + " \" \", {n: (h_inconsistent(n), h_star[n]) for n in counter_graph})\n", + "\n", + "# 2) Consistance : les arcs qui violent h(n) <= c(n, n') + h(n')\n", + "violations = [\n", + " (u, v, c) for u, nbrs in counter_graph.items() for v, c in nbrs.items()\n", + " if h_inconsistent(u) > c + h_inconsistent(v)\n", + "]\n", + "print(\"arcs violant la consistance :\",\n", + " [(u, v, f\"c={c}\", f\"h({u})={h_inconsistent(u)} > {c + h_inconsistent(v):.1f}\")\n", + " for u, v, c in violations])\n", + "\n", + "# 3) A* du notebook (fermeture definitive) contre l'optimum Dijkstra/UCS\n", + "problem_ce = GraphProblem('S', 'G', counter_graph)\n", + "res_astar = a_star_search(problem_ce, h_inconsistent)\n", + "print(f\"A* du notebook -> {' -> '.join(res_astar.path)} {res_astar.cost}\")\n", + "print(f\"Optimum (Dijkstra/UCS) -> S -> B -> A -> G {h_star['S']}\")\n", + "print(f\"Ecart : A* rend {res_astar.cost} pour un optimum de {h_star['S']}\"\n", + " f\" ({(res_astar.cost - h_star['S']) / h_star['S']:.0%} de trop)\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "counterexample-interpretation", + "metadata": { + "papermill": { + "duration": 0.008582, + "end_time": "2026-09-06T22:12:22.762933", + "exception": false, + "start_time": "2026-09-06T22:12:22.754351", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Interprétation : pourquoi l'admissibilité ne suffit pas ici\n", + "\n", + "**Ce qui s'est passé** : A* développe S, puis A (f = 2), puis B (f = 3,5). Depuis B, un chemin de coût 1 + 0,5 = 1,5 vers A **améliorerait** le g de A (2 → 1,5) — mais A est déjà fermé, l'amélioration est ignorée. Le but est ensuite atteint via S → A → G de coût 4, alors que S → B → A → G coûte 3,5.\n", + "\n", + "**La leçon** : l'admissibilité ($h \\leq h^*$) garantit que $f$ ne sous-estime jamais le coût d'un chemin optimal passant par $n$ — mais un nœud fermé avec un $g$ non optimal ne revient jamais en frontière, et la garantie ne peut plus s'exercer. La consistance ($h(n) \\leq c(n, n') + h(n')$) maintient les $f$ monotones le long des chemins : le premier développement de chaque état est déjà optimal, ce qui rend la fermeture définitive **sans risque**.\n", + "\n", + "Ce contre-exemple est le **symétrique** de celui de la section 9 (exercice 2, #12465) : là, une heuristique **non admissible** rendait A* sous-optimal ; ici, l'heuristique est admissible **partout** et A* est sous-optimal quand même. Le couple forme la leçon complète : ce qui achète l'optimalité à **cette** variante de Graph-Search, c'est la **consistance**.\n" + ] + }, { "cell_type": "markdown", "id": "dominance-section", "metadata": { "papermill": { - "duration": 0.005524, - "end_time": "2026-08-23T03:34:46.398922", + "duration": 0.006699, + "end_time": "2026-09-06T22:12:22.777986", "exception": false, - "start_time": "2026-08-23T03:34:46.393398", + "start_time": "2026-09-06T22:12:22.771287", "status": "completed" }, "tags": [] @@ -1924,10 +2062,10 @@ "id": "puzzle-section", "metadata": { "papermill": { - "duration": 0.005015, - "end_time": "2026-08-23T03:34:46.410522", + "duration": 0.007075, + "end_time": "2026-09-06T22:12:22.791959", "exception": false, - "start_time": "2026-08-23T03:34:46.405507", + "start_time": "2026-09-06T22:12:22.784884", "status": "completed" }, "tags": [] @@ -1947,20 +2085,20 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "id": "puzzle-class", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:46.423033Z", - "iopub.status.busy": "2026-08-23T03:34:46.423033Z", - "iopub.status.idle": "2026-08-23T03:34:46.430276Z", - "shell.execute_reply": "2026-08-23T03:34:46.430276Z" + "iopub.execute_input": "2026-09-06T22:12:22.807131Z", + "iopub.status.busy": "2026-09-06T22:12:22.807131Z", + "iopub.status.idle": "2026-09-06T22:12:22.815784Z", + "shell.execute_reply": "2026-09-06T22:12:22.814950Z" }, "papermill": { - "duration": 0.014766, - "end_time": "2026-08-23T03:34:46.431289", + "duration": 0.017268, + "end_time": "2026-09-06T22:12:22.815784", "exception": false, - "start_time": "2026-08-23T03:34:46.416523", + "start_time": "2026-09-06T22:12:22.798516", "status": "completed" }, "tags": [] @@ -2098,10 +2236,10 @@ "id": "b3189c7b", "metadata": { "papermill": { - "duration": 0.006004, - "end_time": "2026-08-23T03:34:46.443294", + "duration": 0.005637, + "end_time": "2026-09-06T22:12:22.828516", "exception": false, - "start_time": "2026-08-23T03:34:46.437290", + "start_time": "2026-09-06T22:12:22.822879", "status": "completed" }, "tags": [] @@ -2112,20 +2250,20 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "id": "puzzle-benchmark", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:46.456294Z", - "iopub.status.busy": "2026-08-23T03:34:46.455294Z", - "iopub.status.idle": "2026-08-23T03:34:47.884107Z", - "shell.execute_reply": "2026-08-23T03:34:47.883599Z" + "iopub.execute_input": "2026-09-06T22:12:22.843696Z", + "iopub.status.busy": "2026-09-06T22:12:22.843696Z", + "iopub.status.idle": "2026-09-06T22:12:24.518048Z", + "shell.execute_reply": "2026-09-06T22:12:24.518048Z" }, "papermill": { - "duration": 1.435814, - "end_time": "2026-08-23T03:34:47.884107", + "duration": 1.683028, + "end_time": "2026-09-06T22:12:24.519062", "exception": false, - "start_time": "2026-08-23T03:34:46.448293", + "start_time": "2026-09-06T22:12:22.836034", "status": "completed" }, "tags": [] @@ -2145,7 +2283,7 @@ "\n", "Heuristique Noeuds Cout Temps (ms)\n", "----------------------------------------------------------------------\n", - "Tuiles mal placees 1 1 0.03\n", + "Tuiles mal placees 1 1 0.02\n", "Distance Manhattan 1 1 0.01\n", "\n", "Reduction des noeuds explores : 0.0%\n", @@ -2157,7 +2295,7 @@ "\n", "Heuristique Noeuds Cout Temps (ms)\n", "----------------------------------------------------------------------\n", - "Tuiles mal placees 770 16 4.33\n", + "Tuiles mal placees 770 16 4.46\n", "Distance Manhattan 220 16 1.52\n", "\n", "Reduction des noeuds explores : 71.4%\n", @@ -2175,8 +2313,8 @@ "\n", "Heuristique Noeuds Cout Temps (ms)\n", "----------------------------------------------------------------------\n", - "Tuiles mal placees 143839 31 1143.01\n", - "Distance Manhattan 20290 31 251.84\n", + "Tuiles mal placees 143839 31 1414.71\n", + "Distance Manhattan 20290 31 223.80\n", "\n", "Reduction des noeuds explores : 85.9%\n" ] @@ -2184,11 +2322,11 @@ { "data": { "text/plain": [ - "(<__main__.SearchResult at 0x13ac250fa10>,\n", - " <__main__.SearchResult at 0x13ac2051e50>)" + "(<__main__.SearchResult at 0x17867552610>,\n", + " <__main__.SearchResult at 0x17826fef550>)" ] }, - "execution_count": 16, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -2237,10 +2375,10 @@ "id": "k292t2mslp", "metadata": { "papermill": { - "duration": 0.006013, - "end_time": "2026-08-23T03:34:47.897126", + "duration": 0.007178, + "end_time": "2026-09-06T22:12:24.533254", "exception": false, - "start_time": "2026-08-23T03:34:47.891113", + "start_time": "2026-09-06T22:12:24.526076", "status": "completed" }, "tags": [] @@ -2265,10 +2403,10 @@ "id": "puzzle-interpretation", "metadata": { "papermill": { - "duration": 0.008305, - "end_time": "2026-08-23T03:34:47.911955", + "duration": 0.007024, + "end_time": "2026-09-06T22:12:24.547889", "exception": false, - "start_time": "2026-08-23T03:34:47.903650", + "start_time": "2026-09-06T22:12:24.540865", "status": "completed" }, "tags": [] @@ -2296,10 +2434,10 @@ "id": "d7b928ea", "metadata": { "papermill": { - "duration": 0.006774, - "end_time": "2026-08-23T03:34:47.924483", + "duration": 0.007252, + "end_time": "2026-09-06T22:12:24.562143", "exception": false, - "start_time": "2026-08-23T03:34:47.917709", + "start_time": "2026-09-06T22:12:24.554891", "status": "completed" }, "tags": [] @@ -2327,20 +2465,20 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "id": "b55555ba", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:47.937805Z", - "iopub.status.busy": "2026-08-23T03:34:47.937805Z", - "iopub.status.idle": "2026-08-23T03:34:47.941092Z", - "shell.execute_reply": "2026-08-23T03:34:47.941092Z" + "iopub.execute_input": "2026-09-06T22:12:24.576823Z", + "iopub.status.busy": "2026-09-06T22:12:24.576823Z", + "iopub.status.idle": "2026-09-06T22:12:24.579899Z", + "shell.execute_reply": "2026-09-06T22:12:24.579899Z" }, "papermill": { - "duration": 0.011291, - "end_time": "2026-08-23T03:34:47.942097", + "duration": 0.011791, + "end_time": "2026-09-06T22:12:24.580911", "exception": false, - "start_time": "2026-08-23T03:34:47.930806", + "start_time": "2026-09-06T22:12:24.569120", "status": "completed" }, "tags": [] @@ -2374,10 +2512,10 @@ "id": "visualization-section", "metadata": { "papermill": { - "duration": 0.008028, - "end_time": "2026-08-23T03:34:47.956124", + "duration": 0.014498, + "end_time": "2026-09-06T22:12:24.609431", "exception": false, - "start_time": "2026-08-23T03:34:47.948096", + "start_time": "2026-09-06T22:12:24.594933", "status": "completed" }, "tags": [] @@ -2390,20 +2528,20 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "id": "puzzle-visualization", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:47.970129Z", - "iopub.status.busy": "2026-08-23T03:34:47.970129Z", - "iopub.status.idle": "2026-08-23T03:34:48.266973Z", - "shell.execute_reply": "2026-08-23T03:34:48.266442Z" + "iopub.execute_input": "2026-09-06T22:12:24.636730Z", + "iopub.status.busy": "2026-09-06T22:12:24.636730Z", + "iopub.status.idle": "2026-09-06T22:12:24.988639Z", + "shell.execute_reply": "2026-09-06T22:12:24.988639Z" }, "papermill": { - "duration": 0.304771, - "end_time": "2026-08-23T03:34:48.267977", + "duration": 0.369238, + "end_time": "2026-09-06T22:12:24.989959", "exception": false, - "start_time": "2026-08-23T03:34:47.963206", + "start_time": "2026-09-06T22:12:24.620721", "status": "completed" }, "tags": [] @@ -2525,10 +2663,10 @@ "id": "visualization-interpretation", "metadata": { "papermill": { - "duration": 0.006006, - "end_time": "2026-08-23T03:34:48.281022", + "duration": 0.00733, + "end_time": "2026-09-06T22:12:25.004914", "exception": false, - "start_time": "2026-08-23T03:34:48.275016", + "start_time": "2026-09-06T22:12:24.997584", "status": "completed" }, "tags": [] @@ -2551,10 +2689,10 @@ "id": "summary-table-section", "metadata": { "papermill": { - "duration": 0.006398, - "end_time": "2026-08-23T03:34:48.295559", + "duration": 0.006928, + "end_time": "2026-09-06T22:12:25.019803", "exception": false, - "start_time": "2026-08-23T03:34:48.289161", + "start_time": "2026-09-06T22:12:25.012875", "status": "completed" }, "tags": [] @@ -2567,20 +2705,20 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 20, "id": "final-comparison", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:48.317524Z", - "iopub.status.busy": "2026-08-23T03:34:48.317524Z", - "iopub.status.idle": "2026-08-23T03:34:48.443041Z", - "shell.execute_reply": "2026-08-23T03:34:48.443041Z" + "iopub.execute_input": "2026-09-06T22:12:25.035220Z", + "iopub.status.busy": "2026-09-06T22:12:25.035220Z", + "iopub.status.idle": "2026-09-06T22:12:25.183313Z", + "shell.execute_reply": "2026-09-06T22:12:25.183313Z" }, "papermill": { - "duration": 0.137514, - "end_time": "2026-08-23T03:34:48.444046", + "duration": 0.157151, + "end_time": "2026-09-06T22:12:25.184327", "exception": false, - "start_time": "2026-08-23T03:34:48.306532", + "start_time": "2026-09-06T22:12:25.027176", "status": "completed" }, "tags": [] @@ -2765,10 +2903,10 @@ "id": "summary-interpretation", "metadata": { "papermill": { - "duration": 0.007203, - "end_time": "2026-08-23T03:34:48.458251", + "duration": 0.008015, + "end_time": "2026-09-06T22:12:25.199346", "exception": false, - "start_time": "2026-08-23T03:34:48.451048", + "start_time": "2026-09-06T22:12:25.191331", "status": "completed" }, "tags": [] @@ -2789,7 +2927,7 @@ "| IDA* | Oui | Oui** | $f(n)$ itérée | Oui |\n", "\n", "\\* Optimal uniquement avec coûts uniformes. \n", - "\\*\\* Optimal avec heuristique admissible.\n", + "\\*\\* A\\* : optimal avec heuristique **consistante** (variante Graph-Search sans ré-ouverture, contre-exemple §6) ; IDA\\* : optimal avec heuristique admissible (pas d'ensemble fermé).\n", "\n", "**Guide de choix pratique** :\n", "\n", @@ -2807,10 +2945,10 @@ "id": "exercises-section", "metadata": { "papermill": { - "duration": 0.006421, - "end_time": "2026-08-23T03:34:48.470673", + "duration": 0.008003, + "end_time": "2026-09-06T22:12:25.215917", "exception": false, - "start_time": "2026-08-23T03:34:48.464252", + "start_time": "2026-09-06T22:12:25.207914", "status": "completed" }, "tags": [] @@ -2825,20 +2963,20 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 21, "id": "exercise1", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:48.485046Z", - "iopub.status.busy": "2026-08-23T03:34:48.485046Z", - "iopub.status.idle": "2026-08-23T03:34:48.488182Z", - "shell.execute_reply": "2026-08-23T03:34:48.488182Z" + "iopub.execute_input": "2026-09-06T22:12:25.233776Z", + "iopub.status.busy": "2026-09-06T22:12:25.233776Z", + "iopub.status.idle": "2026-09-06T22:12:25.236803Z", + "shell.execute_reply": "2026-09-06T22:12:25.236803Z" }, "papermill": { - "duration": 0.010348, - "end_time": "2026-08-23T03:34:48.488182", + "duration": 0.012372, + "end_time": "2026-09-06T22:12:25.236803", "exception": false, - "start_time": "2026-08-23T03:34:48.477834", + "start_time": "2026-09-06T22:12:25.224431", "status": "completed" }, "tags": [] @@ -2861,10 +2999,10 @@ "id": "exercise2", "metadata": { "papermill": { - "duration": 0.007526, - "end_time": "2026-08-23T03:34:48.501961", + "duration": 0.008231, + "end_time": "2026-09-06T22:12:25.253184", "exception": false, - "start_time": "2026-08-23T03:34:48.494435", + "start_time": "2026-09-06T22:12:25.244953", "status": "completed" }, "tags": [] @@ -2877,20 +3015,20 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 22, "id": "exercise2-solution", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:48.515101Z", - "iopub.status.busy": "2026-08-23T03:34:48.515101Z", - "iopub.status.idle": "2026-08-23T03:34:48.520216Z", - "shell.execute_reply": "2026-08-23T03:34:48.520216Z" + "iopub.execute_input": "2026-09-06T22:12:25.271147Z", + "iopub.status.busy": "2026-09-06T22:12:25.269633Z", + "iopub.status.idle": "2026-09-06T22:12:25.276236Z", + "shell.execute_reply": "2026-09-06T22:12:25.276236Z" }, "papermill": { - "duration": 0.012725, - "end_time": "2026-08-23T03:34:48.520216", + "duration": 0.015712, + "end_time": "2026-09-06T22:12:25.277434", "exception": false, - "start_time": "2026-08-23T03:34:48.507491", + "start_time": "2026-09-06T22:12:25.261722", "status": "completed" }, "tags": [] @@ -2963,10 +3101,10 @@ "id": "afc1e48a", "metadata": { "papermill": { - "duration": 0.00612, - "end_time": "2026-08-23T03:34:48.534587", + "duration": 0.007998, + "end_time": "2026-09-06T22:12:25.293603", "exception": false, - "start_time": "2026-08-23T03:34:48.528467", + "start_time": "2026-09-06T22:12:25.285605", "status": "completed" }, "tags": [] @@ -2985,20 +3123,20 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 23, "id": "6f576740", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:48.547595Z", - "iopub.status.busy": "2026-08-23T03:34:48.547595Z", - "iopub.status.idle": "2026-08-23T03:34:48.555533Z", - "shell.execute_reply": "2026-08-23T03:34:48.555533Z" + "iopub.execute_input": "2026-09-06T22:12:25.312531Z", + "iopub.status.busy": "2026-09-06T22:12:25.311532Z", + "iopub.status.idle": "2026-09-06T22:12:25.320079Z", + "shell.execute_reply": "2026-09-06T22:12:25.320079Z" }, "papermill": { - "duration": 0.015963, - "end_time": "2026-08-23T03:34:48.556550", + "duration": 0.018326, + "end_time": "2026-09-06T22:12:25.321092", "exception": false, - "start_time": "2026-08-23T03:34:48.540587", + "start_time": "2026-09-06T22:12:25.302766", "status": "completed" }, "tags": [] @@ -3092,10 +3230,10 @@ "id": "c36f7294", "metadata": { "papermill": { - "duration": 0.007, - "end_time": "2026-08-23T03:34:48.569984", + "duration": 0.008988, + "end_time": "2026-09-06T22:12:25.337128", "exception": false, - "start_time": "2026-08-23T03:34:48.562984", + "start_time": "2026-09-06T22:12:25.328140", "status": "completed" }, "tags": [] @@ -3112,20 +3250,20 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 24, "id": "764c6b27", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:48.584513Z", - "iopub.status.busy": "2026-08-23T03:34:48.583512Z", - "iopub.status.idle": "2026-08-23T03:34:48.589689Z", - "shell.execute_reply": "2026-08-23T03:34:48.589689Z" + "iopub.execute_input": "2026-09-06T22:12:25.354238Z", + "iopub.status.busy": "2026-09-06T22:12:25.354238Z", + "iopub.status.idle": "2026-09-06T22:12:25.361736Z", + "shell.execute_reply": "2026-09-06T22:12:25.361736Z" }, "papermill": { - "duration": 0.014177, - "end_time": "2026-08-23T03:34:48.590705", + "duration": 0.017551, + "end_time": "2026-09-06T22:12:25.362746", "exception": false, - "start_time": "2026-08-23T03:34:48.576528", + "start_time": "2026-09-06T22:12:25.345195", "status": "completed" }, "tags": [] @@ -3182,10 +3320,10 @@ "id": "6ac45e53", "metadata": { "papermill": { - "duration": 0.006045, - "end_time": "2026-08-23T03:34:48.602346", + "duration": 0.008817, + "end_time": "2026-09-06T22:12:25.379078", "exception": false, - "start_time": "2026-08-23T03:34:48.596301", + "start_time": "2026-09-06T22:12:25.370261", "status": "completed" }, "tags": [] @@ -3216,10 +3354,10 @@ "id": "exercise3", "metadata": { "papermill": { - "duration": 0.007004, - "end_time": "2026-08-23T03:34:48.616610", + "duration": 0.008107, + "end_time": "2026-09-06T22:12:25.395252", "exception": false, - "start_time": "2026-08-23T03:34:48.609606", + "start_time": "2026-09-06T22:12:25.387145", "status": "completed" }, "tags": [] @@ -3237,20 +3375,20 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 25, "id": "exercise3-solution", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:48.631854Z", - "iopub.status.busy": "2026-08-23T03:34:48.630854Z", - "iopub.status.idle": "2026-08-23T03:34:48.636799Z", - "shell.execute_reply": "2026-08-23T03:34:48.636799Z" + "iopub.execute_input": "2026-09-06T22:12:25.413040Z", + "iopub.status.busy": "2026-09-06T22:12:25.412003Z", + "iopub.status.idle": "2026-09-06T22:12:25.418286Z", + "shell.execute_reply": "2026-09-06T22:12:25.418286Z" }, "papermill": { - "duration": 0.014153, - "end_time": "2026-08-23T03:34:48.637857", + "duration": 0.016023, + "end_time": "2026-09-06T22:12:25.419299", "exception": false, - "start_time": "2026-08-23T03:34:48.623704", + "start_time": "2026-09-06T22:12:25.403276", "status": "completed" }, "tags": [] @@ -3262,9 +3400,9 @@ "text": [ "Heuristique MST pour le TSP (sur le graphe des villes françaises)\n", "=================================================================\n", - " current=Bordeaux unvisited={'Marseille', 'Paris', 'Lyon'} → h=1224 km\n", - " current=Paris unvisited={'Marseille', 'Nantes', 'Toulouse', 'Lyon'} → h=1527 km\n", - " current=Lyon unvisited={'Nice', 'Grenoble'} → h=410 km\n", + " current=Bordeaux unvisited={'Lyon', 'Marseille', 'Paris'} → h=1224 km\n", + " current=Paris unvisited={'Lyon', 'Marseille', 'Toulouse', 'Nantes'} → h=1527 km\n", + " current=Lyon unvisited={'Grenoble', 'Nice'} → h=410 km\n", "\n", "Propriétés :\n", " ✓ Admissible : le MST est une borne inférieure sur tout tour restant\n", @@ -3358,10 +3496,10 @@ "id": "exercise4", "metadata": { "papermill": { - "duration": 0.008002, - "end_time": "2026-08-23T03:34:48.652806", + "duration": 0.007059, + "end_time": "2026-09-06T22:12:25.434360", "exception": false, - "start_time": "2026-08-23T03:34:48.644804", + "start_time": "2026-09-06T22:12:25.427301", "status": "completed" }, "tags": [] @@ -3376,20 +3514,20 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 26, "id": "exercise4-solution", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:48.668811Z", - "iopub.status.busy": "2026-08-23T03:34:48.668811Z", - "iopub.status.idle": "2026-08-23T03:34:48.741814Z", - "shell.execute_reply": "2026-08-23T03:34:48.741814Z" + "iopub.execute_input": "2026-09-06T22:12:25.452630Z", + "iopub.status.busy": "2026-09-06T22:12:25.452630Z", + "iopub.status.idle": "2026-09-06T22:12:25.526216Z", + "shell.execute_reply": "2026-09-06T22:12:25.526216Z" }, "papermill": { - "duration": 0.082006, - "end_time": "2026-08-23T03:34:48.741814", + "duration": 0.083635, + "end_time": "2026-09-06T22:12:25.527229", "exception": false, - "start_time": "2026-08-23T03:34:48.659808", + "start_time": "2026-09-06T22:12:25.443594", "status": "completed" }, "tags": [] @@ -3552,10 +3690,10 @@ "id": "97bc629e", "metadata": { "papermill": { - "duration": 0.007026, - "end_time": "2026-08-23T03:34:48.756194", + "duration": 0.007585, + "end_time": "2026-09-06T22:12:25.542429", "exception": false, - "start_time": "2026-08-23T03:34:48.749168", + "start_time": "2026-09-06T22:12:25.534844", "status": "completed" }, "tags": [] @@ -3584,20 +3722,20 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 27, "id": "2a6adc42", "metadata": { "execution": { - "iopub.execute_input": "2026-08-23T03:34:48.771831Z", - "iopub.status.busy": "2026-08-23T03:34:48.770830Z", - "iopub.status.idle": "2026-08-23T03:34:48.776507Z", - "shell.execute_reply": "2026-08-23T03:34:48.776507Z" + "iopub.execute_input": "2026-09-06T22:12:25.558431Z", + "iopub.status.busy": "2026-09-06T22:12:25.558431Z", + "iopub.status.idle": "2026-09-06T22:12:25.563934Z", + "shell.execute_reply": "2026-09-06T22:12:25.563430Z" }, "papermill": { - "duration": 0.012676, - "end_time": "2026-08-23T03:34:48.776507", + "duration": 0.015508, + "end_time": "2026-09-06T22:12:25.564939", "exception": false, - "start_time": "2026-08-23T03:34:48.763831", + "start_time": "2026-09-06T22:12:25.549431", "status": "completed" }, "tags": [] @@ -3607,14 +3745,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Exercice 5 : implementez les TODO ci-dessus." - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" + "Exercice 5 : implementez les TODO ci-dessus.\n" ] } ], @@ -3694,10 +3825,10 @@ "id": "conclusion-section", "metadata": { "papermill": { - "duration": 0.005998, - "end_time": "2026-08-23T03:34:48.790869", + "duration": 0.008165, + "end_time": "2026-09-06T22:12:25.582108", "exception": false, - "start_time": "2026-08-23T03:34:48.784871", + "start_time": "2026-09-06T22:12:25.573943", "status": "completed" }, "tags": [] @@ -3720,13 +3851,13 @@ "| Algorithme | Critère | Optimal | Mémoire |\n", "|------------|----------|---------|----------|\n", "| Greedy | Minimiser $h(n)$ | Non | $O(b^m)$ |\n", - "| A* | Minimiser $g(n) + h(n)$ | Oui (si $h$ admissible) | $O(b^d)$ |\n", + "| A* | Minimiser $g(n) + h(n)$ | Oui (si $h$ consistante) | $O(b^d)$ |\n", "| IDA* | DFS itéré par $f(n)$ | Oui | $O(bd)$ |\n", "\n", "### Ce qu'il faut retenir\n", "\n", "1. **L'heuristique est la clé** : une bonne heuristique peut réduire l'explosion combinatoire de plusieurs ordres de grandeur\n", - "2. **A* est l'algo de référence** : optimal avec heuristique admissible, utilisable dans la plupart des cas\n", + "2. **A* est l'algo de référence** : optimal avec heuristique consistante (admissible seule ne suffit pas sans ré-ouverture, cf §6), utilisable dans la plupart des cas\n", "3. **Greedy pour la vitesse** : quand une solution « assez bonne » suffit\n", "4. **IDA* pour la mémoire** : même optimalité que A*, avec une consommation linéaire\n", "5. **La dominance** : une heuristique plus informée (dominante) explore toujours moins de nœuds\n", @@ -3747,10 +3878,10 @@ "id": "nav-footer", "metadata": { "papermill": { - "duration": 0.00652, - "end_time": "2026-08-23T03:34:48.804936", + "duration": 0.009617, + "end_time": "2026-09-06T22:12:25.599994", "exception": false, - "start_time": "2026-08-23T03:34:48.798416", + "start_time": "2026-09-06T22:12:25.590377", "status": "completed" }, "tags": [] @@ -3811,17 +3942,17 @@ }, "papermill": { "default_parameters": {}, - "duration": 4.96378, - "end_time": "2026-08-23T03:34:49.165733", + "duration": 8.127561, + "end_time": "2026-09-06T22:12:25.969581", "environment_variables": {}, "exception": null, - "input_path": "Search-03-Informed.ipynb", - "output_path": "12465_search3_exec.ipynb", + "input_path": "MyIA.AI.Notebooks/Search/Part1-Foundations/Search-03-Informed.ipynb", + "output_path": "MyIA.AI.Notebooks/Search/Part1-Foundations/Search-03-Informed.ipynb", "parameters": {}, - "start_time": "2026-08-23T03:34:44.201953", + "start_time": "2026-09-06T22:12:17.842020", "version": "2.6.0" } }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/MyIA.AI.Notebooks/Search/Part1-Foundations/Search-03d-WeightedAstar.ipynb b/MyIA.AI.Notebooks/Search/Part1-Foundations/Search-03d-WeightedAstar.ipynb index d4ed999f4e..ec9d88aaa9 100644 --- a/MyIA.AI.Notebooks/Search/Part1-Foundations/Search-03d-WeightedAstar.ipynb +++ b/MyIA.AI.Notebooks/Search/Part1-Foundations/Search-03d-WeightedAstar.ipynb @@ -17,7 +17,7 @@ "## 1. L'idée : payer l'optimalité pour acheter de la vitesse\n", "\n", "[Search-3 (Informed)](../Part1-Foundations/Search-03-Informed.ipynb) a posé le\n", - "compromis fondateur : **A\\*** (admissible) est optimal mais peut explorer beaucoup\n", + "compromis fondateur : **A\\*** (heuristique consistante pour la variante sans ré-ouverture) est optimal mais peut explorer beaucoup\n", "de nœuds ; **Greedy** (sans `g`) est rapide mais peut renvoyer une solution\n", "carrément mauvaise. Entre l'optimalité chère et la gloutonnerie aveugle, existe-t-il\n", "un **juste milieu garanti** ?\n", diff --git a/scripts/notebook_tools/twin_pairs.d/search-03-informed.yaml b/scripts/notebook_tools/twin_pairs.d/search-03-informed.yaml index 625978d328..600a9e6dee 100644 --- a/scripts/notebook_tools/twin_pairs.d/search-03-informed.yaml +++ b/scripts/notebook_tools/twin_pairs.d/search-03-informed.yaml @@ -80,6 +80,13 @@ csharp_sha: 1ee2085498a0d498b6ff3c975c2c0ef3e51b3833 content_python_sha: e013f8349ff4a758216eb90f74e5dc8f926683738bdc8de50a4fa1cefd39c630 content_csharp_sha: c3dfe1281305edc7800cb4b298751147ce76cc3c142b3886f41dac2a5da5dbca + - date: "2026-09-07" + by: myia-po-2026:CoursIA + python_sha: 262eb2daab28ef942cf281d8cf527a0ae200ae48 + csharp_sha: 1810b37571f1cc28a8d7cce934cedee33328b469 + content_python_sha: d76c6973ae30afa9730eedc02ca951184dd5a626684fdbbcf88e2f44e117a904 + content_csharp_sha: 9266001db0b3b03023c359584137b11b9b93900aa45a641257ec0bd57ac6dc5b + reason: "PR #14824 (issue) : theoreme admissible=>optimal limite a la consistance (variante Graph-Search sans re-open). Python : enonces c19/c25/c38/c39/c54/c70 corriges + 3 cellules contre-exemple (graphe 4 noeuds, h admissible exhaustive, A* rend 4.0 vs optimum Dijkstra 3.5), re-exec papermill complete. C# : enonces c0/c9/c20/c24/c34 corriges (markdown-only, outputs precedents valides, pas de re-exec requis)." known_differences: - "Socle pedagogique commun : A* et Greedy Best-First avec differentes heuristiques (distance Manhattan, euclidienne, h=0=degenere en UCS). Arrive en Roumanie + 8-puzzle." - "Idiomes framework : Python = `import heapq` (priority queue avec (f, tie_breaker, noeud)). C# = `PriorityQueue` (f-cost = g + h)." diff --git a/scripts/notebook_tools/twin_pairs.d/search-14-weighted-a.yaml b/scripts/notebook_tools/twin_pairs.d/search-14-weighted-a.yaml index 4a5e5efccb..52d88e7539 100644 --- a/scripts/notebook_tools/twin_pairs.d/search-14-weighted-a.yaml +++ b/scripts/notebook_tools/twin_pairs.d/search-14-weighted-a.yaml @@ -36,6 +36,13 @@ csharp_sha: 31bfe385f948f0f69d3cda54067f73696607447d content_python_sha: 2adfd99c41d905a67d2093e6450151bcd2cb8cd373e0f5140676740c61626aff content_csharp_sha: d478f19b813a60e9da0fadd408ccb81e5055cec5f4108bd13cdb26350986349c + - date: "2026-09-07" + by: myia-po-2026:CoursIA + python_sha: ec9d88aaa9c3ed3eb3b0f12534e0c8b0d4d06636 + csharp_sha: 31bfe385f948f0f69d3cda54067f73696607447d + content_python_sha: 9da03367b8539cc3ab4f15db67dacc44673b998593db12ff0347219536ce7592 + content_csharp_sha: d478f19b813a60e9da0fadd408ccb81e5055cec5f4108bd13cdb26350986349c + reason: "PR #14824 (issue) : Search-03d c0 une ligne reconciliee (A* optimal exige h consistante pour la variante sans re-ouverture). Markdown-only, outputs precedents valides. C# 03d inchange : ses phrasings c3/c4 ('W=1 -> A* optimal, admissible') signales en residuel dans le body PR, hors enumeration de l'issue." bridge_verdict: SOTA-OK bridge_verdict_reason: 'Python = heapq (min-heap) + itertools.product + random.Random(seed), from-scratch. C# = PriorityQueue (.NET 6+) + Random(seed), from-scratch. Verdict SOTA-OK (flip 2026-08-22, c.1301+335, myia-po-2025:CoursIA-2, issue #10382) : mislabel corrigige -- la reason anterieure concluat ''pedagogique from-scratch, pas de solveur tiers applicable'' (Weighted A* = algorithme pedagogique), qui contredit RECOVERABLE-LOCAL. Verifie firsthand : 3 imports Python / 1 using C#, aucun moteur tiers. Le balayage W sur f(n) = g(n) + W*h(n) est le sujet meme du chapitre ; PriorityQueue/heapq sont deja les primitives canoniques de leurs ecosystemes respectifs. From-scratch assumé des deux cotes, precedent GT-15 (PR #12312). parity_level preserve.' known_differences: