{
  "chapter": {
    "id": "tris",
    "level": "premiere",
    "theme": "Algorithmique",
    "title": "Tris par sélection et par insertion",
    "description": "Algorithmes de tri étudiés en Première NSI conformément\nau programme officiel : tri par sélection et tri par\ninsertion. Principes, implémentations Python, invariants\nde boucle, terminaison, coût quadratique au pire,\nstabilité, comparaison.",
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition"
      ],
      "title": "Qu'est-ce qu'un tri ?",
      "statement": "Que signifie « **trier** une liste » en informatique ?",
      "options": [
        {
          "text": "Réorganiser ses éléments dans un ordre défini (par exemple croissant)",
          "correct": true,
          "feedback": "Bonne réponse : un tri produit une\n**permutation** de la liste d'origine\nrespectant un ordre (croissant, décroissant,\nalphabétique, etc.).\n"
        },
        {
          "text": "Supprimer les doublons",
          "correct": false,
          "feedback": "Erreur : trier ne supprime pas les doublons.\nPour cela, on utilise un `set` ou une étape\nséparée.\n"
        },
        {
          "text": "Convertir les éléments en chaînes",
          "correct": false,
          "feedback": "Erreur : aucun changement de type.\n"
        },
        {
          "text": "La couper en plusieurs morceaux",
          "correct": false,
          "feedback": "Erreur : c'est le découpage, pas le tri.\n"
        }
      ],
      "explanation": "Trier permet ensuite des opérations efficaces\n(recherche dichotomique, médiane, fusion).\nAu programme de Première : tri par **sélection**\net tri par **insertion**."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "tri-selection-principe"
      ],
      "title": "Tri par sélection : principe",
      "statement": "Quel est le principe du **tri par sélection** ?",
      "options": [
        {
          "text": "Couper la liste en deux et trier chaque moitié",
          "correct": false,
          "feedback": "Erreur : c'est le tri **fusion** (diviser\npour régner), au programme de Terminale.\n"
        },
        {
          "text": "Insérer chaque élément à sa place dans la partie déjà triée",
          "correct": false,
          "feedback": "Erreur : c'est le tri par **insertion**.\n"
        },
        {
          "text": "Sélectionner le plus petit élément non trié et le placer en début de la partie non triée",
          "correct": true,
          "feedback": "Bonne réponse : à chaque étape, on cherche\nle minimum du sous-tableau non trié et on\nl'échange avec le premier élément de cette\npartie.\n"
        },
        {
          "text": "Comparer chaque paire d'éléments adjacents",
          "correct": false,
          "feedback": "Erreur : ce serait l'idée du tri à bulles,\nqui n'est pas au programme.\n"
        }
      ],
      "explanation": "Sélection : « je cherche le min du non trié, je\nle mets en tête de la partie non triée, je\nrecommence sur le reste ». Simple et systématique."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "tri-insertion-principe"
      ],
      "title": "Tri par insertion : principe",
      "statement": "Quel est le principe du **tri par insertion** ?",
      "options": [
        {
          "text": "Insérer chaque nouvel élément à la bonne place dans la partie déjà triée à sa gauche",
          "correct": true,
          "feedback": "Bonne réponse : on parcourt la liste de\ngauche à droite ; pour chaque élément, on\nle décale vers la gauche tant qu'il est plus\npetit que son voisin de gauche.\n"
        },
        {
          "text": "Compter le nombre d'occurrences",
          "correct": false,
          "feedback": "Erreur : c'est le « tri par comptage », un\nalgorithme différent (hors programme).\n"
        },
        {
          "text": "Sélectionner le minimum et le placer en tête",
          "correct": false,
          "feedback": "Erreur : c'est le tri par **sélection**.\n"
        },
        {
          "text": "Inverser l'ordre des éléments",
          "correct": false,
          "feedback": "Erreur : aucune relation avec un tri.\n"
        }
      ],
      "explanation": "C'est exactement comme on trie une main de\ncartes : on prend une carte, on l'insère à sa\nplace dans le paquet déjà trié."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "echange"
      ],
      "title": "Échange Python",
      "statement": "Quelle est la façon la plus simple d'**échanger\ndeux éléments** d'une liste en Python ?",
      "options": [
        {
          "text": "`tmp = liste[i]; liste[i] = liste[j]; liste[j] = tmp`",
          "correct": false,
          "feedback": "Code correct mais verbeux. Python offre\nplus simple.\n"
        },
        {
          "text": "`liste.swap(i, j)`",
          "correct": false,
          "feedback": "Erreur : la méthode `.swap` n'existe pas\nsur les listes Python.\n"
        },
        {
          "text": "`liste[i] = liste[j]`",
          "correct": false,
          "feedback": "Erreur : on perd la valeur originale en\n`liste[i]`.\n"
        },
        {
          "text": "`liste[i], liste[j] = liste[j], liste[i]`",
          "correct": true,
          "feedback": "Bonne réponse : affectation simultanée,\nsyntaxe pythonique. Python crée un tuple\ntemporaire `(liste[j], liste[i])` puis\ndéballe.\n"
        }
      ],
      "explanation": "L'affectation simultanée est utilisée dans les\ntris classiques. Compacte et lisible."
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "complexite"
      ],
      "title": "Coût au pire cas",
      "statement": "Quel est le **coût** au pire cas du tri par\nsélection et du tri par insertion sur une liste\nde longueur n ?",
      "options": [
        {
          "text": "Logarithmique",
          "correct": false,
          "feedback": "Erreur : ce serait possible avec des tris\navancés (tri fusion en n log n, programme\nde Terminale).\n"
        },
        {
          "text": "Constant (indépendant de n)",
          "correct": false,
          "feedback": "Erreur : aucun algorithme ne trie en temps\nconstant.\n"
        },
        {
          "text": "Quadratique (proportionnel à n²)",
          "correct": true,
          "feedback": "Bonne réponse : les deux tris sont\n**quadratiques** au pire à cause d'une\n**double boucle** sur la liste. Pour\nn = 1000, on fait ~ 10⁶ opérations.\n"
        },
        {
          "text": "Linéaire (proportionnel à n)",
          "correct": false,
          "feedback": "Erreur : trop optimiste. La double boucle\nimpose plus.\n"
        }
      ],
      "explanation": "Coût quadratique = limite des tris « par\ncomparaisons simples ». Pour aller plus vite,\nil faut des stratégies plus malines (diviser\npour régner, Terminale)."
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "tri-en-place"
      ],
      "title": "Tri en place",
      "statement": "Que signifie « tri **en place** » ?",
      "options": [
        {
          "text": "Le tri se fait sur place dans le disque dur",
          "correct": false,
          "feedback": "Erreur : aucune relation avec le stockage\nphysique.\n"
        },
        {
          "text": "Le tri remplace les valeurs par leurs indices",
          "correct": false,
          "feedback": "Erreur : le tri ne modifie que l'ordre, pas\nles valeurs.\n"
        },
        {
          "text": "Le tri ne fonctionne que sur des listes courtes",
          "correct": false,
          "feedback": "Erreur : aucune restriction de taille.\n"
        },
        {
          "text": "Le tri ne crée pas de nouvelle liste : il modifie la liste d'origine",
          "correct": true,
          "feedback": "Bonne réponse : un tri en place utilise une\nquantité de mémoire **constante**\n(indépendante de n), juste quelques\nvariables temporaires. Sélection et\ninsertion sont des tris en place.\n"
        }
      ],
      "explanation": "Un tri **non en place** crée une copie triée\n(par exemple `sorted(liste)` en Python). Avec\n`liste.sort()`, on trie en place."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "meilleur-cas-insertion"
      ],
      "title": "Tri par insertion : meilleur cas",
      "statement": "Quel est le meilleur cas pour le **tri par\ninsertion** ?",
      "options": [
        {
          "text": "Liste déjà triée",
          "correct": true,
          "feedback": "Bonne réponse : si la liste est déjà triée,\nchaque élément est déjà à sa place ; on ne\nfait que vérifier (un seul test par\nélément). Coût linéaire (n - 1\ncomparaisons).\n"
        },
        {
          "text": "Liste de longueur impaire",
          "correct": false,
          "feedback": "Erreur : la parité de la longueur n'a\naucune influence.\n"
        },
        {
          "text": "Liste de doublons identiques",
          "correct": false,
          "feedback": "Cas favorable mais pas optimal : chaque\ninsertion fait au moins une comparaison.\n"
        },
        {
          "text": "Liste triée à l'envers",
          "correct": false,
          "feedback": "Erreur : c'est au contraire le **pire cas**\npour insertion (chaque élément remonte tout\nà fait à gauche).\n"
        }
      ],
      "explanation": "Cette propriété fait du tri par insertion un\nbon choix pour les listes **presque triées**\n(par exemple un fichier journal triable au fil\nde l'eau)."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "trace-selection"
      ],
      "title": "Trace tri par sélection",
      "statement": "On trie `[4, 1, 3]` par tri par sélection\n(croissant). Que vaut la liste après la\n**première** passe ?",
      "options": [
        {
          "text": "`[1, 3, 4]`",
          "correct": false,
          "feedback": "Erreur : c'est le résultat **final**, pas\naprès une passe.\n"
        },
        {
          "text": "`[1, 4, 3]`",
          "correct": true,
          "feedback": "Bonne réponse : on cherche le min de la\nliste entière (= 1, indice 1), on l'échange\navec `liste[0]`. Résultat : `[1, 4, 3]`.\n"
        },
        {
          "text": "`[3, 4, 1]`",
          "correct": false,
          "feedback": "Erreur : aucune permutation cohérente avec\nla sélection.\n"
        },
        {
          "text": "`[4, 1, 3]`",
          "correct": false,
          "feedback": "Erreur : la liste a bien été modifiée à la\npremière passe.\n"
        }
      ],
      "explanation": "Tri sélection passe par passe : passe 1 fixe\nl'élément en position 0 ; passe 2 fixe celui en\nposition 1 ; etc."
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "pire-cas-insertion"
      ],
      "title": "Tri par insertion : pire cas",
      "statement": "Quel est le **pire cas** pour le tri par\ninsertion ?",
      "options": [
        {
          "text": "Liste triée à l'envers (décroissant)",
          "correct": true,
          "feedback": "Bonne réponse : chaque nouvel élément doit\nêtre déplacé jusqu'au début de la partie\ndéjà traitée, soit n(n-1)/2 décalages au\ntotal. Coût quadratique.\n"
        },
        {
          "text": "Liste déjà triée",
          "correct": false,
          "feedback": "Erreur : c'est le **meilleur** cas (linéaire).\n"
        },
        {
          "text": "Liste avec doublons",
          "correct": false,
          "feedback": "Pas spécifique : le coût dépend de l'ordre,\npas des doublons.\n"
        },
        {
          "text": "Liste vide",
          "correct": false,
          "feedback": "Pas un pire cas : coût constant (la boucle\nne s'exécute pas).\n"
        }
      ],
      "explanation": "Symétrie typique : « ordre inverse » est\nsouvent le pire cas pour les tris naïfs ;\n« déjà trié » est souvent le meilleur cas pour\nl'insertion."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "stabilite"
      ],
      "title": "Stabilité d'un tri",
      "statement": "Un tri est dit **stable** si :",
      "options": [
        {
          "text": "Il préserve l'ordre relatif des éléments égaux",
          "correct": true,
          "feedback": "Bonne réponse : si deux éléments ont la\nmême clé de tri, leur ordre dans la\nliste triée est le même que dans la liste\nd'origine. Le tri par insertion est stable ;\nle tri par sélection ne l'est pas en\ngénéral.\n"
        },
        {
          "text": "Il fonctionne en temps constant",
          "correct": false,
          "feedback": "Faux et sans rapport.\n"
        },
        {
          "text": "Il ne se trompe jamais",
          "correct": false,
          "feedback": "Trop vague. La correction est exigible\npour tout tri ; la stabilité est un\ncritère plus précis.\n"
        },
        {
          "text": "Il ne plante jamais",
          "correct": false,
          "feedback": "Réponse triviale, sans rapport.\n"
        }
      ],
      "explanation": "La stabilité est essentielle quand on trie sur\nune clé secondaire après une clé principale\n(par exemple, trier des élèves par âge **puis**\npar nom : il faut que le tri par âge préserve\nl'ordre alphabétique)."
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "tri-selection-code"
      ],
      "title": "Code tri par sélection",
      "statement": "Quel code implémente correctement le **tri par\nsélection** croissant ?",
      "options": [
        {
          "text": "```python\nliste.sort()\n```\n",
          "correct": false,
          "feedback": "Erreur : c'est la fonction Python intégrée\n(Timsort en n log n), pas un tri par\nsélection.\n"
        },
        {
          "text": "```python\nfor x in liste:\n    x = min(liste)\n```\n",
          "correct": false,
          "feedback": "Erreur : `x` est une copie de la valeur,\non ne modifie pas la liste.\n"
        },
        {
          "text": "```python\nfor i in range(len(liste)):\n    j_min = i\n    for j in range(i + 1, len(liste)):\n        if liste[j] < liste[j_min]:\n            j_min = j\n    liste[i], liste[j_min] = liste[j_min], liste[i]\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : double boucle ; la boucle\ninterne cherche l'indice du min ; échange\navec `liste[i]` à la fin.\n"
        },
        {
          "text": "```python\nfor i in range(len(liste) - 1):\n    if liste[i] > liste[i + 1]:\n        liste[i], liste[i + 1] = liste[i + 1], liste[i]\n```\n",
          "correct": false,
          "feedback": "Erreur : une seule passe, ne trie pas\ncomplètement.\n"
        }
      ],
      "explanation": "Schéma classique : `j_min` mémorise l'indice\ndu minimum, échange à la fin pour minimiser\nles écritures."
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "tri-insertion-code"
      ],
      "title": "Code tri par insertion",
      "statement": "Quel code implémente correctement le **tri par\ninsertion** croissant ?",
      "options": [
        {
          "text": "```python\nfor i in range(len(liste)):\n    liste.append(min(liste))\n```\n",
          "correct": false,
          "feedback": "Erreur : on ajoute des éléments au lieu de\nréorganiser.\n"
        },
        {
          "text": "```python\nfor i in range(1, len(liste)):\n    j = i\n    while j > 0 and liste[j - 1] > liste[j]:\n        liste[j - 1], liste[j] = liste[j], liste[j - 1]\n        j -= 1\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : pour chaque élément à\npartir du second, on le décale vers la\ngauche tant qu'il est plus petit que son\nvoisin gauche.\n"
        },
        {
          "text": "```python\nwhile liste != sorted(liste):\n    pass\n```\n",
          "correct": false,
          "feedback": "Boucle infinie : `pass` ne modifie rien.\n"
        },
        {
          "text": "```python\nfor i in range(len(liste)):\n    for j in range(i):\n        if liste[i] < liste[j]:\n            liste[i] = liste[j]\n```\n",
          "correct": false,
          "feedback": "Erreur : `liste[i] = liste[j]` écrase la\nvaleur sans la sauvegarder. La liste est\ncorrompue.\n"
        }
      ],
      "explanation": "Variante équivalente : sauvegarder\n`valeur = liste[i]`, décaler à droite les\néléments plus grands, insérer la valeur. Plus\néconome en échanges."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "presque-trie"
      ],
      "title": "Liste presque triée",
      "statement": "Entre le **tri par insertion** et le **tri par\nsélection**, lequel est le plus rapide sur une\nliste « presque triée » (peu d'éléments mal\nplacés) ?",
      "options": [
        {
          "text": "Les deux sont équivalents",
          "correct": false,
          "feedback": "Erreur : leur comportement diffère\nprécisément dans le cas favorable.\nL'insertion s'adapte aux données déjà\ntriées, la sélection non.\n"
        },
        {
          "text": "Aucun des deux ne tire profit de la situation, ils sont aussi lents l'un que l'autre",
          "correct": false,
          "feedback": "Erreur : il y a une différence importante\nen pratique. Le tri par insertion sait\ntirer parti des données déjà triées, ce\nqui n'est pas le cas du tri par sélection.\n"
        },
        {
          "text": "Tri par sélection",
          "correct": false,
          "feedback": "Erreur : la sélection fait toujours un coût\nquadratique, même sur une liste déjà\ntriée. Elle ne sait pas tirer parti de\nl'état des données.\n"
        },
        {
          "text": "Tri par insertion",
          "correct": true,
          "feedback": "Bonne réponse : sur une liste presque\ntriée, le tri par insertion est en coût\nlinéaire (peu de décalages). C'est pour\ncela qu'on le préfère sur les petites\nlistes ou les listes presque triées.\n"
        }
      ],
      "explanation": "Cette propriété de l'insertion explique que\nPython (Timsort, hors programme) bascule sur\nle tri par insertion sur les petits sous-\ntableaux."
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "invariant-selection"
      ],
      "title": "Invariant tri par sélection",
      "statement": "Quel **invariant** vérifie le tri par sélection\naprès l'itération `i` de la boucle externe ?",
      "options": [
        {
          "text": "La liste est inchangée",
          "correct": false,
          "feedback": "Erreur : on a fait des échanges.\n"
        },
        {
          "text": "Les `i` premiers éléments sont les plus grands",
          "correct": false,
          "feedback": "Erreur : c'est l'inverse pour un tri\ncroissant.\n"
        },
        {
          "text": "Les `i` premiers éléments sont triés et plus petits ou égaux à tous les autres",
          "correct": true,
          "feedback": "Bonne réponse : c'est l'invariant clé. À la\nfin (i = n), les n premiers (= toute la\nliste) sont triés.\n"
        },
        {
          "text": "La liste est entièrement triée",
          "correct": false,
          "feedback": "Trop fort : seules les `i` premières\npositions sont triées.\n"
        }
      ],
      "explanation": "Cet invariant rend la correction du tri par\nsélection évidente. Il combine deux conditions :\n« triés » + « plus petits que les autres »."
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "terminaison-tri"
      ],
      "title": "Terminaison",
      "statement": "Pourquoi les tris par sélection et par\ninsertion **terminent** toujours ?",
      "options": [
        {
          "text": "Parce que chaque boucle a un variant strict : le `for` parcourt un `range` fini, et le `while` interne du tri par insertion fait décroître strictement `j`, minoré par $0$",
          "correct": true,
          "feedback": "Bonne réponse : un `for` sur un `range`\nfini termine automatiquement (variant\nimplicite). Pour le `while` du tri par\ninsertion, on observe que `j` décroît\nstrictement à chaque tour et reste minoré\npar $0$, donc la boucle s'arrête en au\nplus `j` itérations.\n"
        },
        {
          "text": "Parce que Python limite les boucles à 1000 itérations",
          "correct": false,
          "feedback": "Erreur : Python n'impose aucune telle limite.\n"
        },
        {
          "text": "Parce qu'aucun tri n'a de boucle while",
          "correct": false,
          "feedback": "Erreur : le tri par insertion utilise un\nwhile.\n"
        },
        {
          "text": "Parce que les listes Python sont finies",
          "correct": false,
          "feedback": "Vrai mais pas la justification suffisante :\nil faut prouver que les boucles s'arrêtent.\n"
        }
      ],
      "explanation": "Justification rigoureuse : pour chaque boucle,\nidentifier ce qui change strictement à chaque\nitération (variant) et la borne qui sera\natteinte."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "trace-insertion"
      ],
      "title": "Trace tri par insertion",
      "statement": "On trie `[3, 1, 4, 1, 5]` par tri par\ninsertion croissant. Quel est l'état de la\nliste **après l'itération i = 3** ?",
      "options": [
        {
          "text": "`[3, 1, 4, 1, 5]`",
          "correct": false,
          "feedback": "Erreur : c'est la liste d'origine non\nmodifiée.\n"
        },
        {
          "text": "`[1, 1, 3, 4, 5]`",
          "correct": true,
          "feedback": "Bonne réponse. Trace : i=1 → `[1,3,4,1,5]` ;\ni=2 → `[1,3,4,1,5]` (le 4 ne bouge pas) ;\ni=3 → on insère le 1 (indice 3) à sa place\n: `[1,1,3,4,5]`.\n"
        },
        {
          "text": "`[1, 3, 4, 1, 5]`",
          "correct": false,
          "feedback": "Erreur : c'est l'état après i = 2\n(l'élément d'indice 2, le 4, est déjà à sa\nplace).\n"
        },
        {
          "text": "`[1, 3, 1, 4, 5]`",
          "correct": false,
          "feedback": "Erreur : le 1 doit aller jusqu'à l'indice\n1, pas à l'indice 2.\n"
        }
      ],
      "explanation": "Méthode : à chaque itération i, l'élément\n`liste[i]` rejoint la partie triée\n`liste[0..i-1]` (en Python : `liste[:i]`) à sa\nbonne place."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "echanges-selection"
      ],
      "title": "Nombre d'échanges sélection",
      "statement": "Combien d'échanges (au maximum) effectue le\ntri par sélection sur une liste de longueur n ?",
      "options": [
        {
          "text": "`log n`",
          "correct": false,
          "feedback": "Erreur : pas de mécanisme dichotomique.\n"
        },
        {
          "text": "`n - 1`",
          "correct": true,
          "feedback": "Bonne réponse : un échange par itération\nde la boucle externe. Quel que soit l'ordre\ninitial, on fait au plus n - 1 échanges\n(parfois on évite l'échange si j_min = i,\nmais pour le pire cas, c'est n - 1).\n"
        },
        {
          "text": "`n × (n - 1) / 2`",
          "correct": false,
          "feedback": "Erreur : c'est l'ordre de grandeur des\n**comparaisons**, pas des échanges.\n"
        },
        {
          "text": "`n`",
          "correct": false,
          "feedback": "Erreur : la dernière itération est inutile.\n"
        }
      ],
      "explanation": "Caractéristique remarquable du tri par\nsélection : très peu d'échanges, indépendamment\nde l'ordre initial. Avantageux quand les\nécritures sont coûteuses (mémoire flash)."
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "comparaison-selection-insertion"
      ],
      "title": "Sélection vs insertion",
      "statement": "Sur une liste **déjà triée** de 1000 éléments,\nquel tri est le plus rapide ?",
      "options": [
        {
          "text": "Aucun ne fonctionne",
          "correct": false,
          "feedback": "Les deux algorithmes sont\nparfaitement corrects ; ils\nfiniront par trier la liste.\nLa question portait sur leur\nperformance comparée, et le\ntri par insertion est très\nrapide sur une liste presque\ntriée.\n"
        },
        {
          "text": "Le tri par sélection",
          "correct": false,
          "feedback": "Erreur : il fait quand même le coût\nquadratique (comparaisons), même si peu\nd'échanges.\n"
        },
        {
          "text": "Les deux sont équivalents",
          "correct": false,
          "feedback": "Erreur : différence d'ordre de grandeur sur\nce cas.\n"
        },
        {
          "text": "Le tri par insertion",
          "correct": true,
          "feedback": "Bonne réponse : sur une liste déjà triée,\nl'insertion est en coût linéaire (~ n\ncomparaisons, aucune décale). La sélection\nreste quadratique.\n"
        }
      ],
      "explanation": "Choix selon le cas pratique : sélection si\nles écritures sont coûteuses (peu d'échanges),\ninsertion si les données arrivent presque\ntriées."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "tri-prealable"
      ],
      "title": "Trier puis chercher",
      "statement": "On veut faire **plusieurs recherches**\nd'éléments dans une liste. Pourquoi peut-il\nêtre intéressant de la **trier** d'abord ?",
      "options": [
        {
          "text": "Pour économiser de la mémoire",
          "correct": false,
          "feedback": "Erreur : trier ne change pas la mémoire.\n"
        },
        {
          "text": "Aucun intérêt",
          "correct": false,
          "feedback": "Au contraire, dès que l'on\nfait plusieurs recherches,\ntrier une fois pour toutes\ndevient très rentable, car\nchaque recherche passe d'un\ncoût linéaire à un coût\nlogarithmique.\n"
        },
        {
          "text": "Parce qu'on pourra ensuite utiliser la recherche dichotomique (coût logarithmique) plutôt que linéaire",
          "correct": true,
          "feedback": "Bonne réponse : le tri (n log n par tri\nfusion en Terminale, ou quadratique en\nPremière) est un investissement amorti par\nde nombreuses recherches rapides.\n"
        },
        {
          "text": "Pour éviter les bugs",
          "correct": false,
          "feedback": "Trier ne corrige aucun bug\ndans le code de recherche.\nLe bénéfice du tri préalable\nest purement algorithmique :\nil rend la dichotomie\napplicable.\n"
        }
      ],
      "explanation": "Investissement initial (tri) vs économie sur\nles recherches futures. Pour une seule\nrecherche, le tri n'est pas rentable."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "tris-natif-python"
      ],
      "title": "Tris natifs Python",
      "statement": "Pourquoi `liste.sort()` n'utilise-t-il **pas**\nle tri par sélection ou par insertion ?",
      "options": [
        {
          "text": "Parce que Python ne sait pas les implémenter",
          "correct": false,
          "feedback": "Python sait parfaitement\nimplémenter ces tris ; on\nle fait régulièrement à\ndes fins pédagogiques. Le\nchoix de Timsort est dicté\npar la performance, pas\npar une limitation du\nlangage.\n"
        },
        {
          "text": "Parce que les listes Python ne sont pas modifiables",
          "correct": false,
          "feedback": "Erreur : les listes sont modifiables.\n"
        },
        {
          "text": "Parce qu'aucun tri n'est implémenté",
          "correct": false,
          "feedback": "La méthode `liste.sort()`\nexiste et trie effectivement\nla liste. Elle s'appuie sur\nTimsort, un algorithme très\nperformant intégré au cœur\nde CPython.\n"
        },
        {
          "text": "Parce que Python utilise un tri plus efficace (Timsort, en n log n) sur les grandes listes",
          "correct": true,
          "feedback": "Bonne réponse : Timsort (hybride mêlant\ninsertion sur petits blocs et fusion) est\nbeaucoup plus rapide que sélection ou\ninsertion. Python applique cependant le\ntri par insertion sur les sous-tableaux\nde moins de ~ 64 éléments.\n"
        }
      ],
      "explanation": "Au programme de Première : sélection et\ninsertion (compréhension). En pratique\n(Terminale et au-delà) : tris en n log n\n(fusion, rapide, Timsort)."
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "bug-borne"
      ],
      "title": "Bug d'indice",
      "statement": "```python\nfor i in range(1, len(liste) + 1):\n    j = i\n    while j > 0 and liste[j - 1] > liste[j]:\n        liste[j - 1], liste[j] = liste[j], liste[j - 1]\n        j -= 1\n```\nPourquoi ce code (tentative de tri par\ninsertion) est-il **bogué** ?",
      "options": [
        {
          "text": "La complexité est trop élevée",
          "correct": false,
          "feedback": "Erreur : la complexité est correcte, c'est\nla borne qui plante.\n"
        },
        {
          "text": "La borne externe est `len(liste) + 1` au lieu de `len(liste)` ; à la dernière itération, on accède à `liste[len(liste)]` qui n'existe pas → `IndexError`",
          "correct": true,
          "feedback": "Bonne réponse : bug d'**off-by-one**.\nToujours vérifier les bornes des boucles.\nLa forme correcte est `range(1,\nlen(liste))`.\n"
        },
        {
          "text": "La fonction `range` n'accepte pas deux paramètres",
          "correct": false,
          "feedback": "Erreur : la syntaxe est valide.\n"
        },
        {
          "text": "Aucun bug",
          "correct": false,
          "feedback": "Le code contient bien une\nerreur de borne : la boucle\nva jusqu'à `len(liste)`\ninclus, ce qui provoque un\naccès hors tableau lors de\nla dernière itération.\n"
        }
      ],
      "explanation": "Bugs « off-by-one » : très fréquents.\nToujours tester avec des listes simples\n(`[]`, `[1]`, `[1, 2]`) pour les détecter."
    },
    {
      "id": "q22",
      "difficulty": 3,
      "skills": [
        "analyse-comparaisons"
      ],
      "title": "Nombre de comparaisons",
      "statement": "Combien de comparaisons effectue le tri par\n**sélection** sur une liste de longueur n,\nquel que soit l'ordre initial ?",
      "options": [
        {
          "text": "Environ n²/2 comparaisons (toujours)",
          "correct": true,
          "feedback": "Bonne réponse : la boucle externe fait n\nitérations, l'interne fait en moyenne n/2.\nTotal : n × n/2 = n²/2 comparaisons. **Le\ntri par sélection ne tire aucun parti**\nd'un ordre favorable : il fait toujours le\nmême travail.\n"
        },
        {
          "text": "Environ log n",
          "correct": false,
          "feedback": "Erreur : pas de dichotomie.\n"
        },
        {
          "text": "Environ n comparaisons",
          "correct": false,
          "feedback": "Erreur : la double boucle impose\nquadratique.\n"
        },
        {
          "text": "Aucune si la liste est déjà triée",
          "correct": false,
          "feedback": "Erreur : la sélection ne « voit » pas que la\nliste est triée. Elle parcourt quand même\ntout.\n"
        }
      ],
      "explanation": "Différence majeure : insertion s'adapte (cas\nfavorable linéaire), sélection ne s'adapte\npas. Pour de petites listes presque triées,\npréférer l'insertion."
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "stabilite-detail"
      ],
      "title": "Stabilité de la sélection",
      "statement": "Pourquoi le tri par **sélection** n'est-il en\ngénéral **pas stable** ?",
      "options": [
        {
          "text": "Parce qu'il modifie la liste en place",
          "correct": false,
          "feedback": "Erreur : l'insertion aussi modifie en place\net est stable.\n"
        },
        {
          "text": "Parce que l'échange entre `liste[i]` et `liste[j_min]` peut faire passer un élément avant un autre élément égal",
          "correct": true,
          "feedback": "Bonne réponse : exemple `[2a, 2b, 1]`\n(où 2a et 2b sont deux éléments égaux à 2\ndans cet ordre). Sélection trouve min=1,\néchange : `[1, 2b, 2a]`. L'ordre relatif\nde 2a et 2b est inversé. Donc non stable.\n"
        },
        {
          "text": "Parce qu'il est lent",
          "correct": false,
          "feedback": "Erreur : performance et stabilité sont\nindépendantes.\n"
        },
        {
          "text": "Parce qu'il utilise des doubles boucles",
          "correct": false,
          "feedback": "Erreur : l'insertion aussi.\n"
        }
      ],
      "explanation": "Pour avoir un tri par sélection stable, il\nfaudrait décaler au lieu d'échanger (plus\ncoûteux). En pratique, l'insertion ou un tri\nstable plus efficace est préféré."
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "choix-tri"
      ],
      "title": "Choisir un tri",
      "statement": "Pour trier une liste de **5 éléments** dans un\nmicrocontrôleur où l'on doit **minimiser\nl'écriture en mémoire flash** (les écritures\nsont lentes), quel tri choisir parmi\nsélection et insertion ?",
      "options": [
        {
          "text": "Aucun, il faut un tri à bulles",
          "correct": false,
          "feedback": "Hors programme. Et sur le critère du\nnombre d'écritures, le tri par sélection\nest meilleur.\n"
        },
        {
          "text": "Tri par insertion",
          "correct": false,
          "feedback": "Erreur : peut faire jusqu'à n(n-1)/2 = 10\néchanges au pire.\n"
        },
        {
          "text": "Tri par sélection",
          "correct": true,
          "feedback": "Bonne réponse : il fait au plus n - 1 = 4\néchanges, indépendamment de l'ordre\ninitial. Optimal pour minimiser les\nécritures.\n"
        },
        {
          "text": "Aucun, on doit utiliser Timsort",
          "correct": false,
          "feedback": "Pas idéal sur micro-contrôleur : Timsort\nest complexe à implémenter et n'est pas\nforcément disponible.\n"
        }
      ],
      "explanation": "Le choix d'un algorithme dépend du **contexte\nmatériel** et des **contraintes** (mémoire,\nénergie, écritures, temps). Pas d'algorithme\nuniversel."
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Synthèse",
      "statement": "Parmi les affirmations suivantes sur les tris\nau programme (sélection, insertion), laquelle\nest **fausse** ?",
      "options": [
        {
          "text": "Le coût au pire des deux est quadratique.",
          "correct": false,
          "feedback": "Vrai : caractéristique commune.\n"
        },
        {
          "text": "Les deux tris peuvent être implémentés en place (sans liste auxiliaire).",
          "correct": false,
          "feedback": "Vrai : caractéristique commune.\n"
        },
        {
          "text": "Le tri par sélection est stable par défaut.",
          "correct": true,
          "feedback": "Faux (donc bonne réponse) : le tri par\nsélection n'est **pas** stable en général.\nL'échange peut modifier l'ordre relatif\nd'éléments égaux. L'insertion, elle, est\nstable.\n"
        },
        {
          "text": "Le tri par insertion est en coût linéaire sur une liste déjà triée.",
          "correct": false,
          "feedback": "Vrai : sa propriété remarquable\n(meilleur cas).\n"
        }
      ],
      "explanation": "À retenir : sélection = peu d'échanges (n-1)\nmais pas stable, ne s'adapte pas au cas\nfavorable. Insertion = stable, excellent en\ncas favorable, plus d'échanges au pire."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "insertion-decalage"
      ],
      "title": "Tri par insertion par décalage",
      "statement": "Une variante classique du tri par insertion ne\nfait **pas** d'échanges deux à deux mais un\n**décalage** suivi d'une insertion. Quel code\nmet cette idée en œuvre correctement ?",
      "options": [
        {
          "text": "```python\nfor i in range(1, len(liste)):\n    valeur = liste[i]\n    j = i\n    while j > 0 and liste[j - 1] > valeur:\n        liste[j] = liste[j - 1]\n        j -= 1\n```\n",
          "correct": false,
          "feedback": "Erreur : il manque la dernière étape\n`liste[j] = valeur`. La valeur sauvegardée\nn'est jamais réinsérée, et la position\nlibérée garde sa valeur précédente déjà\ndécalée. La liste est corrompue.\n"
        },
        {
          "text": "```python\nfor i in range(1, len(liste)):\n    valeur = liste[i]\n    for j in range(i, 0, -1):\n        liste[j] = valeur\n```\n",
          "correct": false,
          "feedback": "Erreur : on remplit toutes les positions de\n`j = i` jusqu'à `j = 1` avec la même\nvaleur. La liste finit par contenir des\ncopies multiples du même élément.\n"
        },
        {
          "text": "```python\nfor i in range(1, len(liste)):\n    valeur = liste[i]\n    j = i\n    while j > 0 and liste[j - 1] > valeur:\n        liste[j] = liste[j - 1]\n        j -= 1\n    liste[j] = valeur\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : on sauvegarde la valeur\ncourante, on décale vers la droite tous les\néléments plus grands à gauche, puis on\ninsère la valeur à la position libérée. Une\nseule écriture par décalage (au lieu de deux\ndans la version par échanges), donc plus\nrapide en pratique.\n"
        },
        {
          "text": "```python\nfor i in range(1, len(liste)):\n    j = i\n    while j > 0 and liste[j - 1] > liste[j]:\n        liste[j] = liste[j - 1]\n        j -= 1\n```\n",
          "correct": false,
          "feedback": "Erreur : on n'a pas sauvegardé la valeur\nd'origine de `liste[i]` avant de la\nremplacer par `liste[j - 1]`. Dès la\npremière itération, cette valeur est\nperdue, donc l'insertion finale est\nimpossible.\n"
        }
      ],
      "explanation": "Cette variante est en général plus rapide en\npratique : un décalage = une seule écriture,\ncontre deux pour un échange. C'est ce que fait\nPython en interne pour les petits sous-tableaux\ndans Timsort."
    },
    {
      "id": "q27",
      "difficulty": 2,
      "skills": [
        "analyse-trace"
      ],
      "title": "Comparaisons précises sur exemple",
      "statement": "Combien de comparaisons (entre éléments)\neffectue le tri par **sélection** sur la liste\n`[5, 3, 8, 1]` (n = 4) ?",
      "options": [
        {
          "text": "4",
          "correct": false,
          "feedback": "Erreur : c'est le nombre d'éléments. La\ndouble boucle fait beaucoup plus de\ncomparaisons.\n"
        },
        {
          "text": "16",
          "correct": false,
          "feedback": "Erreur : ce serait `n²`, mais le tri par\nsélection fait `n(n - 1) / 2`\ncomparaisons, pas `n²`. La boucle interne\nne repart pas de zéro à chaque passe.\n"
        },
        {
          "text": "3",
          "correct": false,
          "feedback": "Erreur : ce serait le nombre d'**itérations**\nde la boucle externe (n - 1), pas le total\ndes comparaisons internes.\n"
        },
        {
          "text": "6",
          "correct": true,
          "feedback": "Bonne réponse : la formule générale est\n`(n - 1) + (n - 2) + ... + 1 = n(n - 1) / 2`,\nsoit ici `4 × 3 / 2 = 6`. Détail : passe 1\n→ 3 comparaisons (parcours indices 1 à 3) ;\npasse 2 → 2 ; passe 3 → 1. Total : 6.\n"
        }
      ],
      "explanation": "Formule à retenir : `n(n - 1) / 2`\ncomparaisons pour le tri par sélection, **quel\nque soit l'ordre initial**. C'est l'ordre de\ngrandeur quadratique. Pour n = 1000 :\n499 500 comparaisons."
    },
    {
      "id": "q28",
      "difficulty": 3,
      "skills": [
        "stabilite-application"
      ],
      "title": "Stabilité : application pratique",
      "statement": "On a une liste d'élèves représentée par des\ncouples `(nom, classe)`, déjà triée par nom.\nOn veut maintenant la trier par classe **tout\nen conservant l'ordre alphabétique des noms à\nl'intérieur de chaque classe**. Pourquoi\nfaut-il un tri stable ?",
      "options": [
        {
          "text": "Parce qu'un tri stable est plus rapide qu'un tri non stable",
          "correct": false,
          "feedback": "Erreur : la rapidité et la stabilité sont des\ncritères indépendants. Le tri par insertion\nest stable et lent ; il existe des tris\nrapides non stables (tri par sélection\nstandard, tri rapide).\n"
        },
        {
          "text": "Parce qu'un tri stable consomme moins de mémoire",
          "correct": false,
          "feedback": "Erreur : la consommation mémoire dépend de\nl'algorithme (en place ou non), pas de la\nstabilité. Le tri par sélection est en\nplace mais n'est pas stable.\n"
        },
        {
          "text": "Parce que les classes sont des chaînes de caractères",
          "correct": false,
          "feedback": "Erreur : la stabilité n'a rien à voir avec\nle type des clés. Elle concerne le\ntraitement des éléments dont les clés sont\négales, quel que soit le type.\n"
        },
        {
          "text": "Parce qu'un tri stable préserve l'ordre\nrelatif des élèves ayant la même classe ;\nainsi, à l'intérieur de chaque classe,\nl'ordre alphabétique des noms (issu du\npremier tri) est conservé\n",
          "correct": true,
          "feedback": "Bonne réponse : c'est l'application typique\nde la stabilité, le tri multi-critères. On\ntrie d'abord sur le critère secondaire\n(nom), puis sur le critère principal\n(classe) avec un tri stable. À l'arrivée,\nles élèves sont rangés par classe puis par\nnom.\n"
        }
      ],
      "explanation": "Schéma général du tri multi-critères : trier\nd'abord par critère secondaire, puis par\ncritère principal avec un tri stable. C'est\npour cela que la fonction `sorted()` de Python,\nqui utilise Timsort, est garantie stable."
    },
    {
      "id": "q29",
      "difficulty": 2,
      "skills": [
        "trace-selection-detaillee"
      ],
      "title": "Trace pas-à-pas du tri par sélection",
      "statement": "On trie la liste `[3, 1, 4, 2]` par tri par\nsélection croissant. Quels sont les états\nsuccessifs de la liste **après chaque\nitération de la boucle externe** (chaque\n« passe ») ?",
      "options": [
        {
          "text": "Après passe $1$ : `[1, 3, 4, 2]`. Après\npasse $2$ : `[1, 2, 4, 3]`. Après passe\n$3$ : `[1, 2, 3, 4]`. La passe $4$\n(sur le dernier élément seul) ne\nchange rien\n",
          "correct": true,
          "feedback": "Bonne réponse : à chaque passe, on\ncherche le minimum à partir de la\nposition courante et on l'échange avec\nl'élément en cette position. Passe $1$\n(position $0$) : minimum de\n`[3, 1, 4, 2]` est $1$ en position\n$1$, on échange → `[1, 3, 4, 2]`.\nPasse $2$ (position $1$) : minimum de\n`[3, 4, 2]` est $2$ en position $3$,\non échange `liste[1]` avec `liste[3]`\n→ `[1, 2, 4, 3]`. Passe $3$ (position\n$2$) : minimum de `[4, 3]` est $3$,\non échange → `[1, 2, 3, 4]`.\n"
        },
        {
          "text": "Après passe $1$ : `[3, 1, 4, 2]`\n(inchangée). Après passe $2$ :\n`[1, 3, 4, 2]`. Après passe $3$ :\n`[1, 2, 4, 3]`\n",
          "correct": false,
          "feedback": "Erreur : la première passe modifie\nbien la liste en plaçant le minimum\n$1$ en position $0$. Une passe ne\nlaisse la liste inchangée que si\nl'élément en position courante est\ndéjà le minimum du sous-tableau, ce\nqui n'est pas le cas ici (la position\n$0$ contient $3$, pas $1$).\n"
        },
        {
          "text": "Après passe $1$ : `[1, 3, 4, 2]`. Après\npasse $2$ : `[1, 3, 2, 4]`. Après passe\n$3$ : `[1, 2, 3, 4]`\n",
          "correct": false,
          "feedback": "Erreur à la passe $2$ : à partir de la\nposition $1$, on cherche le minimum\nparmi `[3, 4, 2]`, qui est $2$ en\nposition $3$. On échange donc\n`liste[1]` (qui vaut $3$) avec\n`liste[3]` (qui vaut $2$), ce qui\ndonne `[1, 2, 4, 3]` et non\n`[1, 3, 2, 4]`.\n"
        },
        {
          "text": "Après passe $1$ : `[1, 2, 3, 4]`. Les\npasses suivantes ne changent rien\n",
          "correct": false,
          "feedback": "Erreur : le tri par sélection ne place\nqu'**un seul** élément à sa position\nfinale par passe (le minimum du\nsous-tableau restant). Il faut donc\nplusieurs passes successives, et la\nliste n'est triée qu'à la fin.\n"
        }
      ],
      "explanation": "Pour tracer le tri par sélection, on tient\nà jour à chaque passe : (1) la position\ncourante (position de la passe), (2)\nl'indice du minimum dans le sous-tableau\n`liste[pos:]`, (3) l'échange éventuel.\nLa liste se trie de gauche à droite, un\nélément à la fois. Pour `n = 4` éléments,\n$3$ passes suffisent (la dernière, sur un\nseul élément, est inutile)."
    },
    {
      "id": "q30",
      "difficulty": 2,
      "skills": [
        "trace-insertion-detaillee"
      ],
      "title": "Trace pas-à-pas du tri par insertion",
      "statement": "On trie la même liste `[3, 1, 4, 2]` par\ntri par insertion croissant. Quels sont\nles états successifs **après chaque\nitération de la boucle externe** ?",
      "options": [
        {
          "text": "Après $i = 1$ (insertion du $1$) :\n`[1, 3, 4, 2]`. Après $i = 2$\n(insertion du $4$) : `[1, 3, 4, 2]`\n(inchangée, $4$ est déjà à sa place).\nAprès $i = 3$ (insertion du $2$) :\n`[1, 2, 3, 4]`\n",
          "correct": true,
          "feedback": "Bonne réponse : à chaque itération $i$,\non prend `liste[i]` et on l'insère à sa\nplace dans la portion déjà triée\n`liste[0..i-1]` (en Python : `liste[:i]`).\nPour $i = 1$, le $1$\nremonte avant le $3$. Pour $i = 2$, le\n$4$ est déjà à sa place (rien à\nfaire). Pour $i = 3$, le $2$ remonte\njusqu'à se placer entre $1$ et $3$,\nce qui décale aussi le $3$ et le $4$\nd'un cran.\n"
        },
        {
          "text": "Après $i = 1$ : `[3, 1, 4, 2]`\n(inchangée). Après $i = 2$ :\n`[1, 3, 4, 2]`. Après $i = 3$ :\n`[1, 2, 3, 4]`\n",
          "correct": false,
          "feedback": "Erreur : la première itération\n($i = 1$) insère le $1$ à sa place\ndans la portion `[3]`, ce qui le\nplace avant le $3$. La liste devient\ndonc `[1, 3, 4, 2]` après cette\nitération, pas inchangée.\n"
        },
        {
          "text": "Après $i = 1$ : `[1, 3, 4, 2]`. Après\n$i = 2$ : `[1, 2, 3, 4]`. Après\n$i = 3$ : `[1, 2, 3, 4]`\n",
          "correct": false,
          "feedback": "Erreur : à $i = 2$, on insère le $4$,\npas le $2$. Le $4$ étant déjà à la\nfin de la portion triée `[1, 3]`, il\nn'a pas besoin d'être déplacé. La\nliste reste `[1, 3, 4, 2]`, pas\n`[1, 2, 3, 4]`. C'est à $i = 3$, en\ninsérant le $2$, qu'on obtient le\ntri final.\n"
        },
        {
          "text": "Identique au tri par sélection :\n`[1, 3, 4, 2]` puis `[1, 2, 4, 3]`\npuis `[1, 2, 3, 4]`\n",
          "correct": false,
          "feedback": "Erreur : les deux algorithmes ne\nproduisent pas les mêmes états\nintermédiaires. Au tri par sélection,\nchaque passe place le minimum du\nreste ; au tri par insertion, chaque\npasse insère l'élément suivant à sa\nplace dans la partie triée. Les\ncomportements à la passe $2$ diffèrent.\n"
        }
      ],
      "explanation": "Différence remarquable avec le tri par\nsélection : à $i = 2$, le tri par\ninsertion ne fait quasiment rien (juste\nune comparaison qui constate que $4 > 3$).\nC'est typique : si la liste est presque\ntriée, l'insertion devient quasi linéaire,\ntandis que la sélection fait toujours le\nmême travail quadratique. Cette propriété\nd'**adaptabilité** rend l'insertion\npréférable sur des données quasi-triées."
    },
    {
      "id": "q31",
      "difficulty": 2,
      "skills": [
        "tri-decroissant"
      ],
      "title": "Trier en ordre décroissant",
      "statement": "Comment adapter un tri par insertion\ncroissant pour qu'il trie en ordre\n**décroissant** ?",
      "options": [
        {
          "text": "Appliquer le tri croissant puis\ninverser la liste finale avec\n`liste.reverse()`\n",
          "correct": false,
          "feedback": "Cette approche fonctionne et coûte\njuste un $O(n)$ supplémentaire. Mais\nla question demandait comment\n**adapter** le tri lui-même. Modifier\nla comparaison interne est plus\nélégant et tout aussi efficace.\n"
        },
        {
          "text": "Trier la liste à l'envers d'abord, puis\nappliquer le tri croissant\n",
          "correct": false,
          "feedback": "Erreur : « trier à l'envers d'abord »\nn'a pas de sens si on n'a pas encore\nde tri. L'inversion d'une liste se\nfait en $O(n)$, certes, mais ce n'est\npas la modification la plus simple\npour adapter un tri.\n"
        },
        {
          "text": "Multiplier toutes les valeurs par\n$-1$, trier, puis multiplier à\nnouveau par $-1$\n",
          "correct": false,
          "feedback": "Cette astuce fonctionne pour des\nnombres, mais pas pour des chaînes ou\ndes objets quelconques. Et elle est\ninutilement compliquée par rapport à\nl'inversion de la comparaison.\n"
        },
        {
          "text": "Inverser le sens de la comparaison dans\nla boucle interne : utiliser\n`liste[j - 1] < liste[j]` au lieu de\n`liste[j - 1] > liste[j]`\n",
          "correct": true,
          "feedback": "Bonne réponse : la comparaison entre\ndeux éléments adjacents décide si on\nles permute. En inversant le sens, on\ntrie dans l'ordre opposé. C'est la\nmodification la plus simple et la\nplus localisée. Pour le tri par\nsélection, on cherche le **maximum**\nau lieu du minimum à chaque passe.\n"
        }
      ],
      "explanation": "Cette logique se généralise : pour un tri\navec un critère arbitraire (par âge, par\nnom, par taille, etc.), on n'a qu'à\nmodifier la **fonction de comparaison**.\nPython permet cela via le paramètre\n`key=` de `sorted` ou `list.sort` :\n`sorted(liste, key=lambda x: -x.age)` ou\n`reverse=True` pour l'ordre décroissant."
    },
    {
      "id": "q32",
      "difficulty": 2,
      "skills": [
        "doublons-tri"
      ],
      "title": "Comportement avec doublons",
      "statement": "Que se passe-t-il quand on trie une liste\ncontenant des **doublons**, par exemple\n`[3, 1, 3, 2, 3]` ?",
      "options": [
        {
          "text": "Le tri fonctionne normalement et\nrenvoie `[1, 2, 3, 3, 3]`. Si le tri\nest stable, l'ordre relatif des\ntrois `3` (qui étaient en positions\n$0$, $2$, $4$ d'origine) est préservé\n",
          "correct": true,
          "feedback": "Bonne réponse : les algorithmes de tri\nstandard fonctionnent parfaitement\navec des doublons. La distinction\nstable / non stable porte sur l'ordre\nrelatif des éléments **égaux**. Pour\ndes entiers, cette distinction est\ninvisible (un $3$ vaut un $3$). Mais\npour des objets avec des clés\ncomposites (par exemple\n`(nom, age)` triés par `age`), la\nstabilité préserve l'ordre alphabétique\ninterne.\n"
        },
        {
          "text": "Les doublons sont automatiquement\nsupprimés\n",
          "correct": false,
          "feedback": "Erreur : le tri ne supprime pas les\ndoublons. Il les conserve tous, dans\nl'ordre. Pour supprimer les doublons,\nil faut une étape distincte (par\nexemple convertir en `set` puis\nretrier).\n"
        },
        {
          "text": "L'algorithme tombe en boucle infinie\n",
          "correct": false,
          "feedback": "Erreur : aucun risque de boucle\ninfinie. Les comparaisons restent\ndéterministes, les indices avancent\nnormalement.\n"
        },
        {
          "text": "Le tri lève une exception\n",
          "correct": false,
          "feedback": "Erreur : aucun problème logique avec\nles doublons. Les algorithmes de tri\nsont conçus pour fonctionner avec\nn'importe quelle suite de valeurs\ncomparables, doublons compris.\n"
        }
      ],
      "explanation": "Test concret : trier\n`[(\"Alice\", 17), (\"Bob\", 17), (\"Chloé\", 15)]`\npar âge avec un tri stable donne\n`[(\"Chloé\", 15), (\"Alice\", 17), (\"Bob\", 17)]`\n(Alice avant Bob, comme dans la liste\nd'origine). Avec un tri non stable, on\npourrait obtenir aussi\n`[(\"Chloé\", 15), (\"Bob\", 17), (\"Alice\", 17)]`\n(Bob avant Alice), ce qui peut être\ngênant en pratique."
    }
  ]
}