{
  "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).",
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition"
      ],
      "title": "Principe",
      "statement": "Quel est le principe de la **recherche dichotomique** ?",
      "options": [
        {
          "text": "Trier la liste avant chaque comparaison",
          "correct": false,
          "feedback": "Erreur : la liste doit être triée **avant**\nla recherche, pas pendant.\n"
        },
        {
          "text": "Utiliser un dictionnaire",
          "correct": false,
          "feedback": "Erreur : c'est une autre stratégie (table\nde hachage), pas la dichotomie.\n"
        },
        {
          "text": "Parcourir la liste élément par élément",
          "correct": false,
          "feedback": "Erreur : c'est la recherche **linéaire**.\n"
        },
        {
          "text": "Comparer la cible à l'élément du milieu, puis répéter sur la moitié appropriée",
          "correct": true,
          "feedback": "Bonne réponse : à chaque étape, on **élimine\nla 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",
      "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é",
      "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 : c'est la complexité du tri (par\nfusion ou rapide), pas de la recherche.\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 : ~ 20 comparaisons. Pour 10⁹\néléments : ~ 30 comparaisons. La dichotomie\npasse à l'échelle remarquablement."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "milieu"
      ],
      "title": "Calcul du milieu",
      "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é",
      "statement": "Si la valeur à l'indice du milieu est **plus\npetite** 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\nplus petites** (liste triée croissant).\n"
        },
        {
          "text": "Dans toute 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",
      "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` et `d = m` ne réduisent pas\nl'intervalle. Boucle infinie possible.\n"
        },
        {
          "text": "```python\ndef dicho(liste, cible):\n    for x in liste:\n        if x == cible:\n            return x\n    return -1\n```\n",
          "correct": false,
          "feedback": "Erreur : c'est de la recherche **linéaire**,\npas dichotomique. Et on renvoie la valeur,\npas l'indice.\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    return liste.index(cible)\n```\n",
          "correct": false,
          "feedback": "Erreur : `list.index` fait une recherche\n**linéaire** en interne, et lève une\nexception si absente.\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",
      "statement": "Dans le code itératif `while g <= d:`, quand la\nboucle se termine-t-elle si la cible est absente ?",
      "options": [
        {
          "text": "Quand `g == d`",
          "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` (les deux indices se sont 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",
      "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 ?",
      "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 moins de mémoire",
          "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",
          "correct": false,
          "feedback": "Erreur : c'est la **précondition**\nfondamentale.\n"
        },
        {
          "text": "Elle est exponentiellement plus rapide sur les listes triées de grande taille",
          "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\n~ 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 O(n log n). Pour une **seule** recherche,\nla linéaire est plus rapide."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "trace"
      ],
      "title": "Trace simple",
      "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)."
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "invariant"
      ],
      "title": "Invariant de boucle",
      "statement": "Quel **invariant de boucle** garantit la\ncorrection de la recherche dichotomique\nitérative (avec les indices `g` et `d`) ?",
      "options": [
        {
          "text": "La cible est toujours à l'indice du milieu",
          "correct": false,
          "feedback": "Erreur : sinon la dichotomie réussirait\nen une seule comparaison.\n"
        },
        {
          "text": "La liste est triée",
          "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 dans la liste, alors elle se trouve nécessairement dans la tranche `liste[g..d]` (notation algorithmique inclusive des deux côtés ; en Python : `liste[g:d+1]`)",
          "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": "`g` et `d` sont toujours égaux",
          "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",
      "statement": "Si la cible apparaît **plusieurs fois** dans la\nliste triée, quelle position renvoie la\ndichotomie standard ?",
      "options": [
        {
          "text": "Une occurrence quelconque (la première rencontrée pendant la dichotomie)",
          "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 dernière occurrence (à droite)",
          "correct": false,
          "feedback": "Possible mais non garanti.\n"
        },
        {
          "text": "Toutes les occurrences",
          "correct": false,
          "feedback": "Erreur : la dichotomie renvoie un seul\nindice.\n"
        },
        {
          "text": "La première occurrence (à 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-detaillee"
      ],
      "title": "Trace détaillée",
      "statement": "On cherche `cible = 25` dans\n`liste = [3, 7, 11, 17, 25, 31, 42]`. Combien de\ncomparaisons effectue la dichotomie itérative\navant de trouver la cible ?",
      "options": [
        {
          "text": "1",
          "correct": false,
          "feedback": "Erreur : la cible n'est pas en indice 3.\n"
        },
        {
          "text": "7",
          "correct": false,
          "feedback": "Erreur : c'est le nombre d'éléments, pas\nde comparaisons.\n"
        },
        {
          "text": "2",
          "correct": true,
          "feedback": "Bonne réponse : étape 1, milieu = 3 (val\n17 < 25, on va à droite, g=4) ; étape 2,\nmilieu = (4+6)//2 = 5 (val 31 > 25, on va à\ngauche, d=4) ; étape 3, milieu = 4 (val 25\n= cible). En réalité 3 comparaisons, mais 2\ndécisions de moitié.\n\nNote : si la question demande « comparaisons »\nau sens « tests d'égalité », c'est 3 ; au\nsens « décisions de moitié », c'est 2.\n"
        },
        {
          "text": "3",
          "correct": false,
          "feedback": "Cette réponse compte les **comparaisons\nd'égalité** (3, 17, 25). Selon\nl'interprétation, c'est aussi correct, mais\nle compte par décisions de moitié donne 2.\n"
        }
      ],
      "explanation": "Pour log₂(7) ≈ 2,8, on attend au plus 3\ncomparaisons. C'est cohérent."
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "absent"
      ],
      "title": "Élément absent",
      "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",
      "statement": "Un élève écrit `while g < d:` au lieu de\n`while g <= d:`. Quel est l'effet ?",
      "options": [
        {
          "text": "La fonction est plus rapide",
          "correct": false,
          "feedback": "Erreur : juste incorrecte.\n"
        },
        {
          "text": "La fonction renvoie toujours -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 rate la cible quand elle est en position telle que `g == d` à la fin",
          "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": "Boucle infinie",
          "correct": false,
          "feedback": "Pas de boucle infinie, 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": [
        "recherche-trouvee"
      ],
      "title": "Indice retourné en cas de succès",
      "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. La méthode `list.index`\nde Python lève une exception `ValueError` à\nla place du `-1`."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "longueur-1"
      ],
      "title": "Liste à un élément",
      "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, on aurait besoin de 1 ou 2\nitérations selon les détails du code."
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "tri-prealable"
      ],
      "title": "Tri préalable utile ?",
      "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 dichotomique = mieux",
          "correct": false,
          "feedback": "Erreur : « mieux » dépend du contexte. Coût\nde tri à amortir.\n"
        },
        {
          "text": "Oui, c'est plus rapide",
          "correct": false,
          "feedback": "Erreur : trier coûte O(n log n) ≈ 10 000\nopérations, plus 10 pour la dichotomie. La\nrecherche linéaire ne fait au plus que\n1 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 recherche 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 : tri rentable si l'on fait au moins ~ k\nrecherches, avec k tel que `n log n + k log n\n< k n`. Approximativement k > log n."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "pseudo-code"
      ],
      "title": "Recherche par approximation",
      "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 : il suffit de continuer à\nchercher dans la moitié gauche après une\négalité (au lieu de s'arrêter), pour\nretomber sur la plus petite valeur ≥\ncible. 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",
      "statement": "Pourquoi la dichotomie itérative **se termine**\ntoujours (pas de boucle infinie) ?",
      "options": [
        {
          "text": "Parce qu'à chaque itération, l'intervalle `[g, d]` se rétrécit strictement",
          "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 est forcément 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 triée",
          "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",
      "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 est plus rapide",
          "correct": false,
          "feedback": "Erreur : elle peut boucler indéfiniment.\n"
        },
        {
          "text": "La fonction lève une exception",
          "correct": false,
          "feedback": "Erreur : pas d'exception, juste une boucle\ninfinie.\n"
        },
        {
          "text": "La fonction renvoie toujours -1",
          "correct": false,
          "feedback": "Pas systématiquement : selon l'entrée, soit\nelle trouve la cible, soit elle boucle.\n"
        },
        {
          "text": "Boucle infinie possible si `g + d` est pair (donc `m == g`)",
          "correct": true,
          "feedback": "Bonne réponse : si `g = 0`, `d = 1`, alors\n`m = 0`. Si on fait `g = m`, on reste à\n`g = 0`, et la boucle ne progresse pas.\nD'où l'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-pas-a-pas"
      ],
      "title": "Trace pas à pas",
      "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`\n(`liste[3]=4 > 2`, on va à gauche : `d=2`) ;\nétape 2, `g=0,d=2,m=1` (`liste[1]=2 = cible`,\nretour immédiat de l'indice 1). Deux indices\ntesté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",
      "statement": "Quel est le **variant de boucle** (quantité qui\ndécroît strictement) de la dichotomie ?",
      "options": [
        {
          "text": "La valeur du milieu `m`",
          "correct": false,
          "feedback": "Erreur : `m` peut augmenter ou diminuer selon\nla comparaison.\n"
        },
        {
          "text": "La cible",
          "correct": false,
          "feedback": "Erreur : la cible ne change pas pendant la\nrecherche.\n"
        },
        {
          "text": "L'indice `g`",
          "correct": false,
          "feedback": "Erreur : `g` peut rester constant si on met\nà jour seulement `d`.\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",
      "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 sont nécessaires",
          "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": "~ 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": [
        "synthese"
      ],
      "title": "Synthèse",
      "statement": "Parmi les affirmations suivantes sur la\nrecherche dichotomique, laquelle est **fausse** ?",
      "options": [
        {
          "text": "Sa complexité est O(log n)",
          "correct": false,
          "feedback": "Vrai : c'est sa caractéristique fondamentale.\n"
        },
        {
          "text": "Elle ne fonctionne que sur des listes triées",
          "correct": false,
          "feedback": "Vrai : c'est la précondition essentielle.\n"
        },
        {
          "text": "Des variantes adaptées du code permettent de localiser la première ou la dernière occurrence en cas de doublons",
          "correct": false,
          "feedback": "Vrai : il suffit, après une égalité, de\ncontinuer à chercher à gauche (pour la\npremière occurrence) ou à droite (pour\nla dernière) au lieu de s'arrêter. La\ncomplexité reste `O(log n)`.\n"
        },
        {
          "text": "Elle est toujours plus rapide que la recherche linéaire",
          "correct": true,
          "feedback": "Faux (donc bonne réponse) : pour une **seule**\nrecherche dans une liste **non triée**,\ntrier puis dichotomiser coûte plus cher que\nla linéaire. Et sur des très petites listes\n(< 10 éléments), la différence est\nnégligeable.\n"
        }
      ],
      "explanation": "Aucun algorithme universellement supérieur :\nla dichotomie est très puissante mais a ses\nconditions d'application (tri, taille\nsuffisante)."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "recursive"
      ],
      "title": "Version récursive",
      "statement": "Quel code implémente correctement une **version\nrécursive** de la recherche dichotomique\n(renvoie l'indice ou `-1`) ?",
      "options": [
        {
          "text": "```python\ndef dicho(liste, cible, g, d):\n    if g >= d:\n        return -1\n    m = (g + d) // 2\n    if liste[m] == cible:\n        return m\n    return dicho(liste, cible, g, d)\n```\n",
          "correct": false,
          "feedback": "Erreur double : le cas de base `g >= d` rate\nla cible quand l'intervalle se réduit à un\nseul élément (cas similaire au bug `while g < d:`),\net l'appel récursif sans mise à jour de\n`g, d` provoque une récursion infinie si la\ncible n'est pas en milieu.\n"
        },
        {
          "text": "```python\ndef dicho(liste, cible, g, d):\n    if g > d:\n        return -1\n    m = (g + d) // 2\n    if liste[m] == cible:\n        return m\n    elif liste[m] < cible:\n        return dicho(liste, cible, m + 1, d)\n    else:\n        return dicho(liste, cible, g, m - 1)\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : cas de base `g > d` (intervalle\nvide), sinon trois cas selon la comparaison\nau milieu. La récursion porte sur des bornes\nmises à jour, exactement comme dans la version\nitérative.\n"
        },
        {
          "text": "```python\ndef dicho(liste, cible, g, d):\n    m = (g + d) // 2\n    if liste[m] == cible:\n        return m\n    elif liste[m] < cible:\n        return dicho(liste, cible, m + 1, d)\n    else:\n        return dicho(liste, cible, g, m - 1)\n```\n",
          "correct": false,
          "feedback": "Erreur : il manque le cas de base. Quand la\ncible est absente, l'intervalle se rétrécit\njusqu'à devenir vide (`g > d`), puis on\naccède à `liste[m]` avec un indice invalide,\nce qui peut lever une exception ou faire une\nrécursion infinie.\n"
        },
        {
          "text": "```python\ndef dicho(liste, cible):\n    if len(liste) == 0:\n        return -1\n    m = len(liste) // 2\n    if liste[m] == cible:\n        return m\n    elif liste[m] < cible:\n        return dicho(liste[m+1:], cible)\n    else:\n        return dicho(liste[:m], cible)\n```\n",
          "correct": false,
          "feedback": "Erreur : le découpage par tranche\n`liste[m+1:]` recopie une partie de la liste\n(coût O(n) à chaque appel), ce qui ruine\nl'avantage de la dichotomie. De plus, l'indice\nrenvoyé est relatif à la sous-liste, donc\nincorrect par rapport à la liste originale.\n"
        }
      ],
      "explanation": "Avantage de la version récursive : code plus\ncourt, qui suit naturellement le raisonnement\n« diviser pour régner ». Inconvénient : risque\nde dépassement de pile pour de très grandes\nlistes (Python limite la profondeur à environ\n1000), ce qui n'est jamais un souci ici car la\nprofondeur reste en log₂(n)."
    },
    {
      "id": "q27",
      "difficulty": 3,
      "skills": [
        "doublement-taille"
      ],
      "title": "Doublement de la taille",
      "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",
      "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 la liste de booléens à nouveau",
          "correct": false,
          "feedback": "Erreur : la liste est déjà triée par\nhypothèse. Trier une liste déjà triée ne\nchange rien, et coûte `O(n log n)`.\n"
        },
        {
          "text": "Une dichotomie adaptée : on regarde le\nmilieu, si c'est `True` la frontière est à\ngauche ou ici, sinon elle est strictement à\ndroite ; on rétrécit l'intervalle jusqu'à\nisoler la frontière\n",
          "correct": true,
          "feedback": "Bonne réponse : la dichotomie ne se limite\npas à la recherche d'égalité. Dès qu'une\npropriété est **monotone** (vraie partout\nau-dessus d'un seuil, fausse en-dessous),\non peut chercher le seuil par dichotomie\nen `O(log n)`, en adaptant légèrement le\ncode (test sur la propriété au lieu d'une\négalité, et choix de la moitié à\nconserver selon la réponse).\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."
    }
  ]
}