{
  "chapter": {
    "id": "recherche-dichotomique",
    "level": "premiere",
    "theme": "Algorithmique",
    "title": "Recherche dichotomique",
    "description": "Recherche dichotomique dans une liste triée, principe\nd'élimination de la moitié, calcul du milieu, indices\ngauche/droite, coût logarithmique, implémentation\nitérative, terminaison à l'aide d'un variant de boucle,\npièges classiques (bornes, doublons).",
    "parties": [
      {
        "id": "principe",
        "title": "Principe et préconditions"
      },
      {
        "id": "code",
        "title": "Écriture et traces"
      },
      {
        "id": "preuve",
        "title": "Terminaison, invariant et coût"
      }
    ],
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition"
      ],
      "title": "Principe",
      "partie": "principe",
      "statement": "Quel est le principe de la **recherche dichotomique** ?",
      "options": [
        {
          "text": "Trier la liste à nouveau avant chaque nouvelle comparaison",
          "correct": false,
          "feedback": "Erreur : la liste doit être triée **avant**\nla recherche, pas pendant.\n"
        },
        {
          "text": "Utiliser un dictionnaire pour accéder directement à la cible",
          "correct": false,
          "feedback": "Erreur : c'est une autre stratégie (accès\ndirect par clé), pas la dichotomie.\n"
        },
        {
          "text": "Parcourir la liste élément par élément depuis le début",
          "correct": false,
          "feedback": "Erreur : c'est la recherche **linéaire**.\n"
        },
        {
          "text": "Comparer la cible au milieu et ne garder qu'une moitié",
          "correct": true,
          "feedback": "Bonne réponse : à chaque étape, on **élimine la moitié** de la liste, ce qui donne une\ncomplexité logarithmique.\n"
        }
      ],
      "explanation": "« Dichotomie » vient du grec « di » (deux) +\n« tomos » (couper) : couper en deux. À chaque\nétape, on coupe l'intervalle de recherche par\ndeux."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "precondition"
      ],
      "title": "Précondition",
      "partie": "principe",
      "statement": "La recherche dichotomique exige-t-elle une\ncondition particulière sur la liste ?",
      "options": [
        {
          "text": "La liste doit être de taille au moins 100",
          "correct": false,
          "feedback": "Erreur : aucune condition de taille minimale.\n"
        },
        {
          "text": "La liste doit être de longueur paire",
          "correct": false,
          "feedback": "Erreur : la dichotomie fonctionne sur\nn'importe quelle longueur.\n"
        },
        {
          "text": "La liste doit contenir uniquement des entiers",
          "correct": false,
          "feedback": "Erreur : la dichotomie fonctionne sur tout\ntype comparable (entiers, flottants, chaînes).\n"
        },
        {
          "text": "La liste doit être triée",
          "correct": true,
          "feedback": "Bonne réponse : c'est la condition\nessentielle. Sans tri, la comparaison au\nmilieu ne permet pas de savoir dans quelle\nmoitié chercher.\n"
        }
      ],
      "explanation": "Si la liste n'est pas triée, on ne peut pas\nconclure qu'un élément cherché se trouve « avant »\nou « après » le milieu. C'est l'hypothèse-clé."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "complexite"
      ],
      "title": "Complexité",
      "partie": "preuve",
      "statement": "Quelle est la **complexité dans le pire cas** de\nla recherche dichotomique ?",
      "options": [
        {
          "text": "O(1)",
          "correct": false,
          "feedback": "Erreur : on doit faire plusieurs comparaisons\ndans le pire cas.\n"
        },
        {
          "text": "O(n log n)",
          "correct": false,
          "feedback": "Erreur : ce n'est pas le coût d'une recherche ;\nil dépasse même celui d'une recherche linéaire,\nO(n).\n"
        },
        {
          "text": "O(log n)",
          "correct": true,
          "feedback": "Bonne réponse : à chaque étape on divise\nl'intervalle par 2, donc le nombre d'étapes\nest `log₂(n)`.\n"
        },
        {
          "text": "O(n)",
          "correct": false,
          "feedback": "Erreur : c'est la complexité de la recherche\n**linéaire**.\n"
        }
      ],
      "explanation": "Pour 10⁶ éléments : environ 20 comparaisons. Pour 10⁹\néléments : environ 30 comparaisons. La dichotomie\npasse à l'échelle remarquablement."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "milieu"
      ],
      "title": "Calcul du milieu",
      "partie": "principe",
      "statement": "Avec deux indices `g` (gauche) et `d` (droite),\ncomment calcule-t-on l'**indice du milieu** en\nPython ?",
      "options": [
        {
          "text": "`m = (g + d) // 2`",
          "correct": true,
          "feedback": "Bonne réponse : moyenne entière des deux\nindices. Le `//` (division entière) garantit\nun indice valide.\n"
        },
        {
          "text": "`m = g - d`",
          "correct": false,
          "feedback": "Erreur : différence, pas milieu.\n"
        },
        {
          "text": "`m = g + d`",
          "correct": false,
          "feedback": "Erreur : ce n'est pas le milieu, c'est la\nsomme.\n"
        },
        {
          "text": "`m = (g + d) / 2`",
          "correct": false,
          "feedback": "Erreur : `/` produit un **flottant** en\nPython 3, qui ne peut pas servir d'indice.\n"
        }
      ],
      "explanation": "Variante équivalente :\n`m = g + (d - g) // 2`. C'est plus robuste sur\ncertains langages où `g + d` peut déborder, mais\nen Python (entiers de précision arbitraire),\n`(g + d) // 2` suffit."
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "comparaison"
      ],
      "title": "Élimination de moitié",
      "partie": "principe",
      "statement": "Si la valeur à l'indice du milieu est **plus petite** que la cible (et la liste est triée\ncroissant), où chercher ensuite ?",
      "options": [
        {
          "text": "Dans la moitié droite",
          "correct": true,
          "feedback": "Bonne réponse : si le milieu est plus petit\nque la cible, alors toutes les valeurs à\ngauche le sont aussi (liste triée). On peut\nles éliminer.\n"
        },
        {
          "text": "Dans la moitié gauche",
          "correct": false,
          "feedback": "Erreur : à gauche, les valeurs sont **encore plus petites** (liste triée croissant).\n"
        },
        {
          "text": "Dans l'ensemble de la liste",
          "correct": false,
          "feedback": "Erreur : l'intérêt de la dichotomie est\nprécisément d'**éliminer** la moitié.\n"
        },
        {
          "text": "Au milieu, à nouveau",
          "correct": false,
          "feedback": "Erreur : on resterait sur la même valeur\nindéfiniment (boucle infinie).\n"
        }
      ],
      "explanation": "Dans le code itératif, on fait `g = m + 1` (et\nnon `g = m`, sinon boucle infinie possible)."
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "code-iteratif"
      ],
      "title": "Code itératif",
      "partie": "code",
      "statement": "Quel code implémente correctement la recherche\ndichotomique itérative (renvoie l'indice ou -1) ?",
      "options": [
        {
          "text": "```python\ndef dicho(liste, cible):\n    g, d = 0, len(liste)\n    while g < d:\n        m = (g + d) // 2\n        if liste[m] == cible:\n            return m\n        g = m\n        d = m\n    return -1\n```\n",
          "correct": false,
          "feedback": "Erreur : `g = m` puis `d = m` vide\nl'intervalle dès la première itération\n(`g == d`, la boucle s'arrête) : la\nfonction renvoie -1 même quand la cible\nest présente, sauf si elle est exactement\nau milieu.\n"
        },
        {
          "text": "```python\ndef dicho(liste, cible):\n    i = 0\n    while i < len(liste):\n        if liste[i] == cible:\n            return i\n        i = i + 1\n    return -1\n```\n",
          "correct": false,
          "feedback": "Erreur : c'est une recherche **linéaire**\ndéguisée : on parcourt les indices un par\nun depuis le début, sans jamais couper\nl'intervalle en deux. Coût O(n) au lieu\nde O(log n).\n"
        },
        {
          "text": "```python\ndef dicho(liste, cible):\n    g, d = 0, len(liste) - 1\n    while g <= d:\n        m = (g + d) // 2\n        if liste[m] == cible:\n            return m\n        elif liste[m] < cible:\n            g = m + 1\n        else:\n            d = m - 1\n    return -1\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : structure classique avec deux\nindices, mise à jour `m + 1` ou `m - 1` pour\néviter les boucles infinies, retour `-1` si\nla cible est absente.\n"
        },
        {
          "text": "```python\ndef dicho(liste, cible):\n    g, d = 0, len(liste)\n    while g <= d:\n        m = (g + d) // 2\n        if liste[m] == cible:\n            return m\n        elif liste[m] < cible:\n            g = m + 1\n        else:\n            d = m - 1\n    return -1\n```",
          "correct": false,
          "feedback": "Erreur : avec `d = len(liste)` et la condition\n`g <= d`, le milieu peut valoir `len(liste)`.\nPour une cible plus grande que tous les\néléments, `liste[m]` sort de la liste et\nprovoque `IndexError`.\n"
        }
      ],
      "explanation": "Trois mises à jour d'indices selon la\ncomparaison : égal (trouvé), inférieur (chercher\nà droite), supérieur (chercher à gauche)."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "borne"
      ],
      "title": "Sortie de boucle",
      "partie": "code",
      "statement": "Dans le code itératif `while g <= d:`, quand la\nboucle se termine-t-elle si la cible est absente ?",
      "options": [
        {
          "text": "Exactement quand les deux indices `g` et `d` deviennent égaux",
          "correct": false,
          "feedback": "Erreur : `g == d` satisfait encore `g <= d`,\nla boucle continue.\n"
        },
        {
          "text": "Au bout de 10 itérations",
          "correct": false,
          "feedback": "Erreur : aucune limite fixe d'itérations.\n"
        },
        {
          "text": "Quand `g > d` (indices croisés)",
          "correct": true,
          "feedback": "Bonne réponse : à chaque échec, l'intervalle\nrétrécit. Quand il devient vide (`g > d`), on\nsort et on renvoie -1.\n"
        },
        {
          "text": "Quand on rencontre une exception",
          "correct": false,
          "feedback": "Erreur : un code correct ne lève pas\nd'exception.\n"
        }
      ],
      "explanation": "C'est la condition d'arrêt « propre » : tant\nqu'il reste au moins un élément candidat\n(`g <= d`), on continue ; sinon, élément absent."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "vide"
      ],
      "title": "Liste vide",
      "partie": "code",
      "statement": "Que renvoie la dichotomie sur une **liste vide** ?",
      "options": [
        {
          "text": "`None`",
          "correct": false,
          "feedback": "Selon notre convention, on renvoie `-1` (pas\n`None`).\n"
        },
        {
          "text": "Une exception",
          "correct": false,
          "feedback": "Erreur : le code correct gère ce cas\nproprement.\n"
        },
        {
          "text": "`-1`",
          "correct": true,
          "feedback": "Bonne réponse : `g = 0`, `d = -1`, donc\n`g > d` immédiatement. La boucle ne s'exécute\npas et la fonction renvoie `-1` directement.\n"
        },
        {
          "text": "`0`",
          "correct": false,
          "feedback": "Erreur : aucune position 0 dans une liste\nvide.\n"
        }
      ],
      "explanation": "Cas limite à toujours tester. Bonne pratique :\n`assert dicho([], 5) == -1`."
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "comparaison-lineaire"
      ],
      "title": "Pourquoi dichotomique ?",
      "partie": "principe",
      "statement": "Quel est le principal **avantage** de la\ndichotomique sur la linéaire ?",
      "options": [
        {
          "text": "Elle est plus simple à programmer",
          "correct": false,
          "feedback": "Au contraire, elle est légèrement plus\ncomplexe (gestion des indices).\n"
        },
        {
          "text": "Elle utilise globalement beaucoup moins de mémoire vive",
          "correct": false,
          "feedback": "Pas un avantage notable : les deux utilisent\npeu de mémoire.\n"
        },
        {
          "text": "Elle ne nécessite pas que la liste soit triée au préalable",
          "correct": false,
          "feedback": "Erreur : c'est la **précondition**\nfondamentale.\n"
        },
        {
          "text": "Elle est bien plus rapide sur les grandes listes triées",
          "correct": true,
          "feedback": "Bonne réponse : passer de O(n) à O(log n)\nest un gain énorme. 1 milliard d'éléments :\n1 milliard de comparaisons en linéaire vs\nenviron 30 en dichotomique.\n"
        }
      ],
      "explanation": "Mais attention : si la liste n'est pas triée et\nqu'on la trie juste pour la dichotomie, le tri\ncoûte bien plus qu'un simple parcours (un tri par\ninsertion ou par sélection est quadratique). Pour\nune **seule** recherche, la linéaire est plus\nrapide."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "trace",
        "milieu"
      ],
      "title": "Trace simple",
      "partie": "code",
      "statement": "On cherche `cible = 17` dans\n`liste = [3, 7, 11, 17, 25, 31, 42]`. Quel est\nl'indice du milieu à la **première étape** ?",
      "options": [
        {
          "text": "4",
          "correct": false,
          "feedback": "Erreur : `(0 + 6) // 2 = 3`, pas 4.\n"
        },
        {
          "text": "6",
          "correct": false,
          "feedback": "Erreur : 6 est l'indice droit, pas le\nmilieu.\n"
        },
        {
          "text": "3",
          "correct": true,
          "feedback": "Bonne réponse : `g = 0`, `d = 6`, milieu =\n`(0 + 6) // 2 = 3`. La valeur en indice 3 est\n`17`, qui est la cible : la dichotomie\nréussit en **une seule** comparaison.\n"
        },
        {
          "text": "0",
          "correct": false,
          "feedback": "Erreur : 0 est l'indice gauche, pas le\nmilieu.\n"
        }
      ],
      "explanation": "Ici, la dichotomie est miraculeusement efficace\nparce que la cible se trouve juste au milieu.\nCas favorable : O(1).",
      "variants": [
        {
          "statement": "On cherche `cible = 17` dans\n`liste = [3, 7, 11, 17, 25, 31, 42]`. Quel est\nl'indice du milieu à la **première étape** ?",
          "options": [
            {
              "text": "4",
              "correct": false,
              "feedback": "Erreur : `(0 + 6) // 2 = 3`, pas 4.\n"
            },
            {
              "text": "6",
              "correct": false,
              "feedback": "Erreur : 6 est l'indice droit, pas le\nmilieu.\n"
            },
            {
              "text": "3",
              "correct": true,
              "feedback": "Bonne réponse : `g = 0`, `d = 6`, milieu =\n`(0 + 6) // 2 = 3`. La valeur en indice 3 est\n`17`, qui est la cible : la dichotomie\nréussit en **une seule** comparaison.\n"
            },
            {
              "text": "0",
              "correct": false,
              "feedback": "Erreur : 0 est l'indice gauche, pas le\nmilieu.\n"
            }
          ],
          "explanation": "Ici, la dichotomie est miraculeusement efficace\nparce que la cible se trouve juste au milieu.\nCas favorable : O(1)."
        },
        {
          "statement": "On cherche `cible = 21` dans\n`liste = [2, 5, 8, 13, 21, 34, 55, 89, 144]`. Quel est\nl'indice du milieu à la **première étape** ?",
          "options": [
            {
              "text": "5",
              "correct": false,
              "feedback": "Erreur : `(0 + 8) // 2 = 4`, pas 5.\n"
            },
            {
              "text": "8",
              "correct": false,
              "feedback": "Erreur : 8 est l'indice droit, pas le\nmilieu.\n"
            },
            {
              "text": "4",
              "correct": true,
              "feedback": "Bonne réponse : `g = 0`, `d = 8`, milieu =\n`(0 + 8) // 2 = 4`. La valeur en indice 4 est\n`21`, qui est la cible : la dichotomie\nréussit en **une seule** comparaison.\n"
            },
            {
              "text": "0",
              "correct": false,
              "feedback": "Erreur : 0 est l'indice gauche, pas le\nmilieu.\n"
            }
          ],
          "explanation": "Ici, la dichotomie est miraculeusement efficace\nparce que la cible se trouve juste au milieu.\nCas favorable : O(1)."
        },
        {
          "statement": "On cherche `cible = 16` dans\n`liste = [4, 9, 16, 25, 36]`. Quel est\nl'indice du milieu à la **première étape** ?",
          "options": [
            {
              "text": "3",
              "correct": false,
              "feedback": "Erreur : `(0 + 4) // 2 = 2`, pas 3.\n"
            },
            {
              "text": "4",
              "correct": false,
              "feedback": "Erreur : 4 est l'indice droit, pas le\nmilieu.\n"
            },
            {
              "text": "2",
              "correct": true,
              "feedback": "Bonne réponse : `g = 0`, `d = 4`, milieu =\n`(0 + 4) // 2 = 2`. La valeur en indice 2 est\n`16`, qui est la cible : la dichotomie\nréussit en **une seule** comparaison.\n"
            },
            {
              "text": "0",
              "correct": false,
              "feedback": "Erreur : 0 est l'indice gauche, pas le\nmilieu.\n"
            }
          ],
          "explanation": "Ici, la dichotomie est miraculeusement efficace\nparce que la cible se trouve juste au milieu.\nCas favorable : O(1)."
        }
      ]
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "invariant"
      ],
      "title": "Invariant de boucle",
      "partie": "preuve",
      "statement": "Quel **invariant de boucle** garantit la\ncorrection de la recherche dichotomique\nitérative (avec les indices `g` et `d`) ?",
      "options": [
        {
          "text": "La cible cherchée est toujours placée à l'indice du milieu",
          "correct": false,
          "feedback": "Erreur : sinon la dichotomie réussirait\nen une seule comparaison.\n"
        },
        {
          "text": "La liste reste constamment triée pendant toute la recherche",
          "correct": false,
          "feedback": "Erreur : ce n'est pas un invariant de la\nboucle, c'est une **précondition** sur\nl'entrée.\n"
        },
        {
          "text": "Si la cible est présente, elle est dans `liste[g..d]`",
          "correct": true,
          "feedback": "Bonne réponse : c'est l'invariant clé. À\nl'entrée de chaque itération, la cible\n(si elle existe) est forcément dans la\ntranche encore considérée. Si l'on\nn'élimine que la moitié sans la cible,\nl'invariant reste vrai à la sortie.\n"
        },
        {
          "text": "Les indices `g` et `d` restent égaux à chaque itération",
          "correct": false,
          "feedback": "Erreur : ils ne deviennent égaux qu'à la\ndernière itération (au plus tard).\n"
        }
      ],
      "explanation": "Invariant + condition d'arrêt → correction.\nÀ la sortie de la boucle, soit on a trouvé\nla cible, soit `g > d` (intervalle vide,\ndonc la cible n'est pas dans la liste)."
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "doublons"
      ],
      "title": "Doublons",
      "partie": "principe",
      "statement": "Si la cible apparaît **plusieurs fois** dans la\nliste triée, quelle position renvoie la\ndichotomie standard ?",
      "options": [
        {
          "text": "L'indice d'une occurrence quelconque, sans garantie",
          "correct": true,
          "feedback": "Bonne réponse : la dichotomie standard ne\nprécise pas laquelle. Pour avoir la\n**première** ou la **dernière** occurrence,\nil faut une variante (continuer la\ndichotomie même après avoir trouvé).\n"
        },
        {
          "text": "La toute dernière occurrence, située le plus à droite",
          "correct": false,
          "feedback": "Possible mais non garanti.\n"
        },
        {
          "text": "Les indices de toutes les occurrences présentes",
          "correct": false,
          "feedback": "Erreur : la dichotomie renvoie un seul\nindice.\n"
        },
        {
          "text": "Toujours la première occurrence, la plus à gauche",
          "correct": false,
          "feedback": "Possible mais non garanti : dépend du\nparcours.\n"
        }
      ],
      "explanation": "Pour obtenir spécifiquement la **première**\nou la **dernière** occurrence en cas de\ndoublons, on adapte légèrement le code de\nla dichotomie (en continuant à chercher à\ngauche ou à droite après une égalité, au\nlieu de s'arrêter)."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "trace",
        "iterations"
      ],
      "title": "Trace détaillée",
      "partie": "code",
      "statement": "On cherche `cible = 25` dans\n`liste = [3, 7, 11, 17, 25, 31, 42]`. Combien\nd'itérations de la boucle la dichotomie itérative\neffectue-t-elle avant de trouver la cible ?",
      "options": [
        {
          "text": "2",
          "correct": false,
          "feedback": "Erreur : il manque une itération. Après 2 calcul(s) du\nmilieu, l'intervalle s'est réduit, mais la valeur qui\ns'y trouve n'a pas encore été comparée à la cible.\n"
        },
        {
          "text": "7",
          "correct": false,
          "feedback": "Erreur : c'est le nombre d'éléments de la liste,\npas le nombre d'itérations.\n"
        },
        {
          "text": "4",
          "correct": false,
          "feedback": "Erreur : une itération de trop. Dès que la valeur du\nmilieu est la cible, la fonction renvoie son indice\nsans tour supplémentaire.\n"
        },
        {
          "text": "3",
          "correct": true,
          "feedback": "Bonne réponse : itération 1, milieu = indice 3 (valeur 17 < 25, on va à droite, g = 4) ; itération 2, milieu = (4 + 6) // 2 = 5 (valeur 31 > 25, on va à gauche, d = 4) ; itération 3, milieu = 4 (valeur 25 = cible). Soit trois\nitérations de la boucle.\n"
        }
      ],
      "explanation": "Pour 7 éléments, on attend au plus 3 itérations\ndans le pire cas. C'est cohérent.",
      "variants": [
        {
          "statement": "On cherche `cible = 25` dans\n`liste = [3, 7, 11, 17, 25, 31, 42]`. Combien\nd'itérations de la boucle la dichotomie itérative\neffectue-t-elle avant de trouver la cible ?",
          "options": [
            {
              "text": "2",
              "correct": false,
              "feedback": "Erreur : il manque une itération. Après 2 calcul(s) du\nmilieu, l'intervalle s'est réduit, mais la valeur qui\ns'y trouve n'a pas encore été comparée à la cible.\n"
            },
            {
              "text": "7",
              "correct": false,
              "feedback": "Erreur : c'est le nombre d'éléments de la liste,\npas le nombre d'itérations.\n"
            },
            {
              "text": "4",
              "correct": false,
              "feedback": "Erreur : une itération de trop. Dès que la valeur du\nmilieu est la cible, la fonction renvoie son indice\nsans tour supplémentaire.\n"
            },
            {
              "text": "3",
              "correct": true,
              "feedback": "Bonne réponse : itération 1, milieu = indice 3 (valeur 17 < 25, on va à droite, g = 4) ; itération 2, milieu = (4 + 6) // 2 = 5 (valeur 31 > 25, on va à gauche, d = 4) ; itération 3, milieu = 4 (valeur 25 = cible). Soit trois\nitérations de la boucle.\n"
            }
          ],
          "explanation": "Pour 7 éléments, on attend au plus 3 itérations\ndans le pire cas. C'est cohérent."
        },
        {
          "statement": "On cherche `cible = 5` dans\n`liste = [2, 5, 8, 13, 21, 34, 55, 89, 144]`. Combien\nd'itérations de la boucle la dichotomie itérative\neffectue-t-elle avant de trouver la cible ?",
          "options": [
            {
              "text": "1",
              "correct": false,
              "feedback": "Erreur : il manque une itération. Après 1 calcul(s) du\nmilieu, l'intervalle s'est réduit, mais la valeur qui\ns'y trouve n'a pas encore été comparée à la cible.\n"
            },
            {
              "text": "9",
              "correct": false,
              "feedback": "Erreur : c'est le nombre d'éléments de la liste,\npas le nombre d'itérations.\n"
            },
            {
              "text": "3",
              "correct": false,
              "feedback": "Erreur : une itération de trop. Dès que la valeur du\nmilieu est la cible, la fonction renvoie son indice\nsans tour supplémentaire.\n"
            },
            {
              "text": "2",
              "correct": true,
              "feedback": "Bonne réponse : itération 1, milieu = indice 4 (valeur 21 > 5, on va à gauche, d = 3) ; itération 2, milieu = (0 + 3) // 2 = 1 (valeur 5 = cible). Soit deux\nitérations de la boucle.\n"
            }
          ],
          "explanation": "Pour 9 éléments, on attend au plus 4 itérations\ndans le pire cas. C'est cohérent."
        },
        {
          "statement": "On cherche `cible = 144` dans\n`liste = [4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144]`. Combien\nd'itérations de la boucle la dichotomie itérative\neffectue-t-elle avant de trouver la cible ?",
          "options": [
            {
              "text": "3",
              "correct": false,
              "feedback": "Erreur : il manque une itération. Après 3 calcul(s) du\nmilieu, l'intervalle s'est réduit, mais la valeur qui\ns'y trouve n'a pas encore été comparée à la cible.\n"
            },
            {
              "text": "11",
              "correct": false,
              "feedback": "Erreur : c'est le nombre d'éléments de la liste,\npas le nombre d'itérations.\n"
            },
            {
              "text": "5",
              "correct": false,
              "feedback": "Erreur : une itération de trop. Dès que la valeur du\nmilieu est la cible, la fonction renvoie son indice\nsans tour supplémentaire.\n"
            },
            {
              "text": "4",
              "correct": true,
              "feedback": "Bonne réponse : itération 1, milieu = indice 5 (valeur 49 < 144, on va à droite, g = 6) ; itération 2, milieu = (6 + 10) // 2 = 8 (valeur 100 < 144, g = 9) ; itération 3, milieu = (9 + 10) // 2 = 9 (valeur 121 < 144, g = 10) ; itération 4, milieu = 10 (valeur 144 = cible). Soit quatre\nitérations de la boucle.\n"
            }
          ],
          "explanation": "Pour 11 éléments, on attend au plus 4 itérations\ndans le pire cas. C'est cohérent."
        }
      ]
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "absent"
      ],
      "title": "Élément absent",
      "partie": "code",
      "statement": "On cherche `cible = 20` dans\n`liste = [3, 7, 11, 17, 25, 31, 42]`. Que\nrenvoie la dichotomie ?",
      "options": [
        {
          "text": "Une exception",
          "correct": false,
          "feedback": "Erreur : code correct gère le cas absent.\n"
        },
        {
          "text": "L'indice 4 (position où 20 devrait être inséré)",
          "correct": false,
          "feedback": "Erreur : ce serait le rôle d'une variante\n(« position d'insertion ») qui demande une\nadaptation du code. La dichotomie standard\nrenvoie `-1` quand l'élément est absent.\n"
        },
        {
          "text": "La valeur 17 (la plus proche)",
          "correct": false,
          "feedback": "Erreur : la dichotomie ne renvoie pas la\n« valeur la plus proche » par défaut.\n"
        },
        {
          "text": "-1 (élément absent)",
          "correct": true,
          "feedback": "Bonne réponse : 20 n'est pas dans la liste.\nLa boucle se termine avec `g > d` et\nrenvoie `-1`.\n"
        }
      ],
      "explanation": "Pour trouver « la valeur la plus proche » ou\n« la position d'insertion », il faut adapter\nlégèrement le code de la dichotomie : au\nmoment où l'on sortirait avec `-1`, on\nrenvoie plutôt l'indice où la cible aurait\ndû s'insérer."
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "bug-bornes"
      ],
      "title": "Bug de bornes",
      "partie": "code",
      "statement": "Un élève écrit `while g < d:` au lieu de\n`while g <= d:`. Quel est l'effet ?",
      "options": [
        {
          "text": "La fonction devient plus rapide sans changer le résultat",
          "correct": false,
          "feedback": "Erreur : le gain d'une itération se paie\nd'un résultat incorrect sur certaines\nentrées.\n"
        },
        {
          "text": "La fonction finit toujours par renvoyer la valeur -1",
          "correct": false,
          "feedback": "Erreur : elle peut renvoyer un indice si la\ncible est trouvée avant le rétrécissement\nfinal.\n"
        },
        {
          "text": "La fonction sort sans tester le cas `g == d`",
          "correct": true,
          "feedback": "Bonne réponse : si l'intervalle se réduit à\nun seul élément (`g == d`), la condition\n`g < d` est fausse et on sort sans tester\nce dernier élément. Bug subtil mais courant.\n"
        },
        {
          "text": "La boucle devient infinie sur certaines entrées",
          "correct": false,
          "feedback": "Pas de boucle infinie ici (l'intervalle\nrétrécit toujours), mais un bug logique.\n"
        }
      ],
      "explanation": "Toujours tester les **cas limites** : liste à\nun seul élément, cible aux extrémités, cible\nnon présente. Ces tests révèlent les bugs de\nbornes."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "valeur-de-retour",
        "indice"
      ],
      "title": "Indice retourné en cas de succès",
      "partie": "code",
      "statement": "Une dichotomie standard sur la liste\n`[3, 7, 11, 17, 25, 31, 42]` cherche `cible = 17`.\nQue renvoie-t-elle ?",
      "options": [
        {
          "text": "L'indice `3`",
          "correct": true,
          "feedback": "Bonne réponse : la valeur `17` se trouve\nen position `3` dans la liste (positions\n`0` à `6`). Une dichotomie standard\nrenvoie cet indice quand la cible est\nprésente.\n"
        },
        {
          "text": "La longueur de la liste, c'est-à-dire `7`",
          "correct": false,
          "feedback": "Erreur : la longueur de la liste n'a aucun\nrapport avec la position de la cible. On\nrenvoie `-1` (par convention) seulement\nsi la cible est absente.\n"
        },
        {
          "text": "La position `0` (la première de la liste)",
          "correct": false,
          "feedback": "Erreur : la position `0` correspond à la\nvaleur `3`, pas à `17`. La dichotomie\nrenvoie l'indice exact de la cible\ntrouvée.\n"
        },
        {
          "text": "La valeur `17`",
          "correct": false,
          "feedback": "Erreur : la fonction renvoie l'**indice**\nde la cible, pas sa valeur (qu'on connaît\ndéjà puisqu'on la passe en paramètre).\n"
        }
      ],
      "explanation": "Convention courante : la fonction renvoie\nl'**indice** de la cible quand elle est\ntrouvée, et `-1` (ou parfois `None`) quand\nelle est absente.",
      "variants": [
        {
          "statement": "Une dichotomie standard sur la liste\n`[3, 7, 11, 17, 25, 31, 42]` cherche `cible = 17`.\nQue renvoie-t-elle ?",
          "options": [
            {
              "text": "L'indice `3`",
              "correct": true,
              "feedback": "Bonne réponse : la valeur `17` se trouve\nen position `3` dans la liste (positions\n`0` à `6`). Une dichotomie standard\nrenvoie cet indice quand la cible est\nprésente.\n"
            },
            {
              "text": "La longueur de la liste, c'est-à-dire `7`",
              "correct": false,
              "feedback": "Erreur : la longueur de la liste n'a aucun\nrapport avec la position de la cible. On\nrenvoie `-1` (par convention) seulement\nsi la cible est absente.\n"
            },
            {
              "text": "La position `0` (la première de la liste)",
              "correct": false,
              "feedback": "Erreur : la position `0` correspond à la\nvaleur `3`, pas à `17`. La dichotomie\nrenvoie l'indice exact de la cible\ntrouvée.\n"
            },
            {
              "text": "La valeur `17`",
              "correct": false,
              "feedback": "Erreur : la fonction renvoie l'**indice**\nde la cible, pas sa valeur (qu'on connaît\ndéjà puisqu'on la passe en paramètre).\n"
            }
          ],
          "explanation": "Convention courante : la fonction renvoie\nl'**indice** de la cible quand elle est\ntrouvée, et `-1` (ou parfois `None`) quand\nelle est absente."
        },
        {
          "statement": "Une dichotomie standard sur la liste\n`[2, 5, 8, 13, 21, 34, 55, 89, 144]` cherche `cible = 55`.\nQue renvoie-t-elle ?",
          "options": [
            {
              "text": "L'indice `6`",
              "correct": true,
              "feedback": "Bonne réponse : la valeur `55` se trouve\nen position `6` dans la liste (positions\n`0` à `8`). Une dichotomie standard\nrenvoie cet indice quand la cible est\nprésente.\n"
            },
            {
              "text": "La longueur de la liste, c'est-à-dire `9`",
              "correct": false,
              "feedback": "Erreur : la longueur de la liste n'a aucun\nrapport avec la position de la cible. On\nrenvoie `-1` (par convention) seulement\nsi la cible est absente.\n"
            },
            {
              "text": "La position `0` (la première de la liste)",
              "correct": false,
              "feedback": "Erreur : la position `0` correspond à la\nvaleur `2`, pas à `55`. La dichotomie\nrenvoie l'indice exact de la cible\ntrouvée.\n"
            },
            {
              "text": "La valeur `55`",
              "correct": false,
              "feedback": "Erreur : la fonction renvoie l'**indice**\nde la cible, pas sa valeur (qu'on connaît\ndéjà puisqu'on la passe en paramètre).\n"
            }
          ],
          "explanation": "Convention courante : la fonction renvoie\nl'**indice** de la cible quand elle est\ntrouvée, et `-1` (ou parfois `None`) quand\nelle est absente."
        },
        {
          "statement": "Une dichotomie standard sur la liste\n`[4, 9, 16, 25, 36]` cherche `cible = 36`.\nQue renvoie-t-elle ?",
          "options": [
            {
              "text": "L'indice `4`",
              "correct": true,
              "feedback": "Bonne réponse : la valeur `36` se trouve\nen position `4` dans la liste (positions\n`0` à `4`). Une dichotomie standard\nrenvoie cet indice quand la cible est\nprésente.\n"
            },
            {
              "text": "La longueur de la liste, c'est-à-dire `5`",
              "correct": false,
              "feedback": "Erreur : la longueur de la liste n'a aucun\nrapport avec la position de la cible. On\nrenvoie `-1` (par convention) seulement\nsi la cible est absente.\n"
            },
            {
              "text": "La position `0` (la première de la liste)",
              "correct": false,
              "feedback": "Erreur : la position `0` correspond à la\nvaleur `4`, pas à `36`. La dichotomie\nrenvoie l'indice exact de la cible\ntrouvée.\n"
            },
            {
              "text": "La valeur `36`",
              "correct": false,
              "feedback": "Erreur : la fonction renvoie l'**indice**\nde la cible, pas sa valeur (qu'on connaît\ndéjà puisqu'on la passe en paramètre).\n"
            }
          ],
          "explanation": "Convention courante : la fonction renvoie\nl'**indice** de la cible quand elle est\ntrouvée, et `-1` (ou parfois `None`) quand\nelle est absente."
        }
      ]
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "longueur-1"
      ],
      "title": "Liste à un élément",
      "partie": "code",
      "statement": "Sur `liste = [42]` et `cible = 42`, combien\nd'itérations effectue la dichotomie standard ?",
      "options": [
        {
          "text": "1 (une seule itération suffit)",
          "correct": true,
          "feedback": "Bonne réponse : `g = 0`, `d = 0`, donc\n`g <= d`. Une itération calcule `m = 0`,\ncompare `liste[0]` et trouve la cible.\n"
        },
        {
          "text": "0 (la cible est trouvée sans entrer dans la boucle)",
          "correct": false,
          "feedback": "Erreur : on entre bien dans la boucle pour\ntester la valeur du milieu.\n"
        },
        {
          "text": "2",
          "correct": false,
          "feedback": "Erreur : une itération suffit.\n"
        },
        {
          "text": "Boucle infinie",
          "correct": false,
          "feedback": "Erreur : code correct se termine.\n"
        }
      ],
      "explanation": "Cas limite à tester. Si la cible n'était pas 42\nmais autre chose, il y aurait aussi exactement\nune itération : le milieu est comparé, puis\nl'intervalle devient vide (`g > d`) et la boucle\ns'arrête en renvoyant -1."
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "tri-prealable"
      ],
      "title": "Tri préalable utile ?",
      "partie": "principe",
      "statement": "On a une liste **non triée** de 1000 éléments\nsur laquelle on doit faire **une seule**\nrecherche. Faut-il la trier avant pour faire de\nla dichotomie ?",
      "options": [
        {
          "text": "Oui, parce que la dichotomie est toujours préférable",
          "correct": false,
          "feedback": "Erreur : aucune méthode n'est « toujours »\npréférable, tout dépend du contexte. Le\ncoût du tri préalable reste à amortir.\n"
        },
        {
          "text": "Oui, ce serait nettement plus rapide ainsi",
          "correct": false,
          "feedback": "Erreur : un tri par insertion ou par sélection\ncoûte de l'ordre de 500 000 comparaisons pour\n1 000 éléments, plus une dizaine pour la\ndichotomie. La recherche linéaire ne fait au\nplus que 1 000 comparaisons. Pas rentable.\n"
        },
        {
          "text": "La dichotomie fonctionne sur une liste non triée",
          "correct": false,
          "feedback": "Erreur : la précondition est essentielle.\n"
        },
        {
          "text": "Non, la linéaire suffit pour une recherche unique",
          "correct": true,
          "feedback": "Bonne réponse : trier juste pour une\nrecherche est contre-productif. La\ndichotomie devient rentable seulement à\npartir de **plusieurs recherches** sur la\nmême liste.\n"
        }
      ],
      "explanation": "Règle : le tri préalable ne devient rentable que\nsi l'on fait de nombreuses recherches sur la même\nliste, assez pour que le coût du tri, payé une\nseule fois, soit compensé par les comparaisons\néconomisées à chaque recherche."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "pseudo-code"
      ],
      "title": "Recherche par approximation",
      "partie": "code",
      "statement": "Pour trouver la **plus petite valeur ≥ cible**\ndans une liste triée, peut-on utiliser une\ndichotomie ?",
      "options": [
        {
          "text": "Oui, mais cela double la complexité",
          "correct": false,
          "feedback": "Erreur : la complexité reste O(log n).\n"
        },
        {
          "text": "Non, la dichotomie ne fonctionne que pour des comparaisons d'égalité",
          "correct": false,
          "feedback": "Erreur : la dichotomie repose sur des\n**comparaisons d'ordre**, pas d'égalité.\n"
        },
        {
          "text": "Non, cela demande une recherche linéaire",
          "correct": false,
          "feedback": "Erreur : la dichotomie peut être adaptée.\n"
        },
        {
          "text": "Oui, en adaptant légèrement le code de la dichotomie",
          "correct": true,
          "feedback": "Bonne réponse : quand `liste[m] >= cible`,\non mémorise `liste[m]` comme meilleur\ncandidat puis on continue à chercher dans\nla moitié gauche (au lieu de s'arrêter) ;\nsinon on cherche à droite. Le dernier\ncandidat mémorisé est la plus petite valeur\n≥ cible. La complexité reste `O(log n)`.\n"
        }
      ],
      "explanation": "C'est l'un des grands intérêts de la\ndichotomie : elle s'adapte à toutes sortes de\nrequêtes (≤, <, ≥, >) sur des données triées."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "terminaison"
      ],
      "title": "Terminaison",
      "partie": "preuve",
      "statement": "Pourquoi la dichotomie itérative **se termine**\ntoujours (pas de boucle infinie) ?",
      "options": [
        {
          "text": "Parce que l'intervalle rétrécit strictement à chaque tour",
          "correct": true,
          "feedback": "Bonne réponse : `g = m + 1` ou `d = m - 1`\nréduit la taille de l'intervalle d'au moins\nun. L'intervalle ne peut donc rétrécir\nindéfiniment, il finit par devenir vide\n(`g > d`).\n"
        },
        {
          "text": "Parce que Python limite les boucles à 1000 itérations",
          "correct": false,
          "feedback": "Erreur : Python n'impose aucune telle limite.\n"
        },
        {
          "text": "Parce que la cible finit forcément par être trouvée",
          "correct": false,
          "feedback": "Erreur : la cible peut être absente, et la\ndichotomie se termine quand même.\n"
        },
        {
          "text": "Parce que la liste est entièrement triée au départ de la recherche",
          "correct": false,
          "feedback": "Erreur : le tri permet la **correction**, pas\nla terminaison.\n"
        }
      ],
      "explanation": "Argument de terminaison : on définit un\n« variant » qui décroît strictement (ici,\n`d - g`). Si le variant est borné inférieurement\n(ici, par 0 ou -1), la boucle se termine."
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "bug-borne-infinie"
      ],
      "title": "Boucle infinie",
      "partie": "code",
      "statement": "Un élève écrit `g = m` (au lieu de `g = m + 1`)\naprès une comparaison `liste[m] < cible`. Que se\npasse-t-il ?",
      "options": [
        {
          "text": "La fonction devient un peu plus rapide, le résultat reste correct",
          "correct": false,
          "feedback": "Erreur : non seulement elle n'est pas plus\nrapide, mais elle peut boucler indéfiniment.\n"
        },
        {
          "text": "La fonction lève une exception `IndexError` au bout de quelques tours",
          "correct": false,
          "feedback": "Erreur : aucune exception, les indices restent\ndans les bornes ; la boucle tourne simplement\nsans fin.\n"
        },
        {
          "text": "La fonction renvoie toujours -1, même si la cible est présente",
          "correct": false,
          "feedback": "Pas systématiquement : selon l'entrée, soit\nelle trouve la cible, soit elle boucle.\n"
        },
        {
          "text": "Une boucle infinie est possible quand il ne reste que deux éléments",
          "correct": true,
          "feedback": "Bonne réponse : quand `d == g + 1`, on a\n`m = (g + d) // 2 = g`. Par exemple avec\n`g = 0`, `d = 1` : `m = 0`, et `g = m` laisse\n`g` à `0` ; la boucle ne progresse plus. D'où\nl'importance d'écrire `g = m + 1`.\n"
        }
      ],
      "explanation": "Bug très classique. Test à faire :\n`dicho([1, 2], 2)` qui doit renvoyer 1. Avec le\nbug, boucle infinie."
    },
    {
      "id": "q22",
      "difficulty": 3,
      "skills": [
        "trace",
        "indices"
      ],
      "title": "Trace pas à pas",
      "partie": "code",
      "statement": "On cherche `cible = 2` dans\n`liste = [1, 2, 3, 4, 5, 6, 7, 8]` (n=8). Quels\nsont les indices `m` successivement testés par\nla dichotomie itérative ?",
      "options": [
        {
          "text": "3, 1, 2",
          "correct": false,
          "feedback": "Erreur : la fonction renvoie dès qu'elle\ntrouve la cible (à `m=1`). Elle ne teste pas\nensuite l'indice 2.\n"
        },
        {
          "text": "4 puis 2",
          "correct": false,
          "feedback": "Erreur : `(0+7)//2 = 3`, pas 4. Le calcul\ndu milieu utilise la division entière.\n"
        },
        {
          "text": "3 puis 1",
          "correct": true,
          "feedback": "Bonne réponse : étape 1, `g=0,d=7,m=3` (`liste[3]=4 > 2`, on va à gauche : `d=2`) ; étape 2, `g=0,d=2,m=1` (`liste[1]=2 = cible`, retour immédiat de l'indice 1). Deux indices testés.\n"
        },
        {
          "text": "0, 1, 2 (parcours du début)",
          "correct": false,
          "feedback": "Erreur : ce serait une recherche linéaire.\nLa dichotomie commence par le milieu.\n"
        }
      ],
      "explanation": "Méthode : noter `g`, `d`, `m` à chaque étape,\nmettre à jour les bornes selon la comparaison,\ns'arrêter dès qu'on trouve la cible.",
      "variants": [
        {
          "statement": "On cherche `cible = 2` dans\n`liste = [1, 2, 3, 4, 5, 6, 7, 8]` (n=8). Quels\nsont les indices `m` successivement testés par\nla dichotomie itérative ?",
          "options": [
            {
              "text": "3, 1, 2",
              "correct": false,
              "feedback": "Erreur : la fonction renvoie dès qu'elle\ntrouve la cible (à `m=1`). Elle ne teste pas\nensuite l'indice 2.\n"
            },
            {
              "text": "4 puis 2",
              "correct": false,
              "feedback": "Erreur : `(0+7)//2 = 3`, pas 4. Le calcul\ndu milieu utilise la division entière.\n"
            },
            {
              "text": "3 puis 1",
              "correct": true,
              "feedback": "Bonne réponse : étape 1, `g=0,d=7,m=3` (`liste[3]=4 > 2`, on va à gauche : `d=2`) ; étape 2, `g=0,d=2,m=1` (`liste[1]=2 = cible`, retour immédiat de l'indice 1). Deux indices testés.\n"
            },
            {
              "text": "0, 1, 2 (parcours du début)",
              "correct": false,
              "feedback": "Erreur : ce serait une recherche linéaire.\nLa dichotomie commence par le milieu.\n"
            }
          ],
          "explanation": "Méthode : noter `g`, `d`, `m` à chaque étape,\nmettre à jour les bornes selon la comparaison,\ns'arrêter dès qu'on trouve la cible."
        },
        {
          "statement": "On cherche `cible = 7` dans\n`liste = [1, 2, 3, 4, 5, 6, 7, 8]` (n=8). Quels\nsont les indices `m` successivement testés par\nla dichotomie itérative ?",
          "options": [
            {
              "text": "3, 5, 6, 7",
              "correct": false,
              "feedback": "Erreur : la fonction renvoie dès qu'elle\ntrouve la cible (à `m=6`). Elle ne teste pas\nensuite l'indice 7.\n"
            },
            {
              "text": "4 puis 6",
              "correct": false,
              "feedback": "Erreur : `(0+7)//2 = 3`, pas 4. Le calcul\ndu milieu utilise la division entière.\n"
            },
            {
              "text": "3, 5 puis 6",
              "correct": true,
              "feedback": "Bonne réponse : étape 1, `g=0,d=7,m=3` (`liste[3]=4 < 7`, on va à droite : `g=4`) ; étape 2, `g=4,d=7,m=5` (`liste[5]=6 < 7`, `g=6`) ; étape 3, `g=6,d=7,m=6` (`liste[6]=7 = cible`, retour immédiat de l'indice 6). Trois indices testés.\n"
            },
            {
              "text": "0, 1, 2 (parcours du début)",
              "correct": false,
              "feedback": "Erreur : ce serait une recherche linéaire.\nLa dichotomie commence par le milieu.\n"
            }
          ],
          "explanation": "Méthode : noter `g`, `d`, `m` à chaque étape,\nmettre à jour les bornes selon la comparaison,\ns'arrêter dès qu'on trouve la cible."
        },
        {
          "statement": "On cherche `cible = 30` dans\n`liste = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]` (n=10). Quels\nsont les indices `m` successivement testés par\nla dichotomie itérative ?",
          "options": [
            {
              "text": "4, 1, 2, 3",
              "correct": false,
              "feedback": "Erreur : la fonction renvoie dès qu'elle\ntrouve la cible (à `m=2`). Elle ne teste pas\nensuite l'indice 3.\n"
            },
            {
              "text": "5 puis 2",
              "correct": false,
              "feedback": "Erreur : `(0+9)//2 = 4`, pas 5. Le calcul\ndu milieu utilise la division entière.\n"
            },
            {
              "text": "4, 1 puis 2",
              "correct": true,
              "feedback": "Bonne réponse : étape 1, `g=0,d=9,m=4` (`liste[4]=50 > 30`, on va à gauche : `d=3`) ; étape 2, `g=0,d=3,m=1` (`liste[1]=20 < 30`, `g=2`) ; étape 3, `g=2,d=3,m=2` (`liste[2]=30 = cible`, retour immédiat de l'indice 2). Trois indices testés.\n"
            },
            {
              "text": "0, 1, 2 (parcours du début)",
              "correct": false,
              "feedback": "Erreur : ce serait une recherche linéaire.\nLa dichotomie commence par le milieu.\n"
            }
          ],
          "explanation": "Méthode : noter `g`, `d`, `m` à chaque étape,\nmettre à jour les bornes selon la comparaison,\ns'arrêter dès qu'on trouve la cible."
        }
      ]
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "variant-loop"
      ],
      "title": "Variant de boucle",
      "partie": "preuve",
      "statement": "Quel est le **variant de boucle** (quantité qui\ndécroît strictement) de la dichotomie ?",
      "options": [
        {
          "text": "La valeur `m = (g + d) // 2` de l'indice du milieu de la liste",
          "correct": false,
          "feedback": "Erreur : `m` peut augmenter ou diminuer selon\nla comparaison ; il ne décroît pas strictement.\n"
        },
        {
          "text": "La valeur de la cible recherchée dans la liste",
          "correct": false,
          "feedback": "Erreur : la cible ne change pas pendant la\nrecherche, ce n'est pas une quantité\ndécroissante.\n"
        },
        {
          "text": "L'indice gauche `g` de l'intervalle de recherche",
          "correct": false,
          "feedback": "Erreur : `g` peut rester constant si on met\nà jour seulement `d` ; et il **croît** quand\nil change.\n"
        },
        {
          "text": "La taille de l'intervalle de recherche, `d - g + 1`",
          "correct": true,
          "feedback": "Bonne réponse : c'est précisément cette\nquantité qui décroît strictement à chaque\nitération (et même elle est divisée par 2\nen moyenne). Quand elle atteint 0, on sort\nde la boucle.\n"
        }
      ],
      "explanation": "Notion de variant : indispensable pour prouver\nla terminaison d'une boucle. La taille de\nl'intervalle est l'exemple canonique pour la\ndichotomie."
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "optimisation"
      ],
      "title": "Recherche en mémoire externe",
      "partie": "code",
      "statement": "On dispose d'un fichier trié de **10 milliards**\nd'enregistrements (sur disque). Quel impact\npratique a la dichotomie ?",
      "options": [
        {
          "text": "Cela ne fonctionne pas hors mémoire vive",
          "correct": false,
          "feedback": "Erreur : la dichotomie ne nécessite pas de\ntout charger en mémoire. C'est même son\nintérêt.\n"
        },
        {
          "text": "10 milliards d'accès disque restent nécessaires pour conclure",
          "correct": false,
          "feedback": "Erreur : ce serait la linéaire, donc plusieurs\njours de calcul.\n"
        },
        {
          "text": "Aucun, la lecture séquentielle est aussi rapide",
          "correct": false,
          "feedback": "Erreur : avec 10 milliards d'enregistrements,\nla lecture séquentielle est très lente.\n"
        },
        {
          "text": "environ 34 accès disque suffisent (log₂(10¹⁰) ≈ 33,2)",
          "correct": true,
          "feedback": "Bonne réponse : la dichotomie permet de\nconsulter une base gigantesque en très peu\nd'accès. C'est ce qui permet aux moteurs de\nrecherche, BDD et autres systèmes\nd'indexer efficacement de gros volumes.\n"
        }
      ],
      "explanation": "Idée centrale : à chaque étape, on **divise**\nl'intervalle de recherche par deux. C'est ce\nqui donne le coût logarithmique."
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "precondition-violee"
      ],
      "title": "Précondition violée",
      "partie": "principe",
      "statement": "Un élève applique à la liste\n`[42, 31, 25, 17, 11, 7, 3]`, triée par ordre\n**décroissant**, la dichotomie écrite pour une\nliste croissante :\n```python\ndef dicho(liste, cible):\n    g, d = 0, len(liste) - 1\n    while g <= d:\n        m = (g + d) // 2\n        if liste[m] == cible:\n            return m\n        elif liste[m] < cible:\n            g = m + 1\n        else:\n            d = m - 1\n    return -1\n```\nQue renvoie `dicho(liste, 31)` ?",
      "options": [
        {
          "text": "`-1`, alors que 31 est bien dans la liste",
          "correct": true,
          "feedback": "Bonne réponse : au premier tour, `m = 3` et\n`liste[3] = 17 < 31`, donc l'algorithme\npoursuit à droite (`g = 4`) alors que 31 est\nà gauche. Il teste ensuite 7 puis 3, toujours\nplus petits que 31, et sort avec `g > d` :\n-1, sans la moindre erreur d'exécution.\n"
        },
        {
          "text": "`1`, l'indice de 31 dans la liste",
          "correct": false,
          "feedback": "Erreur : l'algorithme ne teste jamais\nl'indice 1. Dès le premier tour, la\ncomparaison `17 < 31` l'envoie dans la\nmoitié droite, où 31 ne se trouve pas, et il\nn'en revient jamais.\n"
        },
        {
          "text": "`3`, l'indice du milieu testé en premier",
          "correct": false,
          "feedback": "Erreur : `liste[3]` vaut 17 et non 31, donc\nl'égalité échoue et la recherche continue.\nL'indice du milieu n'est renvoyé que si\nl'élément qui s'y trouve est la cible.\n"
        },
        {
          "text": "Une erreur `IndexError`, car les indices sortent de la liste",
          "correct": false,
          "feedback": "Erreur : `g` et `d` restent entre 0 et 6 ;\nla boucle `while g <= d` s'arrête proprement\ndès que `g` dépasse `d`. Le résultat est\nfaux, mais aucune exception n'est levée.\n"
        }
      ],
      "explanation": "Une précondition violée ne provoque pas\nforcément de plantage : ici la dichotomie\nrenvoie une réponse fausse sans avertir. D'où\nla nécessité de trier la liste (ou de vérifier\nqu'elle l'est) avant d'appliquer l'algorithme."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "dichotomie-table",
        "code-iteratif"
      ],
      "title": "Chercher un nom dans une table triée",
      "partie": "principe",
      "statement": "Une table est rangée par ordre alphabétique des noms :\n\n```python\ntable = [(\"Ali\", 12), (\"Emma\", 15), (\"Hugo\", 9), (\"Lina\", 17), (\"Zoé\", 11)]\n\ndef note_de(table, nom):\n    g = 0\n    d = len(table) - 1\n    while g <= d:\n        m = (g + d) // 2\n        if ...:\n            return table[m][1]\n        elif table[m][0] < nom:\n            g = m + 1\n        else:\n            d = m - 1\n    return -1\n```\n\nQuelle condition faut-il écrire à la place de `...` pour que\n`note_de(table, \"Lina\")` renvoie `17` ?",
      "options": [
        {
          "text": "`table[m] == nom`",
          "correct": false,
          "feedback": "Erreur : `table[m]` est un couple comme `(\"Hugo\", 9)`, jamais égal\nà une chaîne. La condition est toujours fausse et la fonction\nrenvoie `-1` même pour un nom présent.\n"
        },
        {
          "text": "`table[0][m] == nom`",
          "correct": false,
          "feedback": "Erreur : `table[0]` est le premier couple et `table[0][m]` en\nprend l'élément d'indice `m`. Au premier tour, `m` vaut 2 et\nl'accès sort du couple : `IndexError`.\n"
        },
        {
          "text": "`table[m][0] == nom`",
          "correct": true,
          "feedback": "Bonne réponse : `table[m]` est un couple `(nom, note)` et\n`table[m][0]` son nom. On le compare à la cible, comme la ligne\n`elif` le fait déjà pour choisir la moitié à garder.\n"
        },
        {
          "text": "`table[m][1] == nom`",
          "correct": false,
          "feedback": "Erreur : `table[m][1]` est la note, un entier, qui n'est jamais\négal au nom cherché. La condition est toujours fausse et la\nfonction renvoie `-1`.\n"
        }
      ],
      "explanation": "Pour chercher par dichotomie dans une table triée suivant une\ncolonne, on compare la cible à la valeur de cette colonne,\n`table[m][0]`, aussi bien pour le test d'égalité que pour choisir la\nmoitié à conserver."
    },
    {
      "id": "q27",
      "difficulty": 3,
      "skills": [
        "doublement-taille"
      ],
      "title": "Doublement de la taille",
      "partie": "preuve",
      "statement": "Une recherche dichotomique sur une liste de\ntaille `n` effectue au pire `k` comparaisons.\nCombien de comparaisons effectue-t-elle au pire\nsur une liste de taille `2n` ?",
      "options": [
        {
          "text": "`2k`",
          "correct": false,
          "feedback": "Erreur : la dichotomie n'est pas linéaire en\nn. Doubler la taille n'ajoute pas k\ncomparaisons mais une seule.\n"
        },
        {
          "text": "`k`",
          "correct": false,
          "feedback": "Erreur : ce serait dire que la complexité ne\ndépend pas de la taille, ce qui est faux. La\ndichotomie ajoute bien une comparaison\nquand on double n.\n"
        },
        {
          "text": "`k + 1`",
          "correct": true,
          "feedback": "Bonne réponse : la complexité est en\n`log₂(n)`. Or `log₂(2n) = log₂(n) + 1`. Donc\ndoubler la taille n'ajoute qu'**une seule**\ncomparaison au pire. C'est la signature\nfondamentale d'un algorithme logarithmique.\n"
        },
        {
          "text": "`k²`",
          "correct": false,
          "feedback": "Erreur : il n'y a pas de carré dans\n`log(2n)`. Cela donnerait une complexité\nbeaucoup trop élevée.\n"
        }
      ],
      "explanation": "Lecture pratique : on peut multiplier par mille\nla taille d'une liste sans payer plus de dix\ncomparaisons supplémentaires (`log₂(1000) ≈ 10`).\nC'est ce qui rend la dichotomie incontournable\npour les très gros volumes."
    },
    {
      "id": "q28",
      "difficulty": 3,
      "skills": [
        "dichotomie-frontiere"
      ],
      "title": "Dichotomie de frontière",
      "partie": "principe",
      "statement": "On dispose d'une liste triée de booléens (par\nexemple `[False, False, False, True, True, True]`)\net on cherche l'indice de la **première**\noccurrence de `True`. Quel principe permet de\nrésoudre ce problème en `O(log n)` ?",
      "options": [
        {
          "text": "Compter les `True` puis renvoyer leur position",
          "correct": false,
          "feedback": "Erreur : compter les `True` (ou les `False`)\ndemande de parcourir toute la liste, donc\n`O(n)`. On perd l'avantage logarithmique.\n"
        },
        {
          "text": "Une recherche linéaire suffit, on ne peut pas faire mieux",
          "correct": false,
          "feedback": "Erreur : on peut faire mieux. La liste est\ntriée (tous les `False` avant tous les\n`True`), ce qui permet la dichotomie.\n"
        },
        {
          "text": "Trier une nouvelle fois l'intégralité de la liste de booléens",
          "correct": false,
          "feedback": "Erreur : la liste est déjà triée par\nhypothèse. La trier de nouveau ne change\nrien et coûte un tri complet pour rien.\n"
        },
        {
          "text": "Une dichotomie adaptée, resserrée vers la frontière",
          "correct": true,
          "feedback": "Bonne réponse : selon que le milieu est\n`True` ou `False`, on conserve la moitié\nqui contient la frontière. La dichotomie\nne se limite pas à la recherche\nd'égalité : dès qu'une propriété est\n**monotone** (vraie partout au-dessus\nd'un seuil, fausse en dessous), on peut\nchercher le seuil en `O(log n)`.\n"
        }
      ],
      "explanation": "Schéma général : si `P(i)` est une propriété\nmonotone sur `i` (par exemple : « la valeur en\nposition `i` est `True` »), on trouve la\nfrontière en `O(log n)` par dichotomie. Très\nutile pour des problèmes comme « plus petite\ncapacité satisfaisant un critère », « plus\ngrande valeur respectant une contrainte », etc."
    },
    {
      "id": "q29",
      "difficulty": 2,
      "skills": [
        "milieu",
        "bornes",
        "preuve"
      ],
      "title": "Le milieu reste entre les bornes",
      "partie": "preuve",
      "statement": "Dans la recherche dichotomique, on calcule `m = (g + d) // 2` tant\nque `g <= d`. Pourquoi est-il important que `m` soit toujours\ncompris entre `g` et `d` ?",
      "options": [
        {
          "text": "Pour que chaque tour rétrécisse l'intervalle",
          "correct": true,
          "feedback": "Bonne réponse : comme `g <= m <= d`, poser `g = m + 1` ou\n`d = m - 1` rétrécit l'intervalle d'au moins un élément à chaque\ntour. La boucle ne peut pas durer indéfiniment.\n"
        },
        {
          "text": "Pour que `t[m]` soit toujours égal à la valeur cherchée",
          "correct": false,
          "feedback": "Erreur : `t[m]` est la valeur testée, elle n'est égale à la\ncible qu'en cas de succès.\n"
        },
        {
          "text": "Pour éviter que la liste ne soit modifiée pendant la recherche",
          "correct": false,
          "feedback": "Erreur : la recherche ne modifie jamais la liste, quelle que\nsoit la valeur de `m`.\n"
        },
        {
          "text": "Pour que la liste reste triée d'un tour à l'autre",
          "correct": false,
          "feedback": "Erreur : le tri est une précondition, il ne dépend pas de `m`.\n"
        }
      ],
      "explanation": "La terminaison se prouve avec le variant `d - g`, entier positif\nqui diminue strictement à chaque tour grâce à l'encadrement de `m`."
    },
    {
      "id": "q30",
      "difficulty": 3,
      "skills": [
        "invariant",
        "absence",
        "conclusion"
      ],
      "title": "Conclure que la valeur est absente",
      "partie": "preuve",
      "statement": "La recherche dichotomique s'arrête avec `g > d` sans avoir trouvé\nla valeur `x`. Pourquoi peut-on affirmer que `x` n'est pas dans la\nliste ?",
      "options": [
        {
          "text": "Parce que l'invariant place `x`, s'il existe, dans un intervalle désormais vide",
          "correct": true,
          "feedback": "Bonne réponse : à chaque tour, les éléments écartés ne peuvent\npas être `x`. Quand l'intervalle restant est vide, il ne reste\naucune place possible pour `x`.\n"
        },
        {
          "text": "Parce que la boucle a examiné tous les éléments de la liste un par un",
          "correct": false,
          "feedback": "Erreur : la dichotomie n'examine qu'une petite partie des\néléments ; c'est le raisonnement, pas l'examen exhaustif, qui\nconclut.\n"
        },
        {
          "text": "Parce que `t[m]` était différent de `x` au dernier tour, ce qui conclut",
          "correct": false,
          "feedback": "Erreur : une différence au dernier tour ne dit rien sur les\nautres éléments ; c'est l'invariant qui le dit.\n"
        },
        {
          "text": "Parce que la liste est triée et que `x` est plus grand que tous ses éléments",
          "correct": false,
          "feedback": "Erreur : `x` peut être absent tout en étant compris entre deux\néléments de la liste.\n"
        }
      ],
      "explanation": "La correction d'un algorithme se prouve par un invariant : ici,\n« si `x` est dans la liste, alors son indice est dans `[g, d]` »."
    },
    {
      "id": "q31",
      "difficulty": 2,
      "skills": [
        "cout",
        "logarithme",
        "calcul"
      ],
      "title": "Chercher dans un milliard d'éléments",
      "partie": "preuve",
      "statement": "Une liste triée contient un milliard d'éléments. Combien de tours de\nboucle, au maximum, la recherche dichotomique effectue-t-elle ?",
      "options": [
        {
          "text": "Environ $30$",
          "correct": true,
          "feedback": "Bonne réponse : chaque tour divise l'intervalle par deux, et\n$2^{30}$ dépasse un milliard. Une trentaine de tours suffisent pour un\nmilliard d'éléments.\n"
        },
        {
          "text": "Environ un milliard",
          "correct": false,
          "feedback": "Erreur : c'est le pire cas de la recherche linéaire, qui examine\nchaque élément.\n"
        },
        {
          "text": "Environ cinq cents millions",
          "correct": false,
          "feedback": "Erreur : diviser par deux une seule fois ne suffit pas ; la\ndichotomie recommence à chaque tour.\n"
        },
        {
          "text": "Environ $30 \\times 30$",
          "correct": false,
          "feedback": "Erreur : le nombre de tours est logarithmique, pas son carré.\n"
        }
      ],
      "explanation": "Le coût logarithmique est ce qui rend la dichotomie imbattable sur\nde grands volumes : multiplier la taille par mille ajoute dix tours.",
      "variants": [
        {
          "statement": "Une liste triée contient un milliard d'éléments. Combien de tours de\nboucle, au maximum, la recherche dichotomique effectue-t-elle ?",
          "options": [
            {
              "text": "Environ $30$",
              "correct": true,
              "feedback": "Bonne réponse : chaque tour divise l'intervalle par deux, et\n$2^{30}$ dépasse un milliard. Une trentaine de tours suffisent pour un\nmilliard d'éléments.\n"
            },
            {
              "text": "Environ un milliard",
              "correct": false,
              "feedback": "Erreur : c'est le pire cas de la recherche linéaire, qui examine\nchaque élément.\n"
            },
            {
              "text": "Environ cinq cents millions",
              "correct": false,
              "feedback": "Erreur : diviser par deux une seule fois ne suffit pas ; la\ndichotomie recommence à chaque tour.\n"
            },
            {
              "text": "Environ $30 \\times 30$",
              "correct": false,
              "feedback": "Erreur : le nombre de tours est logarithmique, pas son carré.\n"
            }
          ],
          "explanation": "Le coût logarithmique est ce qui rend la dichotomie imbattable sur\nde grands volumes : multiplier la taille par mille ajoute dix tours."
        },
        {
          "statement": "Une liste triée contient un million d'éléments. Combien de tours de\nboucle, au maximum, la recherche dichotomique effectue-t-elle ?",
          "options": [
            {
              "text": "Environ $20$",
              "correct": true,
              "feedback": "Bonne réponse : chaque tour divise l'intervalle par deux, et\n$2^{20}$ dépasse un million. Une trentaine de tours suffisent pour un\nmilliard d'éléments.\n"
            },
            {
              "text": "Environ un million",
              "correct": false,
              "feedback": "Erreur : c'est le pire cas de la recherche linéaire, qui examine\nchaque élément.\n"
            },
            {
              "text": "Environ cinq cent mille",
              "correct": false,
              "feedback": "Erreur : diviser par deux une seule fois ne suffit pas ; la\ndichotomie recommence à chaque tour.\n"
            },
            {
              "text": "Environ $20 \\times 20$",
              "correct": false,
              "feedback": "Erreur : le nombre de tours est logarithmique, pas son carré.\n"
            }
          ],
          "explanation": "Le coût logarithmique est ce qui rend la dichotomie imbattable sur\nde grands volumes : multiplier la taille par mille ajoute dix tours."
        },
        {
          "statement": "Une liste triée contient mille d'éléments. Combien de tours de\nboucle, au maximum, la recherche dichotomique effectue-t-elle ?",
          "options": [
            {
              "text": "Environ $10$",
              "correct": true,
              "feedback": "Bonne réponse : chaque tour divise l'intervalle par deux, et\n$2^{10}$ dépasse mille. Une trentaine de tours suffisent pour un\nmilliard d'éléments.\n"
            },
            {
              "text": "Environ mille",
              "correct": false,
              "feedback": "Erreur : c'est le pire cas de la recherche linéaire, qui examine\nchaque élément.\n"
            },
            {
              "text": "Environ cinq cents",
              "correct": false,
              "feedback": "Erreur : diviser par deux une seule fois ne suffit pas ; la\ndichotomie recommence à chaque tour.\n"
            },
            {
              "text": "Environ $10 \\times 10$",
              "correct": false,
              "feedback": "Erreur : le nombre de tours est logarithmique, pas son carré.\n"
            }
          ],
          "explanation": "Le coût logarithmique est ce qui rend la dichotomie imbattable sur\nde grands volumes : multiplier la taille par mille ajoute dix tours."
        }
      ]
    },
    {
      "id": "q32",
      "difficulty": 3,
      "skills": [
        "correction",
        "tri",
        "raisonnement"
      ],
      "title": "Pourquoi on peut écarter une moitié",
      "partie": "preuve",
      "statement": "Au cours d'une recherche dichotomique, on constate `t[m] < x`. Sur\nquoi repose le droit d'abandonner tous les indices de `g` à `m` ?",
      "options": [
        {
          "text": "Sur le tri, qui rend ces éléments inférieurs ou égaux à `t[m]`",
          "correct": true,
          "feedback": "Bonne réponse : dans une liste triée, tout élément d'indice\ninférieur à `m` vaut au plus `t[m]`, lui-même plus petit que\n`x`. Aucun ne peut être `x`.\n"
        },
        {
          "text": "Sur le fait que `x` est toujours dans la seconde moitié d'une liste",
          "correct": false,
          "feedback": "Erreur : `x` peut être n'importe où, ou absent ; c'est la\ncomparaison avec `t[m]` qui décide.\n"
        },
        {
          "text": "Sur le fait que `m` est le milieu exact de la liste, ni plus ni moins",
          "correct": false,
          "feedback": "Erreur : la position précise de `m` ne compte pas ; seul le tri\nautorise l'élimination.\n"
        },
        {
          "text": "Sur une hypothèse qui doit être vérifiée après coup par une recherche linéaire",
          "correct": false,
          "feedback": "Erreur : aucune vérification supplémentaire n'est nécessaire,\nle raisonnement est exact.\n"
        }
      ],
      "explanation": "Sans tri, aucune moitié ne peut être écartée : la précondition est\nce qui rend chaque étape de la dichotomie correcte."
    },
    {
      "id": "q33",
      "difficulty": 2,
      "skills": [
        "precondition",
        "acces-direct"
      ],
      "title": "Une autre condition que le tri",
      "partie": "principe",
      "statement": "Outre le fait que les données soient triées, quelle propriété de la\nstructure est nécessaire pour que la dichotomie soit efficace ?",
      "options": [
        {
          "text": "Pouvoir accéder directement à l'élément du milieu",
          "correct": true,
          "feedback": "Bonne réponse : une liste Python offre cet accès par indice. Si\natteindre le milieu obligeait à parcourir la moitié des\néléments, l'avantage disparaîtrait.\n"
        },
        {
          "text": "Que tous les éléments soient des entiers positifs",
          "correct": false,
          "feedback": "Erreur : des chaînes triées se prêtent aussi bien à la\ndichotomie.\n"
        },
        {
          "text": "Que la liste ait un nombre pair d'éléments pour couper au milieu",
          "correct": false,
          "feedback": "Erreur : la division entière gère les deux cas.\n"
        },
        {
          "text": "Que les éléments soient tous différents les uns des autres",
          "correct": false,
          "feedback": "Erreur : les doublons ne gênent pas la recherche, ils changent\nseulement l'indice renvoyé.\n"
        }
      ],
      "explanation": "Deux préconditions : des données triées et un accès direct par\nposition. Les listes Python satisfont la seconde."
    },
    {
      "id": "q34",
      "difficulty": 1,
      "skills": [
        "principe",
        "analogie"
      ],
      "title": "Chercher un mot dans un dictionnaire papier",
      "partie": "principe",
      "statement": "Pour trouver un mot dans un dictionnaire papier, on ouvre au milieu,\non compare, puis on ne cherche plus que dans une moitié. Qu'est-ce\nqui rend cette méthode possible ?",
      "options": [
        {
          "text": "Les mots sont rangés dans l'ordre alphabétique",
          "correct": true,
          "feedback": "Bonne réponse : l'ordre permet de savoir, après une seule\ncomparaison, dans quelle moitié continuer. C'est exactement la\ndichotomie.\n"
        },
        {
          "text": "Le dictionnaire a un nombre pair de pages",
          "correct": false,
          "feedback": "Erreur : le nombre de pages n'a aucune importance.\n"
        },
        {
          "text": "Les mots courts sont au début du dictionnaire",
          "correct": false,
          "feedback": "Erreur : seul l'ordre alphabétique compte, pas la longueur des\nmots.\n"
        },
        {
          "text": "Chaque page contient le même nombre de mots",
          "correct": false,
          "feedback": "Erreur : la régularité des pages n'intervient pas dans le\nraisonnement.\n"
        }
      ],
      "explanation": "La dichotomie est une méthode quotidienne dès que les données sont\nordonnées : dictionnaire, annuaire, liste de notes triée."
    },
    {
      "id": "q35",
      "difficulty": 2,
      "skills": [
        "milieu",
        "indice",
        "calcul"
      ],
      "title": "Le premier indice du milieu",
      "partie": "code",
      "statement": "Une liste compte $10$ éléments. Au premier tour, avec `g = 0` et\n`d = 9`, que vaut `m = (g + d) // 2` ?",
      "options": [
        {
          "text": "`4`",
          "correct": true,
          "feedback": "Bonne réponse : `(0 + 9) // 2` vaut 4, l'indice du milieu\navec la division entière.\n"
        },
        {
          "text": "`5`",
          "correct": false,
          "feedback": "Erreur : 5 serait `10 // 2`, mais `d` vaut `9`, pas\n`10`.\n"
        },
        {
          "text": "`9`",
          "correct": false,
          "feedback": "Erreur : 9 est le dernier indice, pas le milieu.\n"
        },
        {
          "text": "`10`",
          "correct": false,
          "feedback": "Erreur : l'indice 10 est hors de la liste.\n"
        }
      ],
      "explanation": "Les indices vont de `0` à `n - 1` ; le milieu se calcule sur ces\nbornes, avec une division entière.",
      "variants": [
        {
          "statement": "Une liste compte $10$ éléments. Au premier tour, avec `g = 0` et\n`d = 9`, que vaut `m = (g + d) // 2` ?",
          "options": [
            {
              "text": "`4`",
              "correct": true,
              "feedback": "Bonne réponse : `(0 + 9) // 2` vaut 4, l'indice du milieu\navec la division entière.\n"
            },
            {
              "text": "`5`",
              "correct": false,
              "feedback": "Erreur : 5 serait `10 // 2`, mais `d` vaut `9`, pas\n`10`.\n"
            },
            {
              "text": "`9`",
              "correct": false,
              "feedback": "Erreur : 9 est le dernier indice, pas le milieu.\n"
            },
            {
              "text": "`10`",
              "correct": false,
              "feedback": "Erreur : l'indice 10 est hors de la liste.\n"
            }
          ],
          "explanation": "Les indices vont de `0` à `n - 1` ; le milieu se calcule sur ces\nbornes, avec une division entière."
        },
        {
          "statement": "Une liste compte $8$ éléments. Au premier tour, avec `g = 0` et\n`d = 7`, que vaut `m = (g + d) // 2` ?",
          "options": [
            {
              "text": "`3`",
              "correct": true,
              "feedback": "Bonne réponse : `(0 + 7) // 2` vaut 3, l'indice du milieu\navec la division entière.\n"
            },
            {
              "text": "`4`",
              "correct": false,
              "feedback": "Erreur : 4 serait `8 // 2`, mais `d` vaut `7`, pas\n`8`.\n"
            },
            {
              "text": "`7`",
              "correct": false,
              "feedback": "Erreur : 7 est le dernier indice, pas le milieu.\n"
            },
            {
              "text": "`8`",
              "correct": false,
              "feedback": "Erreur : l'indice 8 est hors de la liste.\n"
            }
          ],
          "explanation": "Les indices vont de `0` à `n - 1` ; le milieu se calcule sur ces\nbornes, avec une division entière."
        },
        {
          "statement": "Une liste compte $100$ éléments. Au premier tour, avec `g = 0` et\n`d = 99`, que vaut `m = (g + d) // 2` ?",
          "options": [
            {
              "text": "`49`",
              "correct": true,
              "feedback": "Bonne réponse : `(0 + 99) // 2` vaut 49, l'indice du milieu\navec la division entière.\n"
            },
            {
              "text": "`50`",
              "correct": false,
              "feedback": "Erreur : 50 serait `100 // 2`, mais `d` vaut `99`, pas\n`100`.\n"
            },
            {
              "text": "`99`",
              "correct": false,
              "feedback": "Erreur : 99 est le dernier indice, pas le milieu.\n"
            },
            {
              "text": "`100`",
              "correct": false,
              "feedback": "Erreur : l'indice 100 est hors de la liste.\n"
            }
          ],
          "explanation": "Les indices vont de `0` à `n - 1` ; le milieu se calcule sur ces\nbornes, avec une division entière."
        }
      ]
    },
    {
      "id": "q36",
      "difficulty": 3,
      "skills": [
        "bug",
        "division",
        "indice"
      ],
      "title": "Une division de trop",
      "partie": "code",
      "statement": "Lina écrit `m = (g + d) / 2` au lieu de `m = (g + d) // 2`, puis\nutilise `t[m]`. Que se passe-t-il ?",
      "options": [
        {
          "text": "Une erreur, un indice ne pouvant être un flottant",
          "correct": true,
          "feedback": "Bonne réponse : `/` renvoie un flottant, même quand la division\ntombe juste. `t[2.0]` provoque une erreur de type.\n"
        },
        {
          "text": "La recherche fonctionne, Python arrondissant l'indice",
          "correct": false,
          "feedback": "Erreur : Python n'arrondit pas un indice flottant, il refuse.\n"
        },
        {
          "text": "La recherche renvoie toujours `-1`, le milieu étant faux",
          "correct": false,
          "feedback": "Erreur : le programme s'arrête sur une erreur avant de renvoyer\nquoi que ce soit.\n"
        },
        {
          "text": "La boucle devient infinie, `m` ne changeant plus",
          "correct": false,
          "feedback": "Erreur : l'exécution échoue dès le premier accès `t[m]`.\n"
        }
      ],
      "explanation": "Un indice est un entier : le calcul du milieu emploie la division\nentière `//`."
    }
  ]
}