{
  "chapter": {
    "id": "recherche-lineaire",
    "level": "premiere",
    "theme": "Algorithmique",
    "title": "Recherche linéaire",
    "description": "Recherche séquentielle d'un élément dans une liste,\nparcours élément par élément, cas favorables et\ndéfavorables, complexité, comparaison avec la recherche\ndichotomique, variantes (indice, booléen, toutes les\noccurrences).",
    "parties": [
      {
        "id": "principe",
        "title": "Principe et écriture"
      },
      {
        "id": "variantes",
        "title": "Variantes de recherche"
      },
      {
        "id": "couts",
        "title": "Coût et comparaison"
      }
    ],
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition"
      ],
      "title": "Recherche linéaire ou séquentielle",
      "partie": "principe",
      "statement": "En quoi consiste la **recherche linéaire** (ou séquentielle) ?",
      "options": [
        {
          "text": "Sauter directement à la position centrale puis comparer",
          "correct": false,
          "feedback": "Erreur : c'est l'idée de la recherche\n**dichotomique**, qui n'est applicable qu'à\nune liste triée.\n"
        },
        {
          "text": "Parcourir la liste et comparer chaque élément à la cible",
          "correct": true,
          "feedback": "Bonne réponse : c'est l'algorithme le plus\nsimple. Il fonctionne sur n'importe quelle\nliste (triée ou non).\n"
        },
        {
          "text": "Utiliser un dictionnaire pour aller directement à la valeur",
          "correct": false,
          "feedback": "Erreur : l'accès direct par clé d'un\ndictionnaire est une autre stratégie,\ndifférente de la recherche linéaire.\n"
        },
        {
          "text": "Trier la liste puis chercher le premier élément",
          "correct": false,
          "feedback": "Erreur : la recherche linéaire ne nécessite\npas de tri préalable.\n"
        }
      ],
      "explanation": "La recherche linéaire est universelle : elle\nfonctionne sur une liste, une chaîne ou un\np-uplet, sans hypothèse sur l'ordre des\néléments. Sa simplicité a un prix : elle peut\nnécessiter jusqu'à n comparaisons."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "hypothese"
      ],
      "title": "Liste triée ou non ?",
      "partie": "principe",
      "statement": "La recherche linéaire suppose-t-elle que la liste\nsoit **triée** ?",
      "options": [
        {
          "text": "Uniquement si la liste contient des nombres",
          "correct": false,
          "feedback": "Erreur : la recherche linéaire fonctionne\nquel que soit le type des éléments\n(nombres, chaînes, objets...).\n"
        },
        {
          "text": "Non, elle fonctionne sur n'importe quelle liste",
          "correct": true,
          "feedback": "Bonne réponse : c'est précisément l'avantage\nde la recherche linéaire. On peut l'appliquer\nà des données non triées (par exemple, une\nliste de noms saisis dans l'ordre d'arrivée).\n"
        },
        {
          "text": "Uniquement si la liste contient au moins 10 éléments",
          "correct": false,
          "feedback": "Erreur : la recherche linéaire fonctionne\naussi sur des listes courtes ou vides.\n"
        },
        {
          "text": "Oui, sinon elle ne fonctionne pas",
          "correct": false,
          "feedback": "Erreur : c'est la **dichotomique** qui exige\nune liste triée. La linéaire fonctionne sans\ncondition.\n"
        }
      ],
      "explanation": "Quand la liste est triée, on peut faire mieux\navec la dichotomique (O(log n)). Mais la linéaire\nreste un choix correct, simple et universel."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "complexite-pire"
      ],
      "title": "Pire cas",
      "partie": "couts",
      "statement": "Quel est le **nombre maximal de comparaisons** dans\nune recherche linéaire dans une liste de longueur n ?",
      "options": [
        {
          "text": "n²",
          "correct": false,
          "feedback": "Erreur : on parcourt la liste **une seule fois**, sans imbrication de boucle.\n"
        },
        {
          "text": "log(n)",
          "correct": false,
          "feedback": "Erreur : c'est la complexité de la recherche\ndichotomique, applicable seulement aux listes\ntriées.\n"
        },
        {
          "text": "n",
          "correct": true,
          "feedback": "Bonne réponse : dans le pire cas (élément\nabsent ou en dernière position), il faut\ncomparer chacun des n éléments. La complexité\nest en O(n).\n"
        },
        {
          "text": "n / 2",
          "correct": false,
          "feedback": "Erreur : n / 2 est le nombre **moyen** de\ncomparaisons (en supposant l'élément présent\nen position aléatoire), pas le maximum.\n"
        }
      ],
      "explanation": "Pire cas : l'élément n'est pas présent. On a fait\nn comparaisons négatives. Meilleur cas : l'élément\nest en première position, une seule comparaison."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "arret"
      ],
      "title": "Quand arrête-t-on la boucle ?",
      "partie": "principe",
      "statement": "Dans une recherche linéaire bien programmée, quand\narrête-t-on de parcourir la liste ?",
      "options": [
        {
          "text": "Au bout de log(n) comparaisons",
          "correct": false,
          "feedback": "Erreur : c'est la dichotomique. Sans\nhypothèse de tri, on ne peut pas s'arrêter\navant.\n"
        },
        {
          "text": "Dès que le tout premier élément testé n'est pas le bon",
          "correct": false,
          "feedback": "Erreur : si le premier élément n'est pas le\nbon, l'élément peut quand même être plus loin.\n"
        },
        {
          "text": "Toujours après avoir parcouru toute la liste",
          "correct": false,
          "feedback": "Pas optimal : si on a déjà trouvé l'élément,\ninutile de continuer. Mais cela donne malgré\ntout un résultat correct (juste plus lent).\n"
        },
        {
          "text": "Dès que la cible est trouvée, ou à la fin de la liste",
          "correct": true,
          "feedback": "Bonne réponse : on s'arrête au plus tôt, par\nexemple avec un `return` dès que la cible est\ntrouvée. Cela améliore le temps moyen mais ne\nchange pas le pire cas (élément absent,\nparcours complet).\n"
        }
      ],
      "explanation": "Schéma Python typique :\n`for i in range(len(liste)):`\n`    if liste[i] == cible: return i`\n`return -1`."
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "retour"
      ],
      "title": "Que renvoyer si absent ?",
      "partie": "principe",
      "statement": "Si l'élément cherché n'est **pas dans la liste**,\nque peut-on raisonnablement renvoyer ?",
      "options": [
        {
          "text": "La dernière position de la liste (n - 1)",
          "correct": false,
          "feedback": "Erreur : on confondrait avec « élément en\ndernière position ».\n"
        },
        {
          "text": "La position 0 (par défaut)",
          "correct": false,
          "feedback": "Erreur : on confondrait avec « élément en\npremière position ».\n"
        },
        {
          "text": "La longueur de la liste",
          "correct": false,
          "feedback": "Erreur : valeur arbitraire, pas une\nconvention reconnue.\n"
        },
        {
          "text": "`None` ou `-1`, pour signaler l'absence",
          "correct": true,
          "feedback": "Bonne réponse : ce sont les deux conventions\ncourantes. Au programmeur de choisir, mais\nil faut **être cohérent** dans tout le code.\n"
        }
      ],
      "explanation": "Quelle que soit la convention choisie, la\nspécification de la fonction doit l'indiquer,\net le programme qui l'appelle doit tester ce\ncas avant d'utiliser le résultat comme une\nposition."
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "code-python"
      ],
      "title": "Code Python typique",
      "partie": "principe",
      "statement": "Quel code implémente correctement la recherche\nlinéaire et renvoie l'**indice** de la cible (ou\n-1 si absente) ?",
      "options": [
        {
          "text": "```python\ndef chercher(liste, cible):\n    for i in range(len(liste)):\n        if liste[i] == cible:\n            return i\n        return -1\n```\n",
          "correct": false,
          "feedback": "Erreur d'**indentation** : `return -1` est\ndans la boucle, ce qui termine après la\npremière comparaison. La fonction renverrait\ntoujours 0 ou -1 selon le premier élément.\n"
        },
        {
          "text": "```python\ndef chercher(liste, cible):\n    for x in liste:\n        if x == cible:\n            return x\n    return -1\n```\n",
          "correct": false,
          "feedback": "Erreur : on renvoie `x` (la **valeur**), pas\nson indice. La spécification demandait\nl'indice.\n"
        },
        {
          "text": "```python\ndef chercher(liste, cible):\n    for i in range(len(liste)):\n        if liste[i] == cible:\n            return i\n    return -1\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : parcours par indice, retour\nprécoce dès la cible trouvée, -1 si la\nboucle se termine sans trouver.\n"
        },
        {
          "text": "```python\ndef chercher(liste, cible):\n    i = 0\n    while i < len(liste):\n        if liste[i] == cible:\n            return i\n    return -1\n```\n",
          "correct": false,
          "feedback": "Erreur : `i` n'est jamais incrémenté. Si la\ncible n'est pas en première position, la\ncondition `i < len(liste)` reste vraie et le\ntest `liste[0] == cible` se répète sans fin :\nboucle infinie. Il manque `i = i + 1` dans\nla boucle.\n"
        }
      ],
      "explanation": "Vérifier deux choses dans toute fonction de\nrecherche : le **type** de retour (indice,\nbooléen, ou objet), et le **comportement** en\ncas d'absence."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "operateur-in"
      ],
      "title": "Opérateur `in`",
      "partie": "principe",
      "statement": "L'expression Python `42 in liste` réalise\nimplicitement quelle opération ?",
      "options": [
        {
          "text": "Un tri complet de la liste avant de répondre",
          "correct": false,
          "feedback": "Erreur : `in` ne modifie pas la liste et ne\nla trie pas.\n"
        },
        {
          "text": "Une recherche dichotomique, rapide",
          "correct": false,
          "feedback": "Erreur : Python ne suppose pas que la liste\nest triée. Il fait du linéaire.\n"
        },
        {
          "text": "Une recherche linéaire, élément par élément",
          "correct": true,
          "feedback": "Bonne réponse : le parcours s'arrête à la\npremière correspondance. Sur une liste de\nn éléments, `in` peut donc effectuer\njusqu'à n comparaisons.\n"
        },
        {
          "text": "Une vérification du type des éléments",
          "correct": false,
          "feedback": "Erreur : `in` teste l'appartenance, pas le\ntype.\n"
        }
      ],
      "explanation": "L'opérateur `in` cache une boucle : son coût\nest linéaire. Placer un test `in` dans une\nboucle revient donc à imbriquer deux\nparcours."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "liste-vide"
      ],
      "title": "Liste vide",
      "partie": "principe",
      "statement": "Que doit renvoyer une recherche linéaire (qui\nrenvoie l'indice ou -1) sur une **liste vide** ?",
      "options": [
        {
          "text": "Le déclenchement immédiat d'une exception",
          "correct": false,
          "feedback": "Pas nécessairement. Avec le schéma\nstandard, la boucle ne s'exécute pas et la\nfonction renvoie `-1` proprement.\n"
        },
        {
          "text": "`None`",
          "correct": false,
          "feedback": "Possible (autre convention), mais selon\nnotre énoncé, la fonction renvoie `-1`.\n"
        },
        {
          "text": "-1 (élément absent)",
          "correct": true,
          "feedback": "Bonne réponse : aucune valeur n'est dans la\nliste vide, donc tout élément y est absent.\nLa boucle ne s'exécute pas, on tombe\ndirectement sur `return -1`.\n"
        },
        {
          "text": "0",
          "correct": false,
          "feedback": "Erreur : 0 signifierait « trouvé en première\nposition » alors qu'il n'y a aucune position.\n"
        }
      ],
      "explanation": "Cas limite à toujours tester. Bonne pratique :\nécrire un test unitaire `assert chercher([], x) == -1`."
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "min-max"
      ],
      "title": "Recherche de minimum",
      "partie": "variantes",
      "statement": "Trouver le **minimum** d'une liste est aussi un\ncas de recherche linéaire. Quelle est sa\ncomplexité ?",
      "options": [
        {
          "text": "O(n²) : double boucle nécessaire",
          "correct": false,
          "feedback": "Erreur : un seul parcours suffit (en gardant\nle minimum courant dans une variable).\n"
        },
        {
          "text": "O(1) : on prend juste le premier élément",
          "correct": false,
          "feedback": "Erreur : le premier élément n'est pas\nforcément le minimum (sauf liste triée).\n"
        },
        {
          "text": "O(log n) : recherche dichotomique",
          "correct": false,
          "feedback": "Erreur : ne fonctionne pas, même sur liste\ntriée la dichotomique cherche une cible\nprécise, pas un extremum.\n"
        },
        {
          "text": "O(n) : on doit comparer tous les éléments",
          "correct": true,
          "feedback": "Bonne réponse : pour garantir le minimum, il\nfaut **examiner chaque** élément. On ne peut\npas s'arrêter avant la fin (contrairement à\nla recherche d'une cible précise).\n"
        }
      ],
      "explanation": "Note importante : ici on **ne peut pas s'arrêter tôt** car on ne sait pas si un élément plus\npetit existe plus loin. C'est différent de la\nrecherche d'une valeur précise."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "trace",
        "arret-precoce"
      ],
      "title": "Trace simple",
      "partie": "principe",
      "statement": "On cherche `cible = 7` dans `liste = [3, 5, 7, 2, 7]`.\nCombien de comparaisons effectue une recherche\nlinéaire **avec arrêt précoce** ?",
      "options": [
        {
          "text": "5",
          "correct": false,
          "feedback": "Erreur : on ne parcourt pas toute la liste si\non s'arrête au premier `7` rencontré.\n"
        },
        {
          "text": "1",
          "correct": false,
          "feedback": "Erreur : il faut comparer 3, 5 avant\nd'arriver à 7.\n"
        },
        {
          "text": "2",
          "correct": false,
          "feedback": "Erreur : il faut bien tester `liste[2]` (et\nla trouver égale à 7) pour conclure.\n"
        },
        {
          "text": "3",
          "correct": true,
          "feedback": "Bonne réponse : `liste[0]=3` (≠), `liste[1]=5` (≠), `liste[2]=7` (=) → on s'arrête. 3\ncomparaisons.\n"
        }
      ],
      "explanation": "Sans arrêt précoce, on aurait fait 5\ncomparaisons et compté 2 occurrence(s). Avec\narrêt précoce, 3 comparaisons et indice retourné\ndirectement (= 2).",
      "variants": [
        {
          "statement": "On cherche `cible = 7` dans `liste = [3, 5, 7, 2, 7]`.\nCombien de comparaisons effectue une recherche\nlinéaire **avec arrêt précoce** ?",
          "options": [
            {
              "text": "5",
              "correct": false,
              "feedback": "Erreur : on ne parcourt pas toute la liste si\non s'arrête au premier `7` rencontré.\n"
            },
            {
              "text": "1",
              "correct": false,
              "feedback": "Erreur : il faut comparer 3, 5 avant\nd'arriver à 7.\n"
            },
            {
              "text": "2",
              "correct": false,
              "feedback": "Erreur : il faut bien tester `liste[2]` (et\nla trouver égale à 7) pour conclure.\n"
            },
            {
              "text": "3",
              "correct": true,
              "feedback": "Bonne réponse : `liste[0]=3` (≠), `liste[1]=5` (≠), `liste[2]=7` (=) → on s'arrête. 3\ncomparaisons.\n"
            }
          ],
          "explanation": "Sans arrêt précoce, on aurait fait 5\ncomparaisons et compté 2 occurrence(s). Avec\narrêt précoce, 3 comparaisons et indice retourné\ndirectement (= 2)."
        },
        {
          "statement": "On cherche `cible = 6` dans `liste = [8, 1, 4, 6, 4, 9]`.\nCombien de comparaisons effectue une recherche\nlinéaire **avec arrêt précoce** ?",
          "options": [
            {
              "text": "6",
              "correct": false,
              "feedback": "Erreur : on ne parcourt pas toute la liste si\non s'arrête au premier `6` rencontré.\n"
            },
            {
              "text": "1",
              "correct": false,
              "feedback": "Erreur : il faut comparer 8, 1, 4 avant\nd'arriver à 6.\n"
            },
            {
              "text": "3",
              "correct": false,
              "feedback": "Erreur : il faut bien tester `liste[3]` (et\nla trouver égale à 6) pour conclure.\n"
            },
            {
              "text": "4",
              "correct": true,
              "feedback": "Bonne réponse : `liste[0]=8` (≠), `liste[1]=1` (≠), `liste[2]=4` (≠), `liste[3]=6` (=) → on s'arrête. 4\ncomparaisons.\n"
            }
          ],
          "explanation": "Sans arrêt précoce, on aurait fait 6\ncomparaisons et compté 1 occurrence(s). Avec\narrêt précoce, 4 comparaisons et indice retourné\ndirectement (= 3)."
        },
        {
          "statement": "On cherche `cible = 9` dans `liste = [1, 3, 5, 7, 9, 7]`.\nCombien de comparaisons effectue une recherche\nlinéaire **avec arrêt précoce** ?",
          "options": [
            {
              "text": "6",
              "correct": false,
              "feedback": "Erreur : on ne parcourt pas toute la liste si\non s'arrête au premier `9` rencontré.\n"
            },
            {
              "text": "1",
              "correct": false,
              "feedback": "Erreur : il faut comparer 1, 3, 5, 7 avant\nd'arriver à 9.\n"
            },
            {
              "text": "4",
              "correct": false,
              "feedback": "Erreur : il faut bien tester `liste[4]` (et\nla trouver égale à 9) pour conclure.\n"
            },
            {
              "text": "5",
              "correct": true,
              "feedback": "Bonne réponse : `liste[0]=1` (≠), `liste[1]=3` (≠), `liste[2]=5` (≠), `liste[3]=7` (≠), `liste[4]=9` (=) → on s'arrête. 5\ncomparaisons.\n"
            }
          ],
          "explanation": "Sans arrêt précoce, on aurait fait 6\ncomparaisons et compté 1 occurrence(s). Avec\narrêt précoce, 5 comparaisons et indice retourné\ndirectement (= 4)."
        }
      ]
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "meilleur-cas"
      ],
      "title": "Meilleur cas",
      "partie": "couts",
      "statement": "Quelle est la complexité dans le **meilleur cas**\nde la recherche linéaire ?",
      "options": [
        {
          "text": "O(1)",
          "correct": true,
          "feedback": "Bonne réponse : si l'élément cherché est en\n**première position**, on s'arrête après une\nseule comparaison.\n"
        },
        {
          "text": "O(n²)",
          "correct": false,
          "feedback": "Erreur : pas de double boucle.\n"
        },
        {
          "text": "O(log n)",
          "correct": false,
          "feedback": "Erreur : aucun mécanisme dichotomique ici.\n"
        },
        {
          "text": "O(n)",
          "correct": false,
          "feedback": "Erreur : O(n) est le pire cas.\n"
        }
      ],
      "explanation": "Pour une recherche d'un élément aléatoirement\nplacé dans la liste, la complexité **moyenne**\nest O(n / 2), souvent simplifiée en O(n) car les\nconstantes sont ignorées dans la notation O."
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "variante-booleen"
      ],
      "title": "Variante booléenne",
      "partie": "variantes",
      "statement": "On veut juste savoir si l'élément est présent ou\nnon (sans avoir besoin de l'indice). Quelle\nfonction renvoie `True` si la cible est dans la\nliste et `False` sinon, pour toute liste ?",
      "options": [
        {
          "text": "```python\ndef present(liste, cible):\n    return liste[cible]\n```\n",
          "correct": false,
          "feedback": "Erreur : `liste[cible]` lit l'élément\nd'indice `cible` ; on obtient une valeur de\nla liste, ou une erreur si `cible` n'est\npas un indice valide, mais jamais le\nbooléen voulu.\n"
        },
        {
          "text": "```python\ndef present(liste, cible):\n    return cible in liste\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : l'opérateur `in` renvoie\ndirectement un booléen. Il réalise la même\nrecherche linéaire qu'une boucle écrite à\nla main.\n"
        },
        {
          "text": "```python\ndef present(liste, cible):\n    for x in liste:\n        if x == cible:\n            return True\n        else:\n            return False\n```\n",
          "correct": false,
          "feedback": "Erreur : le `else` fait sortir la fonction\ndès le premier élément différent de la\ncible ; `present([4, 7], 7)` renvoie\n`False`.\n"
        },
        {
          "text": "```python\ndef present(liste, cible):\n    return cible == liste\n```\n",
          "correct": false,
          "feedback": "Erreur : on compare l'élément à la liste\nentière, ce qui renvoie toujours `False`\n(sauf cas dégénéré).\n"
        }
      ],
      "explanation": "L'opérateur `in` réalise une recherche\nlinéaire avec arrêt au premier succès. Dans\nune boucle écrite à la main, `return False`\nne doit venir qu'après le parcours complet."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "toutes-occurrences"
      ],
      "title": "Toutes les occurrences",
      "partie": "variantes",
      "statement": "Pour trouver **toutes** les positions où la cible\napparaît dans la liste, quelle adaptation de la\nrecherche linéaire utilise-t-on ?",
      "options": [
        {
          "text": "On trie d'abord entièrement la liste avant de chercher",
          "correct": false,
          "feedback": "Erreur : trier modifie l'ordre et fait perdre\nles positions originales.\n"
        },
        {
          "text": "On parcourt toute la liste en enregistrant tous les indices correspondants",
          "correct": true,
          "feedback": "Bonne réponse : pas d'arrêt précoce ici, on\naccumule dans une liste de résultats.\nComplexité : O(n) (parcours unique).\n"
        },
        {
          "text": "On utilise une recherche dichotomique",
          "correct": false,
          "feedback": "Erreur : la dichotomique trouve **une**\noccurrence dans une liste triée, mais ne\nliste pas toutes les occurrences naturellement.\n"
        },
        {
          "text": "On s'arrête à la première occurrence et on l'enregistre",
          "correct": false,
          "feedback": "Erreur : on raterait les suivantes.\n"
        }
      ],
      "explanation": "Code typique :\n`[i for i in range(len(liste)) if liste[i] == cible]`.\nÉlégant, en une ligne, en O(n)."
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "cout",
        "comparaison-methodes"
      ],
      "title": "Linéaire vs dichotomique",
      "partie": "couts",
      "statement": "Pour rechercher un élément dans une **liste triée**\nde 1 000 000 d'éléments, combien de comparaisons\nau pire avec chacune des deux méthodes ?",
      "options": [
        {
          "text": "Linéaire : 1 000 000 ; dichotomique : environ 20",
          "correct": true,
          "feedback": "Bonne réponse : log₂(10⁶) ≈ 20. La\ndichotomique est environ 50 000 fois plus rapide\ndans ce cas. C'est l'intérêt majeur du tri.\n"
        },
        {
          "text": "Linéaire : 1 000 000 ; dichotomique : 1 000",
          "correct": false,
          "feedback": "Erreur : la dichotomique est en O(log n),\nbeaucoup plus efficace.\n"
        },
        {
          "text": "Linéaire : 1 000 ; dichotomique : 1 000 000",
          "correct": false,
          "feedback": "Erreur : valeurs inversées.\n"
        },
        {
          "text": "Les deux sont identiques",
          "correct": false,
          "feedback": "Erreur : la dichotomique est exponentiellement\nplus rapide sur les grandes tailles.\n"
        }
      ],
      "explanation": "log₂(2¹⁰) = 10, log₂(2²⁰) = 20. Doubler la\ntaille de la liste ajoute juste **une**\ncomparaison à la dichotomique, mais double les\ncomparaisons de la linéaire.",
      "variants": [
        {
          "statement": "Pour rechercher un élément dans une **liste triée**\nde 1 000 000 d'éléments, combien de comparaisons\nau pire avec chacune des deux méthodes ?",
          "options": [
            {
              "text": "Linéaire : 1 000 000 ; dichotomique : environ 20",
              "correct": true,
              "feedback": "Bonne réponse : log₂(10⁶) ≈ 20. La\ndichotomique est environ 50 000 fois plus rapide\ndans ce cas. C'est l'intérêt majeur du tri.\n"
            },
            {
              "text": "Linéaire : 1 000 000 ; dichotomique : 1 000",
              "correct": false,
              "feedback": "Erreur : la dichotomique est en O(log n),\nbeaucoup plus efficace.\n"
            },
            {
              "text": "Linéaire : 1 000 ; dichotomique : 1 000 000",
              "correct": false,
              "feedback": "Erreur : valeurs inversées.\n"
            },
            {
              "text": "Les deux sont identiques",
              "correct": false,
              "feedback": "Erreur : la dichotomique est exponentiellement\nplus rapide sur les grandes tailles.\n"
            }
          ],
          "explanation": "log₂(2¹⁰) = 10, log₂(2²⁰) = 20. Doubler la\ntaille de la liste ajoute juste **une**\ncomparaison à la dichotomique, mais double les\ncomparaisons de la linéaire."
        },
        {
          "statement": "Pour rechercher un élément dans une **liste triée**\nde 1 000 éléments, combien de comparaisons\nau pire avec chacune des deux méthodes ?",
          "options": [
            {
              "text": "Linéaire : 1 000 ; dichotomique : environ 10",
              "correct": true,
              "feedback": "Bonne réponse : log₂(10³) ≈ 10. La\ndichotomique est environ 100 fois plus rapide\ndans ce cas. C'est l'intérêt majeur du tri.\n"
            },
            {
              "text": "Linéaire : 1 000 ; dichotomique : 100",
              "correct": false,
              "feedback": "Erreur : la dichotomique est en O(log n),\nbeaucoup plus efficace.\n"
            },
            {
              "text": "Linéaire : 100 ; dichotomique : 1 000",
              "correct": false,
              "feedback": "Erreur : valeurs inversées.\n"
            },
            {
              "text": "Les deux sont identiques",
              "correct": false,
              "feedback": "Erreur : la dichotomique est exponentiellement\nplus rapide sur les grandes tailles.\n"
            }
          ],
          "explanation": "log₂(2¹⁰) = 10, log₂(2²⁰) = 20. Doubler la\ntaille de la liste ajoute juste **une**\ncomparaison à la dichotomique, mais double les\ncomparaisons de la linéaire."
        },
        {
          "statement": "Pour rechercher un élément dans une **liste triée**\nde 1 000 000 000 d'éléments, combien de comparaisons\nau pire avec chacune des deux méthodes ?",
          "options": [
            {
              "text": "Linéaire : 1 000 000 000 ; dichotomique : environ 30",
              "correct": true,
              "feedback": "Bonne réponse : log₂(10⁹) ≈ 30. La\ndichotomique est environ 33 millions de fois plus rapide\ndans ce cas. C'est l'intérêt majeur du tri.\n"
            },
            {
              "text": "Linéaire : 1 000 000 000 ; dichotomique : 1 000 000",
              "correct": false,
              "feedback": "Erreur : la dichotomique est en O(log n),\nbeaucoup plus efficace.\n"
            },
            {
              "text": "Linéaire : 1 000 000 ; dichotomique : 1 000 000 000",
              "correct": false,
              "feedback": "Erreur : valeurs inversées.\n"
            },
            {
              "text": "Les deux sont identiques",
              "correct": false,
              "feedback": "Erreur : la dichotomique est exponentiellement\nplus rapide sur les grandes tailles.\n"
            }
          ],
          "explanation": "log₂(2¹⁰) = 10, log₂(2²⁰) = 20. Doubler la\ntaille de la liste ajoute juste **une**\ncomparaison à la dichotomique, mais double les\ncomparaisons de la linéaire."
        }
      ]
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "bug-init"
      ],
      "title": "Bug d'initialisation",
      "partie": "principe",
      "statement": "Un élève écrit\n```python\ndef chercher(liste, cible):\n    for i in range(len(liste)):\n        if liste[i] == cible:\n            trouve = True\n        else:\n            trouve = False\n    return trouve\n```\nQuel est le bug ?",
      "options": [
        {
          "text": "`return` doit être à l'intérieur de la boucle",
          "correct": false,
          "feedback": "Erreur : ce serait un autre code, mais ce\nn'est pas la cause du bug actuel.\n"
        },
        {
          "text": "Seul le résultat de la dernière comparaison est renvoyé",
          "correct": true,
          "feedback": "Bonne réponse : à chaque itération, le\n`else` réinitialise `trouve`. Si la cible\nest trouvée puis que la dernière itération\néchoue, `trouve` finit à `False`.\nSolution : initialiser `trouve = False`\navant la boucle, et ne faire que\n`trouve = True` à l'intérieur (sans\n`else`).\n"
        },
        {
          "text": "La fonction parcourt la liste deux fois au lieu d'une",
          "correct": false,
          "feedback": "Erreur : il n'y a qu'un seul parcours ; le\nproblème est logique, pas une question de\nperformance.\n"
        },
        {
          "text": "La variable `trouve` n'est pas définie hors de la boucle",
          "correct": false,
          "feedback": "Erreur : Python autorise (à risque) la sortie\nd'une variable de boucle, tant que la\nboucle s'exécute au moins une fois.\n"
        }
      ],
      "explanation": "Schéma correct : initialiser un drapeau\n**avant** la boucle, ne le mettre à `True`\nqu'**en cas de succès**. Mieux : utiliser\n`return True` direct et `return False` après la\nboucle."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "while-vs-for"
      ],
      "title": "While ou for ?",
      "partie": "principe",
      "statement": "Quelle est la différence pratique entre une\nrecherche linéaire écrite avec `for` et avec\n`while` ?",
      "options": [
        {
          "text": "La boucle `while` ne permet pas de parcourir une liste",
          "correct": false,
          "feedback": "Erreur : `while` fonctionne avec n'importe\nquelle condition booléenne, y compris sur\nun indice de liste.\n"
        },
        {
          "text": "`for` parcourt les indices, `while` combine deux conditions d'arrêt",
          "correct": true,
          "feedback": "Bonne réponse : `while` permet une condition\ncomposée. `for` est plus concis quand on\nitère simplement sur la longueur.\n"
        },
        {
          "text": "La boucle `for` s'exécute toujours nettement plus rapidement",
          "correct": false,
          "feedback": "Performance équivalente. La différence est\nstylistique.\n"
        },
        {
          "text": "Les deux sont strictement équivalents en Python",
          "correct": false,
          "feedback": "Sur le plan du résultat oui, sur le plan de\nla lisibilité non.\n"
        }
      ],
      "explanation": "Bonne pratique : `for` pour parcourir une\nstructure connue ; `while` quand on ne sait pas\nà l'avance combien d'itérations seront\nnécessaires (lecture utilisateur, condition\ncomplexe)."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "dictionnaire-recherche",
        "parcours-cles"
      ],
      "title": "Retrouver un nom à partir d'un numéro",
      "partie": "variantes",
      "statement": "Le dictionnaire suivant associe à chaque nom un\nnuméro de téléphone :\n\n```python\nannuaire = {\"Ali\": \"0612\", \"Awa\": \"0745\", \"Tom\": \"0698\"}\n```\n\nQuel moyen permet de retrouver le nom dont le\nnuméro est `\"0745\"` ?",
      "options": [
        {
          "text": "Écrire `annuaire[\"0745\"]`, qui renvoie directement le nom associé",
          "correct": false,
          "feedback": "Erreur : entre crochets, on donne une clé,\net `\"0745\"` n'est pas une clé mais une\nvaleur. Cette instruction provoque une\nerreur.\n"
        },
        {
          "text": "Évaluer `\"0745\" in annuaire`, qui renvoie le nom associé au numéro",
          "correct": false,
          "feedback": "Erreur : `in` teste la présence parmi les\n**clés** et renvoie un booléen, ici\n`False`. Il ne renvoie jamais de nom.\n"
        },
        {
          "text": "Parcourir les clés et comparer à `\"0745\"` la valeur associée à chacune",
          "correct": true,
          "feedback": "Bonne réponse : l'accès par clé ne\nfonctionne que dans le sens nom vers\nnuméro. Dans l'autre sens, on fait une\nrecherche linéaire : `for nom in annuaire:`\npuis on teste `annuaire[nom] == \"0745\"`.\n"
        },
        {
          "text": "Faire une recherche dichotomique sur les numéros, rangés dans l'ordre croissant",
          "correct": false,
          "feedback": "Erreur : rien ne garantit que les numéros\nsoient rangés dans l'ordre, et on ne peut\npas accéder à la valeur du milieu d'un\ndictionnaire par un indice.\n"
        }
      ],
      "explanation": "Dans un dictionnaire, on retrouve une valeur\nà partir de sa clé, pas l'inverse. Pour\nchercher une clé d'après sa valeur, on\nparcourt les clés (ou `annuaire.items()`)\net on s'arrête à la première valeur égale à\nla cible."
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "optimisation-position"
      ],
      "title": "Optimisation par fréquence",
      "partie": "couts",
      "statement": "Si l'on sait qu'un élément est **plus souvent cherché** que les autres, comment optimiser une\nsérie de recherches linéaires successives ?",
      "options": [
        {
          "text": "Trier la liste par ordre croissant",
          "correct": false,
          "feedback": "Le tri n'aide pas la recherche linéaire et\ndemande lui-même de nombreuses comparaisons.\nDe plus, les éléments fréquents ne sont pas\nforcément les plus petits.\n"
        },
        {
          "text": "Doubler purement et simplement la taille de la liste",
          "correct": false,
          "feedback": "Erreur : cela aggrave le problème.\n"
        },
        {
          "text": "Placer les éléments les plus fréquents en début de liste",
          "correct": true,
          "feedback": "Bonne réponse : la recherche linéaire avec\narrêt précoce trouve ces éléments après très\npeu de comparaisons.\n"
        },
        {
          "text": "Convertir la liste en chaîne",
          "correct": false,
          "feedback": "Erreur : aucun gain, et `in` sur une chaîne\nrecherche les sous-chaînes, pas les\néléments.\n"
        }
      ],
      "explanation": "Le nombre de comparaisons d'une recherche\nlinéaire dépend de la position de l'élément\ncherché. C'est un bon exemple où l'**ordre**\ndes données influence la performance."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "recherche-tuple"
      ],
      "title": "Recherche dans liste de tuples",
      "partie": "variantes",
      "statement": "Soit\n`eleves = [(\"Alice\", 17), (\"Bob\", 16), (\"Eve\", 18)]`.\nComment trouver l'âge d'Alice ?",
      "options": [
        {
          "text": "```python\neleves[\"Alice\"]\n```\n",
          "correct": false,
          "feedback": "Erreur : `eleves` est une liste, pas un\ndictionnaire. On ne peut pas l'indexer par\nchaîne.\n"
        },
        {
          "text": "```python\nfor nom, age in eleves:\n    if nom == \"Alice\":\n        return age\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : on déballe chaque tuple en\ndeux variables, on teste le nom, on renvoie\nl'âge. Recherche linéaire classique.\n"
        },
        {
          "text": "```python\nfor nom, age in eleves:\n    if nom == \"Alice\":\n        return nom\n```\n",
          "correct": false,
          "feedback": "Erreur : on renvoie le **nom** (`\"Alice\"`,\ndéjà connu) au lieu de l'âge. La structure\nest bonne, mais la valeur renvoyée n'est\npas celle demandée : il faut `return age`.\n"
        },
        {
          "text": "```python\nfor eleve in eleves:\n    if eleve == \"Alice\":\n        return eleve[1]\n```\n",
          "correct": false,
          "feedback": "Erreur : `eleve` est un **tuple** complet, par\nexemple `(\"Alice\", 17)`, jamais égal à la\nchaîne `\"Alice\"` : la condition est toujours\nfausse et rien n'est renvoyé. Il faut comparer\n`eleve[0]`, ou déballer le tuple en `nom, age`.\n"
        }
      ],
      "explanation": "Si l'on fait souvent ce genre de recherche,\nmieux vaut construire une fois pour toutes un\ndictionnaire des âges :\n`ages = {}`\n`for nom, age in eleves: ages[nom] = age`\npuis `ages[\"Alice\"]` répond en O(1)."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "chaine"
      ],
      "title": "Sur une chaîne",
      "partie": "variantes",
      "statement": "L'expression `\"a\" in \"abracadabra\"` :",
      "options": [
        {
          "text": "Lève une exception",
          "correct": false,
          "feedback": "Erreur : la syntaxe est valide.\n"
        },
        {
          "text": "Renvoie `False`",
          "correct": false,
          "feedback": "Erreur : `\"a\"` apparaît bien dans\n`\"abracadabra\"`.\n"
        },
        {
          "text": "Renvoie le nombre d'occurrences",
          "correct": false,
          "feedback": "Erreur : `in` renvoie un booléen, pas un\nentier. Pour le comptage, utiliser `.count()`.\n"
        },
        {
          "text": "Renvoie `True`",
          "correct": true,
          "feedback": "Bonne réponse : `in` sur une chaîne fait une\nrecherche de **sous-chaîne** (pas de\ncaractère unique seulement). `\"a\"` est\nprésent comme sous-chaîne.\n"
        }
      ],
      "explanation": "Subtilité : sur une chaîne, `\"abc\" in \"ababcabc\"`\nrenvoie aussi `True` (sous-chaîne, pas\ncaractère). Sur une liste, `\"abc\" in liste`\nteste l'égalité avec chaque élément."
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "bug-arret"
      ],
      "title": "Bug : oubli de l'arrêt",
      "partie": "principe",
      "statement": "```python\ndef trouver(liste, cible):\n    indice = -1\n    for i in range(len(liste)):\n        if liste[i] == cible:\n            indice = i\n    return indice\n```\nQuel est le comportement de cette fonction si la\ncible apparaît plusieurs fois ?",
      "options": [
        {
          "text": "Elle renvoie -1",
          "correct": false,
          "feedback": "Erreur : -1 est l'initialisation, mais elle\nest écrasée si une correspondance existe.\n"
        },
        {
          "text": "Elle renvoie l'indice de la dernière occurrence",
          "correct": true,
          "feedback": "Bonne réponse : la boucle continue jusqu'au\nbout, et `indice = i` écrase la valeur à\nchaque correspondance. À la fin, c'est la\ndernière occurrence qui reste.\n"
        },
        {
          "text": "Elle entre dans une boucle infinie",
          "correct": false,
          "feedback": "Erreur : `for` sur `range` est borné.\n"
        },
        {
          "text": "Elle renvoie l'indice de la première occurrence",
          "correct": false,
          "feedback": "Erreur : rien n'arrête la boucle à la\npremière correspondance : il faudrait\nécrire `return i` dès que `liste[i] == cible`.\n"
        }
      ],
      "explanation": "Cas piège mais utile : ce code peut être correct\nsi l'on cherche **la dernière occurrence**. Pour\nla version classique « première occurrence », on\nécrit `return i` à la place de `indice = i` : la\nfonction s'arrête dès la première correspondance."
    },
    {
      "id": "q22",
      "difficulty": 3,
      "skills": [
        "choix-algorithme"
      ],
      "title": "Choisir le bon algorithme",
      "partie": "couts",
      "statement": "Pour rechercher un élément dans une liste de\n100 éléments, **non triée**, qu'on ne consultera\nqu'une seule fois, quelle stratégie est la plus\nefficace **globalement** ?",
      "options": [
        {
          "text": "Recherche linéaire directement",
          "correct": true,
          "feedback": "Bonne réponse : pour **une seule**\nrecherche, la linéaire (au plus 100\ncomparaisons) est plus rapide que tri +\ndichotomie. L'investissement du tri n'est\nrentable que pour de **multiples**\nrecherches.\n"
        },
        {
          "text": "Convertir en dictionnaire",
          "correct": false,
          "feedback": "Erreur : construire le dictionnaire demande\ndéjà de parcourir toute la liste. Pour une\nseule recherche, le gain est nul.\n"
        },
        {
          "text": "Faire la recherche en parallèle sur 4 cœurs",
          "correct": false,
          "feedback": "Sur 100 éléments, le coût de\nparallélisation dépasse largement le coût\nde la recherche.\n"
        },
        {
          "text": "Trier puis dichotomie",
          "correct": false,
          "feedback": "Erreur : trier 100 éléments par sélection\ncoûte $100 \\times 99 / 2 = 4\\,950$\ncomparaisons, plus 7 pour la dichotomie,\ncontre 100 au plus pour la recherche\nlinéaire. Pas rentable pour une seule\nrecherche.\n"
        }
      ],
      "explanation": "Règle générale : prétraiter (trier, indexer)\nn'est rentable qu'au-delà d'un certain nombre\nde recherches. Pour une recherche unique, la\nlinéaire est imbattable en simplicité."
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "boucle-correcte"
      ],
      "title": "Boucle while à bug",
      "partie": "principe",
      "statement": "```python\ndef chercher(liste, cible):\n    i = 0\n    while liste[i] != cible:\n        i += 1\n    return i\n```\nQuel problème pose ce code ?",
      "options": [
        {
          "text": "Aucun, ce code est correct pour toutes les entrées",
          "correct": false,
          "feedback": "Erreur : il y a un bug majeur lié au cas\nd'absence de la cible.\n"
        },
        {
          "text": "La fonction ne renvoie jamais rien, même si la cible est présente",
          "correct": false,
          "feedback": "Erreur : elle renvoie `i` quand elle trouve\nla cible.\n"
        },
        {
          "text": "Si l'élément est absent, la boucle dépasse la liste et lève `IndexError`",
          "correct": true,
          "feedback": "Bonne réponse : il manque la condition\n`i < len(liste)` dans le `while`. Sans cela,\non accède à `liste[len(liste)]` qui n'existe\npas. Solution :\n`while i < len(liste) and liste[i] != cible:`.\n"
        },
        {
          "text": "Le code modifie accidentellement le contenu de la liste",
          "correct": false,
          "feedback": "Erreur : aucune modification de la liste.\n"
        }
      ],
      "explanation": "Bug très classique. Pour les boucles `while`,\ntoujours vérifier deux choses : la condition\nd'arrêt **et** l'évolution de la variable de\nboucle (pour éviter une boucle infinie ou un\ndépassement)."
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "cout",
        "extrapolation"
      ],
      "title": "Comparer en pratique",
      "partie": "couts",
      "statement": "Sur ma machine, une recherche linéaire dans une\nliste de 1 million d'éléments prend environ 30 ms. Que\nprendra une recherche dans une liste de 1 milliard\nd'éléments (si la mémoire le permet) ?",
      "options": [
        {
          "text": "Environ 60 ms",
          "correct": false,
          "feedback": "Erreur : 1 milliard, c'est mille fois plus\nque 1 million, pas deux fois. Le temps est\ndonc multiplié par 1000, pas par 2.\n"
        },
        {
          "text": "Environ 30 secondes",
          "correct": true,
          "feedback": "Bonne réponse : O(n) → temps proportionnel.\n1 milliard / 1 million = 1000, donc 30 ms ×\n1000 = 30 secondes. Cette estimation est précieuse\npour anticiper les performances.\n"
        },
        {
          "text": "Environ 8 heures",
          "correct": false,
          "feedback": "Erreur : ce serait l'ordre de grandeur d'une\ncomplexité **quadratique** (facteur\n1000² = 10⁶, soit 30 ms × 10⁶ ≈ 8 h). La\nrecherche linéaire ne croît que d'un\nfacteur 1000.\n"
        },
        {
          "text": "Toujours environ 30 ms",
          "correct": false,
          "feedback": "Erreur : ce serait un temps **constant**.\nO(n) signifie que le temps croît\n**proportionnellement** à n.\n"
        }
      ],
      "explanation": "Compétence essentielle : savoir extrapoler le\ntemps d'exécution selon la complexité. La\nrecherche linéaire devient gênante au-delà de\nquelques millions d'éléments si elle est\nrépétée.",
      "variants": [
        {
          "statement": "Sur ma machine, une recherche linéaire dans une\nliste de 1 million d'éléments prend environ 30 ms. Que\nprendra une recherche dans une liste de 1 milliard\nd'éléments (si la mémoire le permet) ?",
          "options": [
            {
              "text": "Environ 60 ms",
              "correct": false,
              "feedback": "Erreur : 1 milliard, c'est mille fois plus\nque 1 million, pas deux fois. Le temps est\ndonc multiplié par 1000, pas par 2.\n"
            },
            {
              "text": "Environ 30 secondes",
              "correct": true,
              "feedback": "Bonne réponse : O(n) → temps proportionnel.\n1 milliard / 1 million = 1000, donc 30 ms ×\n1000 = 30 secondes. Cette estimation est précieuse\npour anticiper les performances.\n"
            },
            {
              "text": "Environ 8 heures",
              "correct": false,
              "feedback": "Erreur : ce serait l'ordre de grandeur d'une\ncomplexité **quadratique** (facteur\n1000² = 10⁶, soit 30 ms × 10⁶ ≈ 8 h). La\nrecherche linéaire ne croît que d'un\nfacteur 1000.\n"
            },
            {
              "text": "Toujours environ 30 ms",
              "correct": false,
              "feedback": "Erreur : ce serait un temps **constant**.\nO(n) signifie que le temps croît\n**proportionnellement** à n.\n"
            }
          ],
          "explanation": "Compétence essentielle : savoir extrapoler le\ntemps d'exécution selon la complexité. La\nrecherche linéaire devient gênante au-delà de\nquelques millions d'éléments si elle est\nrépétée."
        },
        {
          "statement": "Sur ma machine, une recherche linéaire dans une\nliste de 100 000 d'éléments prend environ 5 ms. Que\nprendra une recherche dans une liste de 10 millions\nd'éléments (si la mémoire le permet) ?",
          "options": [
            {
              "text": "Environ 10 ms",
              "correct": false,
              "feedback": "Erreur : 10 millions, c'est cent fois plus\nque 100 000, pas deux fois. Le temps est\ndonc multiplié par 100, pas par 2.\n"
            },
            {
              "text": "Environ 500 ms",
              "correct": true,
              "feedback": "Bonne réponse : O(n) → temps proportionnel.\n10 millions / 100 000 = 100, donc 5 ms ×\n100 = 500 ms. Cette estimation est précieuse\npour anticiper les performances.\n"
            },
            {
              "text": "Environ 50 secondes",
              "correct": false,
              "feedback": "Erreur : ce serait l'ordre de grandeur d'une\ncomplexité **quadratique** (facteur\n100² = 10⁴, soit 5 ms × 10⁴ = 50 s). La\nrecherche linéaire ne croît que d'un\nfacteur 100.\n"
            },
            {
              "text": "Toujours environ 5 ms",
              "correct": false,
              "feedback": "Erreur : ce serait un temps **constant**.\nO(n) signifie que le temps croît\n**proportionnellement** à n.\n"
            }
          ],
          "explanation": "Compétence essentielle : savoir extrapoler le\ntemps d'exécution selon la complexité. La\nrecherche linéaire devient gênante au-delà de\nquelques millions d'éléments si elle est\nrépétée."
        },
        {
          "statement": "Sur ma machine, une recherche linéaire dans une\nliste de 1 million d'éléments prend environ 2 ms. Que\nprendra une recherche dans une liste de 10 milliards\nd'éléments (si la mémoire le permet) ?",
          "options": [
            {
              "text": "Environ 4 ms",
              "correct": false,
              "feedback": "Erreur : 10 milliards, c'est dix mille fois plus\nque 1 million, pas deux fois. Le temps est\ndonc multiplié par 10 000, pas par 2.\n"
            },
            {
              "text": "Environ 20 secondes",
              "correct": true,
              "feedback": "Bonne réponse : O(n) → temps proportionnel.\n10 milliards / 1 million = 10 000, donc 2 ms ×\n10 000 = 20 secondes. Cette estimation est précieuse\npour anticiper les performances.\n"
            },
            {
              "text": "Environ 56 heures",
              "correct": false,
              "feedback": "Erreur : ce serait l'ordre de grandeur d'une\ncomplexité **quadratique** (facteur\n10 000² = 10⁸, soit 2 ms × 10⁸ ≈ 56 h). La\nrecherche linéaire ne croît que d'un\nfacteur 10 000.\n"
            },
            {
              "text": "Toujours environ 2 ms",
              "correct": false,
              "feedback": "Erreur : ce serait un temps **constant**.\nO(n) signifie que le temps croît\n**proportionnellement** à n.\n"
            }
          ],
          "explanation": "Compétence essentielle : savoir extrapoler le\ntemps d'exécution selon la complexité. La\nrecherche linéaire devient gênante au-delà de\nquelques millions d'éléments si elle est\nrépétée."
        }
      ]
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Synthèse",
      "partie": "couts",
      "statement": "Parmi les affirmations suivantes sur la recherche\nlinéaire, laquelle est **fausse** ?",
      "options": [
        {
          "text": "En Python, l'opérateur `in` sur une liste fait une recherche linéaire",
          "correct": false,
          "feedback": "Vrai : `in` sur une liste est en O(n) ; sur\nles clés d'un dictionnaire, il est en O(1)\nen moyenne.\n"
        },
        {
          "text": "Elle fonctionne sur des listes triées comme non triées",
          "correct": false,
          "feedback": "Vrai : c'est l'avantage majeur sur la\ndichotomique.\n"
        },
        {
          "text": "Sa complexité au pire est O(n)",
          "correct": false,
          "feedback": "Vrai : parcours complet de la liste.\n"
        },
        {
          "text": "Elle est toujours plus rapide que la dichotomique",
          "correct": true,
          "feedback": "Faux (donc bonne réponse) : sur une liste\ntriée et de grande taille, la dichotomique\n(O(log n)) est exponentiellement plus\nrapide. La linéaire reste compétitive sur\ndes petites listes ou pour une recherche\nunique.\n"
        }
      ],
      "explanation": "Maxime : « Pas d'algorithme universellement\nmeilleur. » Le choix dépend des hypothèses\n(liste triée ?), du nombre de recherches, et\nde la taille des données."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "comptage-occurrences"
      ],
      "title": "Compter les occurrences",
      "partie": "variantes",
      "statement": "On veut **compter** combien de fois la cible\napparaît dans la liste (et non simplement la\ntrouver). Quelle adaptation de la recherche\nlinéaire faut-il faire ?",
      "options": [
        {
          "text": "```python\ndef compter(liste, cible):\n    for x in liste:\n        if x == cible:\n            return 1\n    return 0\n```\n",
          "correct": false,
          "feedback": "Erreur classique : on s'arrête à la première\noccurrence et on renvoie toujours 0 ou 1, ce\nqui revient à un test d'appartenance, pas à\nun comptage.\n"
        },
        {
          "text": "```python\ndef compter(liste, cible):\n    if cible in liste:\n        return len(liste)\n    else:\n        return 0\n```\n",
          "correct": false,
          "feedback": "Erreur grossière : si la cible apparaît au\nmoins une fois, on renvoie la longueur\ntotale de la liste (donc tous les éléments\nsont comptés, même ceux qui ne sont pas la\ncible).\n"
        },
        {
          "text": "```python\ndef compter(liste, cible):\n    n = 0\n    for x in liste:\n        if x == cible:\n            n += 1\n    return n\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : on parcourt **toute** la\nliste sans arrêt précoce et on incrémente un\ncompteur à chaque correspondance. Le coût\nest linéaire.\n"
        },
        {
          "text": "```python\ndef compter(liste, cible):\n    for x in liste:\n        n = 0\n        if x == cible:\n            n += 1\n    return n\n```\n",
          "correct": false,
          "feedback": "Erreur : `n = 0` est placé dans la boucle,\ndonc le compteur repart de zéro à chaque\ntour. La fonction renvoie 1 si le dernier\nélément est la cible et 0 sinon, et\nprovoque une erreur sur une liste vide.\n"
        }
      ],
      "explanation": "Différence essentielle avec la recherche d'une\noccurrence : on ne peut pas s'arrêter au premier\nsuccès, donc le pire cas et le meilleur cas sont\nidentiques, avec n comparaisons."
    },
    {
      "id": "q27",
      "difficulty": 3,
      "skills": [
        "recherche-predicat"
      ],
      "title": "Recherche par condition",
      "partie": "variantes",
      "statement": "On cherche le **premier** élément d'une liste de\nnombres qui soit **strictement supérieur** à\n`100` (et son indice). Quel code applique\ncorrectement la recherche linéaire à ce cas ?",
      "options": [
        {
          "text": "```python\ndef premier_grand(liste):\n    triee = sorted(liste)\n    return triee[len(triee) - 1]\n```\n",
          "correct": false,
          "feedback": "Erreur double : on trie une copie de la\nliste, ce qui coûte un tri complet, puis on\nrenvoie le maximum, qui n'est pas forcément\nle **premier** élément supérieur à 100 dans\nl'ordre original. L'indice n'est pas non\nplus renvoyé.\n"
        },
        {
          "text": "```python\ndef premier_grand(liste):\n    for i in range(len(liste)):\n        if liste[i] > 100:\n            return True\n    return False\n```\n",
          "correct": false,
          "feedback": "Erreur de spécification : ce code répond à\nla question « existe-t-il un élément\nsupérieur à 100 ? » par un booléen, alors\nqu'on demande cet élément et son indice. Le\nparcours est le bon, mais la valeur\nrenvoyée ne l'est pas.\n"
        },
        {
          "text": "```python\ndef premier_grand(liste):\n    for x in liste:\n        if x > 100:\n            return x\n        return None\n```\n",
          "correct": false,
          "feedback": "Erreur d'indentation : `return None` est\ndans la boucle, donc la fonction sort dès le\npremier élément (qui est rarement supérieur\nà 100). De plus, on ne renvoie pas l'indice.\n"
        },
        {
          "text": "```python\ndef premier_grand(liste):\n    for i in range(len(liste)):\n        if liste[i] > 100:\n            return (i, liste[i])\n    return None\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : la recherche linéaire se\ngénéralise à n'importe quelle condition. On\nparcourt les indices, on teste la condition\nsur `liste[i]`, on renvoie le couple (indice,\nvaleur) dès le premier succès, et `None` si\naucun élément ne convient.\n"
        }
      ],
      "explanation": "Schéma général de la recherche linéaire par\ncondition : le test d'égalité `liste[i] == cible`\nest remplacé par n'importe quelle condition sur\n`liste[i]` ; le reste du parcours (arrêt au\npremier succès, valeur renvoyée en cas\nd'absence) est inchangé."
    },
    {
      "id": "q28",
      "difficulty": 3,
      "skills": [
        "tableau-2d"
      ],
      "title": "Recherche dans un tableau à deux dimensions",
      "partie": "variantes",
      "statement": "On dispose d'une matrice carrée représentée par\nune liste de listes (par exemple\n`m = [[3, 7, 1], [8, 2, 5], [4, 9, 6]]`). On\ncherche le couple `(ligne, colonne)` de la\npremière occurrence de la valeur `5`. Quel code\nest correct ?",
      "options": [
        {
          "text": "```python\ndef chercher(m, cible):\n    for i in range(len(m)):\n        for j in range(len(m[i])):\n            if m[i][j] == cible:\n                return (i, j)\n        return None\n```\n",
          "correct": false,
          "feedback": "Erreur : `return None` est placé dans la\nboucle sur les lignes, donc la fonction\nabandonne après avoir examiné la première\nligne. Avec la matrice donnée, `5` est en\nligne 1 et l'appel renvoie `None`.\n"
        },
        {
          "text": "```python\ndef chercher(m, cible):\n    for ligne in m:\n        for x in ligne:\n            if x == cible:\n                return x\n    return None\n```\n",
          "correct": false,
          "feedback": "Erreur : on renvoie la **valeur** trouvée\n(ce qui est trivial puisqu'on connaît\ndéjà la cible), pas le couple de\ncoordonnées demandé. Il faut parcourir\nles indices `i` et `j` avec `range` pour\npouvoir renvoyer le couple `(i, j)`.\n"
        },
        {
          "text": "```python\ndef chercher(m, cible):\n    for i in range(len(m)):\n        if m[i] == cible:\n            return i\n    return None\n```\n",
          "correct": false,
          "feedback": "Erreur : on compare une **ligne entière** à\nla cible (un nombre), ce qui sera toujours\nfaux. On confond élément de la matrice et\nligne.\n"
        },
        {
          "text": "```python\ndef chercher(m, cible):\n    for i in range(len(m)):\n        for j in range(len(m[i])):\n            if m[i][j] == cible:\n                return (i, j)\n    return None\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : double boucle imbriquée pour\nparcourir lignes et colonnes, retour\nprécoce dès la cible trouvée. Pour la\nmatrice donnée, on renvoie `(1, 2)`.\nComplexité O(L × C) où L est le nombre de\nlignes et C le nombre de colonnes.\n"
        }
      ],
      "explanation": "Ce code fonctionne aussi pour une matrice non\ncarrée, puisque `len(m[i])` est la longueur de\nla ligne courante. Le `return (i, j)` sort des\ndeux boucles d'un coup : c'est l'intérêt\nd'écrire cette recherche dans une fonction."
    },
    {
      "id": "q29",
      "difficulty": 2,
      "skills": [
        "cout",
        "comparaisons",
        "calcul"
      ],
      "title": "Le nombre de comparaisons pour un élément présent",
      "partie": "couts",
      "statement": "Une recherche linéaire parcourt une liste de $10$ éléments depuis\nle début et s'arrête dès qu'elle trouve la valeur cherchée. Cette\nvaleur se trouve en position $7$, en comptant à partir de $1$.\nCombien de comparaisons sont effectuées ?",
      "options": [
        {
          "text": "$7$",
          "correct": true,
          "feedback": "Bonne réponse : chaque élément examiné coûte une comparaison, du\npremier jusqu'à celui qui est trouvé, soit $7$.\n"
        },
        {
          "text": "$10$",
          "correct": false,
          "feedback": "Erreur : la recherche s'arrête dès que la valeur est trouvée,\nsans examiner la fin de la liste.\n"
        },
        {
          "text": "$6$",
          "correct": false,
          "feedback": "Erreur : la comparaison qui trouve l'élément compte aussi.\n"
        },
        {
          "text": "$8$",
          "correct": false,
          "feedback": "Erreur : une fois la valeur trouvée, aucune comparaison\nsupplémentaire n'a lieu.\n"
        }
      ],
      "explanation": "Le coût d'une recherche linéaire dépend de la position de la\nvaleur : au mieux $1$, au pire $n$.",
      "variants": [
        {
          "statement": "Une recherche linéaire parcourt une liste de $10$ éléments depuis\nle début et s'arrête dès qu'elle trouve la valeur cherchée. Cette\nvaleur se trouve en position $7$, en comptant à partir de $1$.\nCombien de comparaisons sont effectuées ?",
          "options": [
            {
              "text": "$7$",
              "correct": true,
              "feedback": "Bonne réponse : chaque élément examiné coûte une comparaison, du\npremier jusqu'à celui qui est trouvé, soit $7$.\n"
            },
            {
              "text": "$10$",
              "correct": false,
              "feedback": "Erreur : la recherche s'arrête dès que la valeur est trouvée,\nsans examiner la fin de la liste.\n"
            },
            {
              "text": "$6$",
              "correct": false,
              "feedback": "Erreur : la comparaison qui trouve l'élément compte aussi.\n"
            },
            {
              "text": "$8$",
              "correct": false,
              "feedback": "Erreur : une fois la valeur trouvée, aucune comparaison\nsupplémentaire n'a lieu.\n"
            }
          ],
          "explanation": "Le coût d'une recherche linéaire dépend de la position de la\nvaleur : au mieux $1$, au pire $n$."
        },
        {
          "statement": "Une recherche linéaire parcourt une liste de $100$ éléments depuis\nle début et s'arrête dès qu'elle trouve la valeur cherchée. Cette\nvaleur se trouve en position $42$, en comptant à partir de $1$.\nCombien de comparaisons sont effectuées ?",
          "options": [
            {
              "text": "$42$",
              "correct": true,
              "feedback": "Bonne réponse : chaque élément examiné coûte une comparaison, du\npremier jusqu'à celui qui est trouvé, soit $42$.\n"
            },
            {
              "text": "$100$",
              "correct": false,
              "feedback": "Erreur : la recherche s'arrête dès que la valeur est trouvée,\nsans examiner la fin de la liste.\n"
            },
            {
              "text": "$41$",
              "correct": false,
              "feedback": "Erreur : la comparaison qui trouve l'élément compte aussi.\n"
            },
            {
              "text": "$43$",
              "correct": false,
              "feedback": "Erreur : une fois la valeur trouvée, aucune comparaison\nsupplémentaire n'a lieu.\n"
            }
          ],
          "explanation": "Le coût d'une recherche linéaire dépend de la position de la\nvaleur : au mieux $1$, au pire $n$."
        },
        {
          "statement": "Une recherche linéaire parcourt une liste de $8$ éléments depuis\nle début et s'arrête dès qu'elle trouve la valeur cherchée. Cette\nvaleur se trouve en position $1$, en comptant à partir de $1$.\nCombien de comparaisons sont effectuées ?",
          "options": [
            {
              "text": "$1$",
              "correct": true,
              "feedback": "Bonne réponse : chaque élément examiné coûte une comparaison, du\npremier jusqu'à celui qui est trouvé, soit $1$.\n"
            },
            {
              "text": "$8$",
              "correct": false,
              "feedback": "Erreur : la recherche s'arrête dès que la valeur est trouvée,\nsans examiner la fin de la liste.\n"
            },
            {
              "text": "$0$",
              "correct": false,
              "feedback": "Erreur : la comparaison qui trouve l'élément compte aussi.\n"
            },
            {
              "text": "$2$",
              "correct": false,
              "feedback": "Erreur : une fois la valeur trouvée, aucune comparaison\nsupplémentaire n'a lieu.\n"
            }
          ],
          "explanation": "Le coût d'une recherche linéaire dépend de la position de la\nvaleur : au mieux $1$, au pire $n$."
        }
      ]
    },
    {
      "id": "q30",
      "difficulty": 2,
      "skills": [
        "cout",
        "pire-cas",
        "calcul"
      ],
      "title": "Le nombre de comparaisons pour un élément absent",
      "partie": "couts",
      "statement": "Une recherche linéaire cherche une valeur absente d'une liste de\n$1\\,000$ éléments. Combien de comparaisons effectue-t-elle ?",
      "options": [
        {
          "text": "$1\\,000$",
          "correct": true,
          "feedback": "Bonne réponse : pour conclure à l'absence, il faut avoir\ncomparé la valeur à chaque élément, sans exception.\n"
        },
        {
          "text": "$500$",
          "correct": false,
          "feedback": "Erreur : la moitié serait un coût moyen pour une valeur\nprésente ; une valeur absente oblige à tout parcourir.\n"
        },
        {
          "text": "$1$",
          "correct": false,
          "feedback": "Erreur : une seule comparaison ne permet pas d'affirmer que la\nvaleur n'est nulle part.\n"
        },
        {
          "text": "$2\\,000$",
          "correct": false,
          "feedback": "Erreur : chaque élément n'est comparé qu'une fois.\n"
        }
      ],
      "explanation": "L'absence est le pire cas de la recherche linéaire : $n$\ncomparaisons pour $n$ éléments.",
      "variants": [
        {
          "statement": "Une recherche linéaire cherche une valeur absente d'une liste de\n$1\\,000$ éléments. Combien de comparaisons effectue-t-elle ?",
          "options": [
            {
              "text": "$1\\,000$",
              "correct": true,
              "feedback": "Bonne réponse : pour conclure à l'absence, il faut avoir\ncomparé la valeur à chaque élément, sans exception.\n"
            },
            {
              "text": "$500$",
              "correct": false,
              "feedback": "Erreur : la moitié serait un coût moyen pour une valeur\nprésente ; une valeur absente oblige à tout parcourir.\n"
            },
            {
              "text": "$1$",
              "correct": false,
              "feedback": "Erreur : une seule comparaison ne permet pas d'affirmer que la\nvaleur n'est nulle part.\n"
            },
            {
              "text": "$2\\,000$",
              "correct": false,
              "feedback": "Erreur : chaque élément n'est comparé qu'une fois.\n"
            }
          ],
          "explanation": "L'absence est le pire cas de la recherche linéaire : $n$\ncomparaisons pour $n$ éléments."
        },
        {
          "statement": "Une recherche linéaire cherche une valeur absente d'une liste de\n$50$ éléments. Combien de comparaisons effectue-t-elle ?",
          "options": [
            {
              "text": "$50$",
              "correct": true,
              "feedback": "Bonne réponse : pour conclure à l'absence, il faut avoir\ncomparé la valeur à chaque élément, sans exception.\n"
            },
            {
              "text": "$25$",
              "correct": false,
              "feedback": "Erreur : la moitié serait un coût moyen pour une valeur\nprésente ; une valeur absente oblige à tout parcourir.\n"
            },
            {
              "text": "$1$",
              "correct": false,
              "feedback": "Erreur : une seule comparaison ne permet pas d'affirmer que la\nvaleur n'est nulle part.\n"
            },
            {
              "text": "$100$",
              "correct": false,
              "feedback": "Erreur : chaque élément n'est comparé qu'une fois.\n"
            }
          ],
          "explanation": "L'absence est le pire cas de la recherche linéaire : $n$\ncomparaisons pour $n$ éléments."
        },
        {
          "statement": "Une recherche linéaire cherche une valeur absente d'une liste de\n$1\\,000\\,000$ éléments. Combien de comparaisons effectue-t-elle ?",
          "options": [
            {
              "text": "$1\\,000\\,000$",
              "correct": true,
              "feedback": "Bonne réponse : pour conclure à l'absence, il faut avoir\ncomparé la valeur à chaque élément, sans exception.\n"
            },
            {
              "text": "$500\\,000$",
              "correct": false,
              "feedback": "Erreur : la moitié serait un coût moyen pour une valeur\nprésente ; une valeur absente oblige à tout parcourir.\n"
            },
            {
              "text": "$1$",
              "correct": false,
              "feedback": "Erreur : une seule comparaison ne permet pas d'affirmer que la\nvaleur n'est nulle part.\n"
            },
            {
              "text": "$2\\,000\\,000$",
              "correct": false,
              "feedback": "Erreur : chaque élément n'est comparé qu'une fois.\n"
            }
          ],
          "explanation": "L'absence est le pire cas de la recherche linéaire : $n$\ncomparaisons pour $n$ éléments."
        }
      ]
    },
    {
      "id": "q31",
      "difficulty": 3,
      "skills": [
        "cout",
        "cas-moyen"
      ],
      "title": "Le coût moyen d'une recherche",
      "partie": "couts",
      "statement": "Une liste contient $1\\,000$ éléments et la valeur cherchée s'y trouve,\nà une position aussi probable qu'une autre. Combien de comparaisons\nla recherche linéaire effectue-t-elle en moyenne ?",
      "options": [
        {
          "text": "Environ $500$",
          "correct": true,
          "feedback": "Bonne réponse : la position va de $1$ à $1\\,000$ avec la même\nprobabilité ; la moyenne des coûts est à peu près la moitié de\n$1\\,000$.\n"
        },
        {
          "text": "$1\\,000$",
          "correct": false,
          "feedback": "Erreur : $1\\,000$ est le pire cas, quand la valeur est en dernière\nposition ou absente.\n"
        },
        {
          "text": "$1$",
          "correct": false,
          "feedback": "Erreur : $1$ est le meilleur cas, quand la valeur est en tête.\n"
        },
        {
          "text": "Environ $\\log_2 1\\,000$",
          "correct": false,
          "feedback": "Erreur : le coût logarithmique est celui de la recherche\ndichotomique, sur une liste triée.\n"
        }
      ],
      "explanation": "Meilleur cas, pire cas et cas moyen décrivent trois situations. Pour\nla recherche linéaire, le cas moyen reste proportionnel à $n$.",
      "variants": [
        {
          "statement": "Une liste contient $1\\,000$ éléments et la valeur cherchée s'y trouve,\nà une position aussi probable qu'une autre. Combien de comparaisons\nla recherche linéaire effectue-t-elle en moyenne ?",
          "options": [
            {
              "text": "Environ $500$",
              "correct": true,
              "feedback": "Bonne réponse : la position va de $1$ à $1\\,000$ avec la même\nprobabilité ; la moyenne des coûts est à peu près la moitié de\n$1\\,000$.\n"
            },
            {
              "text": "$1\\,000$",
              "correct": false,
              "feedback": "Erreur : $1\\,000$ est le pire cas, quand la valeur est en dernière\nposition ou absente.\n"
            },
            {
              "text": "$1$",
              "correct": false,
              "feedback": "Erreur : $1$ est le meilleur cas, quand la valeur est en tête.\n"
            },
            {
              "text": "Environ $\\log_2 1\\,000$",
              "correct": false,
              "feedback": "Erreur : le coût logarithmique est celui de la recherche\ndichotomique, sur une liste triée.\n"
            }
          ],
          "explanation": "Meilleur cas, pire cas et cas moyen décrivent trois situations. Pour\nla recherche linéaire, le cas moyen reste proportionnel à $n$."
        },
        {
          "statement": "Une liste contient $200$ éléments et la valeur cherchée s'y trouve,\nà une position aussi probable qu'une autre. Combien de comparaisons\nla recherche linéaire effectue-t-elle en moyenne ?",
          "options": [
            {
              "text": "Environ $100$",
              "correct": true,
              "feedback": "Bonne réponse : la position va de $1$ à $200$ avec la même\nprobabilité ; la moyenne des coûts est à peu près la moitié de\n$200$.\n"
            },
            {
              "text": "$200$",
              "correct": false,
              "feedback": "Erreur : $200$ est le pire cas, quand la valeur est en dernière\nposition ou absente.\n"
            },
            {
              "text": "$1$",
              "correct": false,
              "feedback": "Erreur : $1$ est le meilleur cas, quand la valeur est en tête.\n"
            },
            {
              "text": "Environ $\\log_2 200$",
              "correct": false,
              "feedback": "Erreur : le coût logarithmique est celui de la recherche\ndichotomique, sur une liste triée.\n"
            }
          ],
          "explanation": "Meilleur cas, pire cas et cas moyen décrivent trois situations. Pour\nla recherche linéaire, le cas moyen reste proportionnel à $n$."
        },
        {
          "statement": "Une liste contient $10\\,000$ éléments et la valeur cherchée s'y trouve,\nà une position aussi probable qu'une autre. Combien de comparaisons\nla recherche linéaire effectue-t-elle en moyenne ?",
          "options": [
            {
              "text": "Environ $5\\,000$",
              "correct": true,
              "feedback": "Bonne réponse : la position va de $1$ à $10\\,000$ avec la même\nprobabilité ; la moyenne des coûts est à peu près la moitié de\n$10\\,000$.\n"
            },
            {
              "text": "$10\\,000$",
              "correct": false,
              "feedback": "Erreur : $10\\,000$ est le pire cas, quand la valeur est en dernière\nposition ou absente.\n"
            },
            {
              "text": "$1$",
              "correct": false,
              "feedback": "Erreur : $1$ est le meilleur cas, quand la valeur est en tête.\n"
            },
            {
              "text": "Environ $\\log_2 10\\,000$",
              "correct": false,
              "feedback": "Erreur : le coût logarithmique est celui de la recherche\ndichotomique, sur une liste triée.\n"
            }
          ],
          "explanation": "Meilleur cas, pire cas et cas moyen décrivent trois situations. Pour\nla recherche linéaire, le cas moyen reste proportionnel à $n$."
        }
      ]
    },
    {
      "id": "q32",
      "difficulty": 3,
      "skills": [
        "cout",
        "lineaire",
        "temps"
      ],
      "title": "Une liste deux fois plus longue",
      "partie": "couts",
      "statement": "Sur une liste de un million d'éléments, une recherche linéaire dans le pire\ncas prend $2$ s. Combien de temps prend-elle, toujours dans le\npire cas, sur une liste de deux millions d'éléments ?",
      "options": [
        {
          "text": "Environ $4$ s",
          "correct": true,
          "feedback": "Bonne réponse : le coût est proportionnel au nombre d'éléments.\nDeux fois plus d'éléments, deux fois plus de comparaisons, deux\nfois plus de temps.\n"
        },
        {
          "text": "Toujours $2$ s",
          "correct": false,
          "feedback": "Erreur : chaque élément supplémentaire est une comparaison de\nplus ; le temps augmente.\n"
        },
        {
          "text": "Environ $2 + 1$ s",
          "correct": false,
          "feedback": "Erreur : le temps ne croît pas d'une constante mais\nproportionnellement à la taille.\n"
        },
        {
          "text": "Environ $2 \\times 2$ s",
          "correct": false,
          "feedback": "Erreur : le coût n'est pas quadratique ; doubler la taille\ndouble le temps, il ne le met pas au carré.\n"
        }
      ],
      "explanation": "Dire qu'un coût est linéaire, c'est dire que le temps suit la\ntaille des données : multiplier l'une par deux multiplie l'autre\npar deux.",
      "variants": [
        {
          "statement": "Sur une liste de un million d'éléments, une recherche linéaire dans le pire\ncas prend $2$ s. Combien de temps prend-elle, toujours dans le\npire cas, sur une liste de deux millions d'éléments ?",
          "options": [
            {
              "text": "Environ $4$ s",
              "correct": true,
              "feedback": "Bonne réponse : le coût est proportionnel au nombre d'éléments.\nDeux fois plus d'éléments, deux fois plus de comparaisons, deux\nfois plus de temps.\n"
            },
            {
              "text": "Toujours $2$ s",
              "correct": false,
              "feedback": "Erreur : chaque élément supplémentaire est une comparaison de\nplus ; le temps augmente.\n"
            },
            {
              "text": "Environ $2 + 1$ s",
              "correct": false,
              "feedback": "Erreur : le temps ne croît pas d'une constante mais\nproportionnellement à la taille.\n"
            },
            {
              "text": "Environ $2 \\times 2$ s",
              "correct": false,
              "feedback": "Erreur : le coût n'est pas quadratique ; doubler la taille\ndouble le temps, il ne le met pas au carré.\n"
            }
          ],
          "explanation": "Dire qu'un coût est linéaire, c'est dire que le temps suit la\ntaille des données : multiplier l'une par deux multiplie l'autre\npar deux."
        },
        {
          "statement": "Sur une liste de cent mille d'éléments, une recherche linéaire dans le pire\ncas prend $0{,}5$ s. Combien de temps prend-elle, toujours dans le\npire cas, sur une liste de deux cent mille d'éléments ?",
          "options": [
            {
              "text": "Environ $1$ s",
              "correct": true,
              "feedback": "Bonne réponse : le coût est proportionnel au nombre d'éléments.\nDeux fois plus d'éléments, deux fois plus de comparaisons, deux\nfois plus de temps.\n"
            },
            {
              "text": "Toujours $0{,}5$ s",
              "correct": false,
              "feedback": "Erreur : chaque élément supplémentaire est une comparaison de\nplus ; le temps augmente.\n"
            },
            {
              "text": "Environ $0{,}5 + 1$ s",
              "correct": false,
              "feedback": "Erreur : le temps ne croît pas d'une constante mais\nproportionnellement à la taille.\n"
            },
            {
              "text": "Environ $0{,}5 \\times 0{,}5$ s",
              "correct": false,
              "feedback": "Erreur : le coût n'est pas quadratique ; doubler la taille\ndouble le temps, il ne le met pas au carré.\n"
            }
          ],
          "explanation": "Dire qu'un coût est linéaire, c'est dire que le temps suit la\ntaille des données : multiplier l'une par deux multiplie l'autre\npar deux."
        },
        {
          "statement": "Sur une liste de dix millions d'éléments, une recherche linéaire dans le pire\ncas prend $3$ s. Combien de temps prend-elle, toujours dans le\npire cas, sur une liste de vingt millions d'éléments ?",
          "options": [
            {
              "text": "Environ $6$ s",
              "correct": true,
              "feedback": "Bonne réponse : le coût est proportionnel au nombre d'éléments.\nDeux fois plus d'éléments, deux fois plus de comparaisons, deux\nfois plus de temps.\n"
            },
            {
              "text": "Toujours $3$ s",
              "correct": false,
              "feedback": "Erreur : chaque élément supplémentaire est une comparaison de\nplus ; le temps augmente.\n"
            },
            {
              "text": "Environ $3 + 1$ s",
              "correct": false,
              "feedback": "Erreur : le temps ne croît pas d'une constante mais\nproportionnellement à la taille.\n"
            },
            {
              "text": "Environ $3 \\times 3$ s",
              "correct": false,
              "feedback": "Erreur : le coût n'est pas quadratique ; doubler la taille\ndouble le temps, il ne le met pas au carré.\n"
            }
          ],
          "explanation": "Dire qu'un coût est linéaire, c'est dire que le temps suit la\ntaille des données : multiplier l'une par deux multiplie l'autre\npar deux."
        }
      ]
    },
    {
      "id": "q33",
      "difficulty": 2,
      "skills": [
        "variante",
        "derniere-occurrence",
        "trace"
      ],
      "title": "La dernière position d'une valeur",
      "partie": "variantes",
      "statement": "Qu'affiche ce programme ?\n```python\ndef derniere(t, x):\n    pos = -1\n    for i in range(len(t)):\n        if t[i] == x:\n            pos = i\n    return pos\n\nprint(derniere([4, 7, 4, 9, 4, 1], 4))\n```",
      "options": [
        {
          "text": "`4`",
          "correct": true,
          "feedback": "Bonne réponse : sans arrêt anticipé, `pos` est réécrit à chaque\noccurrence et garde l'indice de la dernière, 4.\n"
        },
        {
          "text": "`0`",
          "correct": false,
          "feedback": "Erreur : 0 est l'indice de la première occurrence ; la\nboucle continue après.\n"
        },
        {
          "text": "`3`",
          "correct": false,
          "feedback": "Erreur : 3 est le nombre d'occurrences, mais `pos` mémorise\nun indice.\n"
        },
        {
          "text": "`-1`",
          "correct": false,
          "feedback": "Erreur : `-1` ne reste que si la valeur est absente.\n"
        }
      ],
      "explanation": "Une recherche linéaire sans `return` dans la boucle parcourt toute\nla liste : elle trouve la dernière occurrence au lieu de la\npremière.",
      "variants": [
        {
          "statement": "Qu'affiche ce programme ?\n```python\ndef derniere(t, x):\n    pos = -1\n    for i in range(len(t)):\n        if t[i] == x:\n            pos = i\n    return pos\n\nprint(derniere([4, 7, 4, 9, 4, 1], 4))\n```",
          "options": [
            {
              "text": "`4`",
              "correct": true,
              "feedback": "Bonne réponse : sans arrêt anticipé, `pos` est réécrit à chaque\noccurrence et garde l'indice de la dernière, 4.\n"
            },
            {
              "text": "`0`",
              "correct": false,
              "feedback": "Erreur : 0 est l'indice de la première occurrence ; la\nboucle continue après.\n"
            },
            {
              "text": "`3`",
              "correct": false,
              "feedback": "Erreur : 3 est le nombre d'occurrences, mais `pos` mémorise\nun indice.\n"
            },
            {
              "text": "`-1`",
              "correct": false,
              "feedback": "Erreur : `-1` ne reste que si la valeur est absente.\n"
            }
          ],
          "explanation": "Une recherche linéaire sans `return` dans la boucle parcourt toute\nla liste : elle trouve la dernière occurrence au lieu de la\npremière."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\ndef derniere(t, x):\n    pos = -1\n    for i in range(len(t)):\n        if t[i] == x:\n            pos = i\n    return pos\n\nprint(derniere([5, 2, 8, 5], 5))\n```",
          "options": [
            {
              "text": "`3`",
              "correct": true,
              "feedback": "Bonne réponse : sans arrêt anticipé, `pos` est réécrit à chaque\noccurrence et garde l'indice de la dernière, 3.\n"
            },
            {
              "text": "`0`",
              "correct": false,
              "feedback": "Erreur : 0 est l'indice de la première occurrence ; la\nboucle continue après.\n"
            },
            {
              "text": "`2`",
              "correct": false,
              "feedback": "Erreur : 2 est le nombre d'occurrences, mais `pos` mémorise\nun indice.\n"
            },
            {
              "text": "`-1`",
              "correct": false,
              "feedback": "Erreur : `-1` ne reste que si la valeur est absente.\n"
            }
          ],
          "explanation": "Une recherche linéaire sans `return` dans la boucle parcourt toute\nla liste : elle trouve la dernière occurrence au lieu de la\npremière."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\ndef derniere(t, x):\n    pos = -1\n    for i in range(len(t)):\n        if t[i] == x:\n            pos = i\n    return pos\n\nprint(derniere([1, 9, 3, 9, 6, 9, 2], 9))\n```",
          "options": [
            {
              "text": "`5`",
              "correct": true,
              "feedback": "Bonne réponse : sans arrêt anticipé, `pos` est réécrit à chaque\noccurrence et garde l'indice de la dernière, 5.\n"
            },
            {
              "text": "`1`",
              "correct": false,
              "feedback": "Erreur : 1 est l'indice de la première occurrence ; la\nboucle continue après.\n"
            },
            {
              "text": "`3`",
              "correct": false,
              "feedback": "Erreur : 3 est le nombre d'occurrences, mais `pos` mémorise\nun indice.\n"
            },
            {
              "text": "`-1`",
              "correct": false,
              "feedback": "Erreur : `-1` ne reste que si la valeur est absente.\n"
            }
          ],
          "explanation": "Une recherche linéaire sans `return` dans la boucle parcourt toute\nla liste : elle trouve la dernière occurrence au lieu de la\npremière."
        }
      ]
    },
    {
      "id": "q34",
      "difficulty": 3,
      "skills": [
        "variante",
        "maximum",
        "indice",
        "trace"
      ],
      "title": "La position du maximum",
      "partie": "variantes",
      "statement": "Qu'affiche ce programme ?\n```python\ndef position_max(t):\n    im = 0\n    for i in range(1, len(t)):\n        if t[i] > t[im]:\n            im = i\n    return im\n\nprint(position_max([3, 8, 2, 8, 5]))\n```",
      "options": [
        {
          "text": "`1`",
          "correct": true,
          "feedback": "Bonne réponse : `im` ne change qu'en cas de valeur strictement\nplus grande ; en cas d'égalité, la première occurrence du\nmaximum est conservée.\n"
        },
        {
          "text": "`8`",
          "correct": false,
          "feedback": "Erreur : 8 est la valeur maximale ; la fonction renvoie son\nindice.\n"
        },
        {
          "text": "`3`",
          "correct": false,
          "feedback": "Erreur : avec `>` strict, la dernière occurrence du maximum ne\nremplace pas la première.\n"
        },
        {
          "text": "`0`",
          "correct": false,
          "feedback": "Erreur : `im` part de 0 mais est remplacé dès qu'une valeur plus\ngrande apparaît.\n"
        }
      ],
      "explanation": "Chercher la position du maximum est une recherche linéaire avec\nmémoire : on retient l'indice du meilleur candidat rencontré.",
      "variants": [
        {
          "statement": "Qu'affiche ce programme ?\n```python\ndef position_max(t):\n    im = 0\n    for i in range(1, len(t)):\n        if t[i] > t[im]:\n            im = i\n    return im\n\nprint(position_max([3, 8, 2, 8, 5]))\n```",
          "options": [
            {
              "text": "`1`",
              "correct": true,
              "feedback": "Bonne réponse : `im` ne change qu'en cas de valeur strictement\nplus grande ; en cas d'égalité, la première occurrence du\nmaximum est conservée.\n"
            },
            {
              "text": "`8`",
              "correct": false,
              "feedback": "Erreur : 8 est la valeur maximale ; la fonction renvoie son\nindice.\n"
            },
            {
              "text": "`3`",
              "correct": false,
              "feedback": "Erreur : avec `>` strict, la dernière occurrence du maximum ne\nremplace pas la première.\n"
            },
            {
              "text": "`0`",
              "correct": false,
              "feedback": "Erreur : `im` part de 0 mais est remplacé dès qu'une valeur plus\ngrande apparaît.\n"
            }
          ],
          "explanation": "Chercher la position du maximum est une recherche linéaire avec\nmémoire : on retient l'indice du meilleur candidat rencontré."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\ndef position_max(t):\n    im = 0\n    for i in range(1, len(t)):\n        if t[i] > t[im]:\n            im = i\n    return im\n\nprint(position_max([10, 4, 12, 12]))\n```",
          "options": [
            {
              "text": "`2`",
              "correct": true,
              "feedback": "Bonne réponse : `im` ne change qu'en cas de valeur strictement\nplus grande ; en cas d'égalité, la première occurrence du\nmaximum est conservée.\n"
            },
            {
              "text": "`12`",
              "correct": false,
              "feedback": "Erreur : 12 est la valeur maximale ; la fonction renvoie son\nindice.\n"
            },
            {
              "text": "`3`",
              "correct": false,
              "feedback": "Erreur : avec `>` strict, la dernière occurrence du maximum ne\nremplace pas la première.\n"
            },
            {
              "text": "`0`",
              "correct": false,
              "feedback": "Erreur : `im` part de 0 mais est remplacé dès qu'une valeur plus\ngrande apparaît.\n"
            }
          ],
          "explanation": "Chercher la position du maximum est une recherche linéaire avec\nmémoire : on retient l'indice du meilleur candidat rencontré."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\ndef position_max(t):\n    im = 0\n    for i in range(1, len(t)):\n        if t[i] > t[im]:\n            im = i\n    return im\n\nprint(position_max([5, 5, 2, 8, 8]))\n```",
          "options": [
            {
              "text": "`3`",
              "correct": true,
              "feedback": "Bonne réponse : `im` ne change qu'en cas de valeur strictement\nplus grande ; en cas d'égalité, la première occurrence du\nmaximum est conservée.\n"
            },
            {
              "text": "`8`",
              "correct": false,
              "feedback": "Erreur : 8 est la valeur maximale ; la fonction renvoie son\nindice.\n"
            },
            {
              "text": "`4`",
              "correct": false,
              "feedback": "Erreur : avec `>` strict, la dernière occurrence du maximum ne\nremplace pas la première.\n"
            },
            {
              "text": "`0`",
              "correct": false,
              "feedback": "Erreur : `im` part de 0 mais est remplacé dès qu'une valeur plus\ngrande apparaît.\n"
            }
          ],
          "explanation": "Chercher la position du maximum est une recherche linéaire avec\nmémoire : on retient l'indice du meilleur candidat rencontré."
        }
      ]
    },
    {
      "id": "q35",
      "difficulty": 1,
      "skills": [
        "principe",
        "premiere-occurrence",
        "indice"
      ],
      "title": "L'indice renvoyé en cas de répétition",
      "partie": "principe",
      "statement": "La fonction `recherche(t, x)` renvoie l'indice du premier élément\négal à `x`, ou `-1` s'il n'y en a pas. Que renvoie\n`recherche([5, 6, 7, 6], 6)` ?",
      "options": [
        {
          "text": "`1`",
          "correct": true,
          "feedback": "Bonne réponse : la valeur 6 apparaît plusieurs fois, et la\nrecherche s'arrête à la première, d'indice 1.\n"
        },
        {
          "text": "`3`",
          "correct": false,
          "feedback": "Erreur : 3 est l'indice de la dernière occurrence ; la\nrecherche s'arrête à la première.\n"
        },
        {
          "text": "`6`",
          "correct": false,
          "feedback": "Erreur : la fonction renvoie un indice, pas la valeur cherchée.\n"
        },
        {
          "text": "`-1`",
          "correct": false,
          "feedback": "Erreur : `-1` n'est renvoyé que si la valeur est absente.\n"
        }
      ],
      "explanation": "En cas de répétition, la recherche linéaire classique renvoie la\npremière occurrence, celle qui arrête le parcours.",
      "variants": [
        {
          "statement": "La fonction `recherche(t, x)` renvoie l'indice du premier élément\négal à `x`, ou `-1` s'il n'y en a pas. Que renvoie\n`recherche([5, 6, 7, 6], 6)` ?",
          "options": [
            {
              "text": "`1`",
              "correct": true,
              "feedback": "Bonne réponse : la valeur 6 apparaît plusieurs fois, et la\nrecherche s'arrête à la première, d'indice 1.\n"
            },
            {
              "text": "`3`",
              "correct": false,
              "feedback": "Erreur : 3 est l'indice de la dernière occurrence ; la\nrecherche s'arrête à la première.\n"
            },
            {
              "text": "`6`",
              "correct": false,
              "feedback": "Erreur : la fonction renvoie un indice, pas la valeur cherchée.\n"
            },
            {
              "text": "`-1`",
              "correct": false,
              "feedback": "Erreur : `-1` n'est renvoyé que si la valeur est absente.\n"
            }
          ],
          "explanation": "En cas de répétition, la recherche linéaire classique renvoie la\npremière occurrence, celle qui arrête le parcours."
        },
        {
          "statement": "La fonction `recherche(t, x)` renvoie l'indice du premier élément\négal à `x`, ou `-1` s'il n'y en a pas. Que renvoie\n`recherche([2, 2, 9], 2)` ?",
          "options": [
            {
              "text": "`0`",
              "correct": true,
              "feedback": "Bonne réponse : la valeur 2 apparaît plusieurs fois, et la\nrecherche s'arrête à la première, d'indice 0.\n"
            },
            {
              "text": "`1`",
              "correct": false,
              "feedback": "Erreur : 1 est l'indice de la dernière occurrence ; la\nrecherche s'arrête à la première.\n"
            },
            {
              "text": "`2`",
              "correct": false,
              "feedback": "Erreur : la fonction renvoie un indice, pas la valeur cherchée.\n"
            },
            {
              "text": "`-1`",
              "correct": false,
              "feedback": "Erreur : `-1` n'est renvoyé que si la valeur est absente.\n"
            }
          ],
          "explanation": "En cas de répétition, la recherche linéaire classique renvoie la\npremière occurrence, celle qui arrête le parcours."
        },
        {
          "statement": "La fonction `recherche(t, x)` renvoie l'indice du premier élément\négal à `x`, ou `-1` s'il n'y en a pas. Que renvoie\n`recherche([8, 6, 9, 2, 9], 9)` ?",
          "options": [
            {
              "text": "`2`",
              "correct": true,
              "feedback": "Bonne réponse : la valeur 9 apparaît plusieurs fois, et la\nrecherche s'arrête à la première, d'indice 2.\n"
            },
            {
              "text": "`4`",
              "correct": false,
              "feedback": "Erreur : 4 est l'indice de la dernière occurrence ; la\nrecherche s'arrête à la première.\n"
            },
            {
              "text": "`9`",
              "correct": false,
              "feedback": "Erreur : la fonction renvoie un indice, pas la valeur cherchée.\n"
            },
            {
              "text": "`-1`",
              "correct": false,
              "feedback": "Erreur : `-1` n'est renvoyé que si la valeur est absente.\n"
            }
          ],
          "explanation": "En cas de répétition, la recherche linéaire classique renvoie la\npremière occurrence, celle qui arrête le parcours."
        }
      ]
    },
    {
      "id": "q36",
      "difficulty": 2,
      "skills": [
        "principe",
        "return",
        "structure"
      ],
      "title": "La place du return final",
      "partie": "principe",
      "statement": "Dans cette fonction, pourquoi `return -1` est-il placé après la\nboucle et non à l'intérieur ?\n```python\ndef recherche(t, x):\n    for i in range(len(t)):\n        if t[i] == x:\n            return i\n    return -1\n```",
      "options": [
        {
          "text": "Pour ne conclure à l'absence qu'après la fin du parcours",
          "correct": true,
          "feedback": "Bonne réponse : `return -1` n'est atteint que si la boucle s'est\nterminée sans trouver `x`. Placé dans la boucle, il\ninterromprait la recherche dès le premier élément différent.\n"
        },
        {
          "text": "Pour que la fonction renvoie toujours `-1` en dernier",
          "correct": false,
          "feedback": "Erreur : si `x` est trouvé, `return i` sort de la fonction avant\nd'atteindre `return -1`.\n"
        },
        {
          "text": "Parce qu'un `return` est interdit dans une boucle",
          "correct": false,
          "feedback": "Erreur : `return i` est précisément dans la boucle, et c'est\npermis.\n"
        },
        {
          "text": "Pour économiser une comparaison à chaque tour de boucle",
          "correct": false,
          "feedback": "Erreur : la position du `return` final ne change pas le nombre de\ncomparaisons.\n"
        }
      ],
      "explanation": "Un `return` dans la boucle signale le succès ; le `return` après la\nboucle signale l'échec, une fois tous les éléments examinés."
    }
  ]
}