{
  "chapter": {
    "id": "diviser-pour-regner",
    "level": "terminale",
    "theme": "Algorithmique",
    "title": "Diviser pour régner",
    "description": "Paradigme diviser pour régner (étapes diviser, régner, combiner),\nrecherche dichotomique, tri fusion et tri rapide, exponentiation\nrapide, complexité par récurrence et théorème maître.",
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "paradigme",
        "definition"
      ],
      "title": "Étapes du paradigme diviser pour régner",
      "statement": "Quelles sont les **trois étapes** du paradigme diviser pour\nrégner ?",
      "options": [
        {
          "text": "Initialiser, itérer, terminer",
          "correct": false,
          "feedback": "Erreur : ces étapes décrivent une boucle, pas le paradigme\ndiviser pour régner qui est typiquement récursif.\n"
        },
        {
          "text": "Diviser, régner, combiner",
          "correct": true,
          "feedback": "Bonne réponse : on divise le problème en sous-problèmes\nplus petits, on les résout (récursivement), puis on\ncombine les solutions partielles pour obtenir la solution\ndu problème initial.\n"
        },
        {
          "text": "Trier, fusionner, comparer",
          "correct": false,
          "feedback": "Erreur : ces opérations apparaissent dans le tri fusion\n(un cas particulier), mais ne sont pas les trois étapes\ngénérales du paradigme.\n"
        },
        {
          "text": "Identifier, calculer, vérifier",
          "correct": false,
          "feedback": "Erreur : ces verbes décrivent une démarche scientifique\ngénérale, pas la stratégie diviser pour régner.\n"
        }
      ],
      "explanation": "Le paradigme repose sur l'idée que résoudre un sous-problème\nplus petit est plus simple, et que combiner les solutions\npartielles donne efficacement la solution complète. C'est\nl'idée mère de la recherche dichotomique, du tri fusion, de\nl'exponentiation rapide, etc."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "dichotomie",
        "complexite"
      ],
      "title": "Complexité de la recherche dichotomique",
      "statement": "Quelle est la complexité au pire de la recherche dichotomique\nsur un tableau trié de $n$ éléments ?",
      "options": [
        {
          "text": "$O(\\log n)$",
          "correct": true,
          "feedback": "Bonne réponse : à chaque étape, l'intervalle de recherche\nest divisé par deux. Le nombre d'étapes est donc le\nlogarithme en base $2$ de $n$.\n"
        },
        {
          "text": "$O(n \\log n)$",
          "correct": false,
          "feedback": "Erreur : c'est la complexité du tri fusion, pas d'une\nrecherche.\n"
        },
        {
          "text": "$O(n)$",
          "correct": false,
          "feedback": "Erreur : c'est la complexité de la recherche **linéaire**.\nLa dichotomie est plus rapide grâce au tri préalable.\n"
        },
        {
          "text": "$O(1)$",
          "correct": false,
          "feedback": "Erreur : ce serait une recherche en temps constant, ce\nqui n'est pas réaliste pour rechercher un élément dans\nun tableau de taille variable.\n"
        }
      ],
      "explanation": "Pour $n = 10^9$, la dichotomie effectue au plus $30$\ncomparaisons. C'est l'un des gains algorithmiques les plus\nspectaculaires : passer de linéaire à logarithmique."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "tri-fusion",
        "principe"
      ],
      "title": "Principe du tri fusion",
      "statement": "Quel est le principe du **tri fusion** ?",
      "options": [
        {
          "text": "Chercher le plus petit élément, le placer en début, recommencer",
          "correct": false,
          "feedback": "Erreur : c'est le principe du tri par sélection.\n"
        },
        {
          "text": "Couper en deux moitiés, trier chacune récursivement, puis les fusionner",
          "correct": true,
          "feedback": "Bonne réponse : c'est l'application directe du paradigme\ndiviser pour régner. La fusion de deux tableaux triés\nse fait en temps linéaire en parcourant les deux\nsimultanément.\n"
        },
        {
          "text": "Insérer chaque élément à sa place dans la portion déjà triée",
          "correct": false,
          "feedback": "Erreur : c'est le principe du tri par insertion.\n"
        },
        {
          "text": "Comparer chaque paire d'éléments adjacents et les permuter si nécessaire",
          "correct": false,
          "feedback": "Erreur : c'est le principe du tri à bulles, en $O(n^2)$.\n"
        }
      ],
      "explanation": "Le tri fusion est l'archétype du tri par diviser pour régner.\nSa complexité $O(n \\log n)$ ne dépend pas de l'ordre initial,\nce qui en fait un tri à comportement **prévisible**."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "tri-fusion",
        "complexite"
      ],
      "title": "Complexité du tri fusion",
      "statement": "Quelle est la complexité du tri fusion sur un tableau de $n$\néléments ?",
      "options": [
        {
          "text": "$O(n \\log n)$",
          "correct": true,
          "feedback": "Bonne réponse : il y a $\\log n$ niveaux de récursion (on\ndivise par deux à chaque niveau). À chaque niveau, on\nparcourt l'ensemble des $n$ éléments lors des fusions.\nTotal : $n \\log n$.\n"
        },
        {
          "text": "$O(n^2)$",
          "correct": false,
          "feedback": "Erreur : c'est la complexité des tris naïfs (sélection,\ninsertion, bulles). Le tri fusion est meilleur grâce au\nparadigme DPR.\n"
        },
        {
          "text": "$O(2^n)$",
          "correct": false,
          "feedback": "Erreur : aucun tri usuel n'est exponentiel.\n"
        },
        {
          "text": "$O(n)$",
          "correct": false,
          "feedback": "Erreur : la fusion seule est en $O(n)$, mais on la\nrépète sur $\\log n$ niveaux de récursion, ce qui donne\n$O(n \\log n)$ au total.\n"
        }
      ],
      "explanation": "Le tri fusion est **stable** (préserve l'ordre des éléments\négaux) et a une complexité **garantie** $O(n \\log n)$ dans\ntous les cas, ce qui le rend prédictible. Son inconvénient\nprincipal : il n'est **pas en place**."
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "tri-fusion",
        "base"
      ],
      "title": "Cas de base du tri fusion",
      "statement": "Quel est le **cas de base** du tri fusion ?",
      "options": [
        {
          "text": "Un tableau de deux éléments",
          "correct": false,
          "feedback": "Erreur : un tableau de deux éléments peut être mal\ntrié. Il faut soit le diviser à nouveau, soit le trier\ndirectement (cas particulier optimisé).\n"
        },
        {
          "text": "Un tableau trié",
          "correct": false,
          "feedback": "Erreur : on ne sait pas a priori si le tableau est trié\n(sans le parcourir). Le cas de base du tri fusion est\nbasé sur la **taille**, pas sur l'ordre.\n"
        },
        {
          "text": "Un tableau de longueur paire",
          "correct": false,
          "feedback": "Erreur : la parité ne garantit pas qu'un tableau soit\ntrié. Le tri fusion fonctionne pour toute parité.\n"
        },
        {
          "text": "Un tableau de zéro ou un élément",
          "correct": true,
          "feedback": "Bonne réponse : un tableau vide ou de longueur $1$ est\ndéjà trié par définition. La récursion s'arrête à ce\nniveau, sans nouvelle découpe ni fusion.\n"
        }
      ],
      "explanation": "Choisir un cas de base à $0$ ou $1$ élément simplifie le\ncode : on n'a même pas besoin de gérer la division pour ces\ncas. Cela rend la fonction parfaitement récursive."
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "exponentiation-rapide"
      ],
      "title": "Principe de l'exponentiation rapide",
      "statement": "L'**exponentiation rapide** calcule $a^n$ en exploitant\nquelle propriété mathématique ?",
      "options": [
        {
          "text": "$a^n = a^{n-1} \\cdot a$ si $n \\geq 1$, avec $a^0 = 1$",
          "correct": false,
          "feedback": "Erreur : c'est la définition récursive **naïve** de la\npuissance, en $O(n)$. L'exponentiation rapide va beaucoup\nplus vite.\n"
        },
        {
          "text": "$a^n = a + a + \\cdots + a$ ($n$ fois)",
          "correct": false,
          "feedback": "Erreur : c'est la définition de la multiplication, pas\nde la puissance. L'exponentiation rapide ne peut pas\npartir d'une somme.\n"
        },
        {
          "text": "$a^n = (a^{n/2})^2$ si $n$ est pair, et $a^n = a \\cdot (a^{(n-1)/2})^2$ si $n$ est impair",
          "correct": true,
          "feedback": "Bonne réponse : on divise l'exposant par deux à chaque\nétape (au lieu de soustraire $1$). Cela amène la\ncomplexité de $O(n)$ à $O(\\log n)$.\n"
        },
        {
          "text": "$a^n = (a^2)^{n/2}$ si $n$ est pair, et $a^n = (a^2)^{(n-1)/2}$ si $n$ est impair",
          "correct": false,
          "feedback": "Erreur : le cas pair est juste, mais le cas impair oublie\nle facteur $a$ restant : $(a^2)^{(n-1)/2} = a^{n-1}$, pas\n$a^n$. Il faut multiplier par $a$ une dernière fois.\n"
        }
      ],
      "explanation": "Pour calculer $a^{20}$ : la méthode naïve fait $19$\nmultiplications, l'exponentiation rapide en fait environ\n$\\log_2(20) \\approx 5$. Le gain devient énorme pour de\ngrands exposants (cryptographie, arithmétique modulaire)."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "tri-fusion",
        "place"
      ],
      "title": "Tri fusion en place ?",
      "statement": "Le tri fusion est-il un tri **en place** ?",
      "options": [
        {
          "text": "Oui, il modifie le tableau d'origine sans mémoire supplémentaire",
          "correct": false,
          "feedback": "Erreur : le tri fusion utilise des tableaux auxiliaires\n(les moitiés gauche et droite, puis le résultat fusionné).\nSa mémoire supplémentaire est en $O(n)$.\n"
        },
        {
          "text": "Non, il utilise une mémoire supplémentaire proportionnelle à $n$",
          "correct": true,
          "feedback": "Bonne réponse : à chaque niveau de récursion, on crée des\ntableaux pour les moitiés et la fusion. C'est l'un des\ninconvénients du tri fusion, contrairement au tri rapide\nou au tri par insertion.\n"
        },
        {
          "text": "Oui, mais seulement pour des listes de taille paire",
          "correct": false,
          "feedback": "Erreur : la parité n'a aucune influence sur le caractère\nen place ou non.\n"
        },
        {
          "text": "Non, mais il utilise une mémoire constante",
          "correct": false,
          "feedback": "Erreur : la mémoire supplémentaire n'est pas constante,\nelle est proportionnelle à la taille du tableau.\n"
        }
      ],
      "explanation": "Tris en place : par sélection, par insertion, à bulles, tri\nrapide. Tri **non en place** : tri fusion (mémoire\n$O(n)$). Pour de grandes données, cet inconvénient peut\nfaire préférer le tri rapide."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "dichotomie",
        "prerequis"
      ],
      "title": "Pré-requis de la recherche dichotomique",
      "statement": "Quelle est la condition **indispensable** pour appliquer la\nrecherche dichotomique ?",
      "options": [
        {
          "text": "Le tableau doit être trié",
          "correct": true,
          "feedback": "Bonne réponse : la dichotomie compare l'élément cherché\nau milieu pour décider d'aller à gauche ou à droite. Si\nle tableau n'est pas trié, cette comparaison ne donne\naucune information utile, et l'algorithme peut renvoyer\nun faux négatif.\n"
        },
        {
          "text": "Le tableau doit être de taille puissance de $2$",
          "correct": false,
          "feedback": "Erreur : la taille n'a pas besoin d'être une puissance\nde $2$. La dichotomie fonctionne pour toute taille\n$\\geq 0$.\n"
        },
        {
          "text": "Le tableau doit avoir une taille connue à l'avance",
          "correct": false,
          "feedback": "Erreur : c'est une caractéristique des tableaux Python\nou C en général, pas une contrainte de la dichotomie.\n"
        },
        {
          "text": "Le tableau doit contenir uniquement des entiers",
          "correct": false,
          "feedback": "Erreur : la dichotomie fonctionne sur tout type\ncomparable (entiers, flottants, chaînes, dates, etc.).\n"
        }
      ],
      "explanation": "Si la liste n'est pas triée et que les recherches sont\noccasionnelles, mieux vaut faire une recherche linéaire\nqu'un tri préalable. La dichotomie est rentable quand on\nprévoit de très nombreuses recherches sur la même liste."
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "recursivite-lien"
      ],
      "title": "Récursivité et diviser pour régner",
      "statement": "Pourquoi le paradigme diviser pour régner s'implémente-t-il\nnaturellement par **récursivité** ?",
      "options": [
        {
          "text": "Parce que la récursivité est le seul moyen de coder une boucle en Python",
          "correct": false,
          "feedback": "Erreur : Python permet aussi des boucles `for` et\n`while`. La récursivité est un choix, pas une\nobligation.\n"
        },
        {
          "text": "Parce que ce paradigme nécessite obligatoirement le recours à la mémoïsation",
          "correct": false,
          "feedback": "Erreur : la mémoïsation concerne la programmation\ndynamique, pas le diviser pour régner classique.\n"
        },
        {
          "text": "Parce que la récursivité est plus rapide que l'itération",
          "correct": false,
          "feedback": "Erreur : la récursivité est en réalité souvent un peu\n**plus lente** (surcoût des appels) en Python. Son\nintérêt est la lisibilité, pas la vitesse.\n"
        },
        {
          "text": "Parce que les sous-problèmes sont de même nature, ce qui s'exprime par récursivité",
          "correct": true,
          "feedback": "Bonne réponse : la définition même du paradigme implique\nque les sous-problèmes sont de même structure que le\nproblème principal. Une fonction qui s'appelle\nelle-même sur le sous-problème est l'expression la plus\ndirecte.\n"
        }
      ],
      "explanation": "Certains algorithmes DPR ont des versions itératives plus\nefficaces (recherche dichotomique itérative, exponentiation\nrapide itérative), mais la version récursive est souvent\nplus claire pédagogiquement."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "tri-fusion",
        "fusion"
      ],
      "title": "Fusion de deux listes triées",
      "statement": "Pour fusionner deux listes **déjà triées** de longueur $n$\nchacune en une seule liste triée, quelle est la complexité\nde l'opération ?",
      "options": [
        {
          "text": "$O(n^2)$",
          "correct": false,
          "feedback": "Erreur : on n'a pas besoin de comparer chaque élément à\ntous les autres, seulement aux candidats en tête de\nchaque liste.\n"
        },
        {
          "text": "$O(n \\log n)$",
          "correct": false,
          "feedback": "Erreur : c'est la complexité du tri fusion **complet**,\npas de la seule fusion. La fusion est l'étape\n« combiner » du paradigme.\n"
        },
        {
          "text": "$O(n)$",
          "correct": true,
          "feedback": "Bonne réponse : on parcourt les deux listes\nsimultanément avec un index pour chacune. Chaque\ncomparaison fait avancer l'un des deux index. Au total,\non fait $2n$ pas, soit $O(n)$.\n"
        },
        {
          "text": "$O(1)$",
          "correct": false,
          "feedback": "Erreur : il faut quand même parcourir les éléments des\ndeux listes au moins une fois pour les disposer dans\nl'ordre.\n"
        }
      ],
      "explanation": "C'est précisément cette fusion en $O(n)$ par niveau, répétée\nsur $\\log n$ niveaux, qui donne la complexité globale en\n$O(n \\log n)$ du tri fusion."
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "dichotomie",
        "evaluation"
      ],
      "title": "Nombre de comparaisons en dichotomie",
      "statement": "Combien de comparaisons effectue **au pire** une recherche\ndichotomique sur un tableau de $1\\ 000$ éléments ?",
      "options": [
        {
          "text": "$1$",
          "correct": false,
          "feedback": "Erreur : on ne peut pas garantir une seule comparaison\ndans le pire cas. Le pire cas atteint $\\lceil \\log_2 n \\rceil$\ncomparaisons.\n"
        },
        {
          "text": "$500$",
          "correct": false,
          "feedback": "Erreur : la première étape de la dichotomie élimine la\nmoitié des candidats, mais on continue à diviser\nensuite. Le total est bien plus faible que $500$.\n"
        },
        {
          "text": "$1\\ 000$",
          "correct": false,
          "feedback": "Erreur : c'est le nombre maximal de comparaisons d'une\nrecherche **linéaire**. La dichotomie est bien plus\nrapide.\n"
        },
        {
          "text": "$10$",
          "correct": true,
          "feedback": "Bonne réponse : $\\log_2(1\\ 000) \\approx 9{,}97$, soit\nenviron $10$. Au pire, $10$ comparaisons suffisent pour\ntrouver l'élément ou conclure à son absence.\n"
        }
      ],
      "explanation": "Repère utile : doubler la taille n'ajoute qu'**une**\ncomparaison. Pour passer de $1\\ 000$ à $2\\ 000$, on passe de\n$10$ à $11$ comparaisons. C'est ce qui rend les algorithmes\nlogarithmiques exceptionnellement rapides à grande échelle."
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "tri-fusion",
        "deroulement"
      ],
      "title": "Tri fusion sur un exemple",
      "statement": "Si l'on applique le tri fusion à la liste $[5, 2, 8, 3, 9, 1]$,\nquelle est la première étape ?",
      "options": [
        {
          "text": "Diviser la liste en $[5, 2, 8]$ et $[3, 9, 1]$",
          "correct": true,
          "feedback": "Bonne réponse : on coupe la liste au milieu en deux\nsous-listes de taille (presque) égale. Chaque moitié\nsera triée récursivement avant la fusion finale.\n"
        },
        {
          "text": "Trouver le plus petit élément ($1$) et le placer en début",
          "correct": false,
          "feedback": "Erreur : c'est le tri par sélection. Le tri fusion ne\ncherche pas le minimum, il découpe la liste en deux.\n"
        },
        {
          "text": "Trier la liste avec une fonction `sorted` intégrée",
          "correct": false,
          "feedback": "Erreur : utiliser `sorted` éviterait l'algorithme\nétudié. La question porte sur le déroulement du tri\nfusion lui-même.\n"
        },
        {
          "text": "Comparer $5$ et $2$, les permuter",
          "correct": false,
          "feedback": "Erreur : c'est l'opération du tri à bulles. Le tri\nfusion ne fait pas de comparaisons adjacentes au début.\n"
        }
      ],
      "explanation": "Après division récursive, on obtient des sous-listes de\ntaille $1$ (déjà triées). Puis les fusions remontent en\nreconstituant des sous-listes triées de plus en plus\ngrandes : $[2, 5]$, $[2, 5, 8]$, etc. jusqu'au tableau\ncomplet trié."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "exponentiation-rapide",
        "complexite"
      ],
      "title": "Complexité de l'exponentiation rapide",
      "statement": "Quelle est la complexité de l'exponentiation rapide pour\ncalculer $a^n$ ?",
      "options": [
        {
          "text": "$O(1)$",
          "correct": false,
          "feedback": "Erreur : la complexité dépend bien de $n$. Pour $n$\ntrès grand, le calcul nécessite plusieurs étapes.\n"
        },
        {
          "text": "$O(n)$",
          "correct": false,
          "feedback": "Erreur : c'est la complexité de la méthode **naïve**\n($a^n = a \\cdot a^{n-1}$, $n - 1$ multiplications).\nL'exponentiation rapide divise par deux au lieu de\ndécrémenter de $1$.\n"
        },
        {
          "text": "$O(\\log n)$",
          "correct": true,
          "feedback": "Bonne réponse : à chaque appel récursif, on divise\nl'exposant par deux. Il y a donc environ $\\log_2 n$\nniveaux d'appels, donc autant de multiplications.\n"
        },
        {
          "text": "$O(n^2)$",
          "correct": false,
          "feedback": "Erreur : aucune raison d'avoir une complexité quadratique\npour ce calcul.\n"
        }
      ],
      "explanation": "Pour $n = 10^{18}$, la méthode naïve ferait $10^{18} - 1$\nmultiplications (impossible). L'exponentiation rapide n'en\nfait qu'une soixantaine. Indispensable en cryptographie\nasymétrique (RSA)."
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "recurrence",
        "calcul"
      ],
      "title": "Récurrence du tri fusion",
      "statement": "Soit $T(n)$ le nombre d'opérations du tri fusion sur un\ntableau de $n$ éléments. Quelle relation de récurrence\nvérifie $T(n)$ ?",
      "options": [
        {
          "text": "$T(n) = T(n/2) + 1$",
          "correct": false,
          "feedback": "Erreur : c'est la récurrence de la **dichotomie** (un\nseul appel récursif sur la moitié). Le tri fusion en\nfait deux.\n"
        },
        {
          "text": "$T(n) = T(n-1) + 1$",
          "correct": false,
          "feedback": "Erreur : cette récurrence donne $T(n) = O(n)$, ce qui\nest trop optimiste pour le tri fusion.\n"
        },
        {
          "text": "$T(n) = 2 \\cdot T(n/2) + n$",
          "correct": true,
          "feedback": "Bonne réponse : on appelle deux fois la fonction sur des\nmoitiés (les $2 \\cdot T(n/2)$), puis on fusionne en\n$O(n)$ opérations. Le théorème maître donne\n$T(n) = O(n \\log n)$.\n"
        },
        {
          "text": "$T(n) = T(n-1) + T(n-2)$",
          "correct": false,
          "feedback": "Erreur : c'est la récurrence de Fibonacci. Le tri\nfusion ne dépend pas des deux étapes précédentes.\n"
        }
      ],
      "explanation": "Cette récurrence est l'archétype du « théorème maître » en\nanalyse de complexité : elle apparaît dans tous les\nalgorithmes DPR qui divisent en deux et combinent en\n$O(n)$."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "comparaison"
      ],
      "title": "Tri fusion vs tri par insertion",
      "statement": "Pour trier un tableau de $1\\ 000\\ 000$ éléments, le tri\nfusion ($O(n \\log n)$) est environ combien de fois plus\nrapide que le tri par insertion ($O(n^2)$) ?",
      "options": [
        {
          "text": "$50\\ 000$ fois",
          "correct": true,
          "feedback": "Bonne réponse : pour $n = 10^6$, $n^2 = 10^{12}$ et\n$n \\log_2 n \\approx 2 \\cdot 10^7$. Le rapport est\nenviron $50\\ 000$. C'est la différence entre une seconde\net $14$ heures sur la même machine.\n"
        },
        {
          "text": "$1\\ 000\\ 000$ fois",
          "correct": false,
          "feedback": "Erreur : ce serait le rapport $n^2 / n = n$, mais on\ndivise par $n \\log n$, pas par $n$.\n"
        },
        {
          "text": "$2$ fois",
          "correct": false,
          "feedback": "Erreur : la différence est bien plus grande. $\\log_2 n$\nn'est qu'une fraction de $n$ pour de grandes valeurs.\n"
        },
        {
          "text": "$10$ fois",
          "correct": false,
          "feedback": "Erreur : la différence est de plusieurs ordres de\ngrandeur, pas un seul facteur $10$.\n"
        }
      ],
      "explanation": "Cette différence d'ordre de grandeur explique pourquoi le\nchoix de l'algorithme est crucial. Sur petites entrées, les\ntris simples sont compétitifs ; sur grandes entrées, les\ntris en $O(n \\log n)$ deviennent indispensables."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "exponentiation-rapide",
        "optimisation"
      ],
      "title": "Optimisation par mémoïsation",
      "statement": "Dans une implémentation **récursive** d'exponentiation\nrapide, est-il utile d'ajouter de la **mémoïsation** ?",
      "options": [
        {
          "text": "Oui, la mémoïsation accélère nettement le calcul récursif",
          "correct": false,
          "feedback": "Erreur : à chaque appel, on calcule une seule valeur\nintermédiaire (`demi = puissance(a, n // 2)`), pas la\nmême plusieurs fois. La mémoïsation n'apporterait rien.\n"
        },
        {
          "text": "Non, l'exponentiation rapide ne fait pas d'appels redondants",
          "correct": true,
          "feedback": "Bonne réponse : à chaque appel, le sous-problème est\nunique (un seul exposant intermédiaire est calculé). Il\nn'y a donc aucun calcul redondant, contrairement à\nFibonacci naïf.\n"
        },
        {
          "text": "Oui, mais seulement pour des exposants pairs",
          "correct": false,
          "feedback": "Erreur : la mémoïsation n'est utile que s'il y a des\nappels redondants, ce qui n'est pas le cas ici.\n"
        },
        {
          "text": "Cela rendrait l'algorithme plus lent",
          "correct": false,
          "feedback": "Erreur : la mémoïsation seule ne ralentit pas\nsignificativement, mais l'avantage est nul ici.\n"
        }
      ],
      "explanation": "La distinction est cruciale : le diviser pour régner peut\navoir des sous-problèmes redondants (cas de Fibonacci) ou\nuniques (cas de l'exponentiation rapide). Seul le premier\nbénéficie de la mémoïsation."
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "dichotomie",
        "code"
      ],
      "title": "Détection d'une erreur dans la dichotomie",
      "statement": "Dans la fonction suivante :\n\n```\ndef recherche(x, tab):\n    gauche, droite = 0, len(tab) - 1\n    while gauche < droite:        # ← ICI\n        milieu = (gauche + droite) // 2\n        if tab[milieu] == x:\n            return milieu\n        elif tab[milieu] < x:\n            gauche = milieu + 1\n        else:\n            droite = milieu - 1\n    return -1\n```\n\nQuel est le bug ?",
      "options": [
        {
          "text": "Il manque l'initialisation de la variable `milieu` juste avant la boucle",
          "correct": false,
          "feedback": "Erreur : `milieu` est défini à l'intérieur de la boucle,\nce qui est la convention habituelle.\n"
        },
        {
          "text": "La condition de boucle devrait être `gauche <= droite`, pas `<`",
          "correct": true,
          "feedback": "Bonne réponse : avec `<` strict, on n'examine pas le\ndernier élément quand `gauche == droite`. La fonction\npeut renvoyer $-1$ pour un élément qui est en réalité\nprésent.\n"
        },
        {
          "text": "Le calcul de `milieu` est incorrect",
          "correct": false,
          "feedback": "Erreur : le calcul `(gauche + droite) // 2` est\nstandard et correct. Le bug est ailleurs.\n"
        },
        {
          "text": "La fonction devrait être récursive pour fonctionner",
          "correct": false,
          "feedback": "Erreur : la dichotomie peut s'écrire en itératif comme\nen récursif. La forme itérative est même souvent\npréférée.\n"
        }
      ],
      "explanation": "Erreur subtile et classique. Test concret : avec\n`tab = [5]` et `x = 5`, on a `gauche = droite = 0`, la\ncondition `gauche < droite` est fausse, on sort\nimmédiatement et on renvoie $-1$ alors que $x$ est présent."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "tri-rapide",
        "approfondissement"
      ],
      "title": "Tri rapide vs tri fusion",
      "statement": "Quelle est la principale différence entre le **tri rapide**\n(*quicksort*) et le tri fusion ?",
      "options": [
        {
          "text": "Le tri rapide est toujours plus rapide que le tri fusion, quelle que soit l'entrée",
          "correct": false,
          "feedback": "Erreur : ce n'est pas systématique. Sur certaines\nentrées (déjà triées avec mauvais pivot), il est même\nbeaucoup plus lent.\n"
        },
        {
          "text": "Le tri rapide travaille en place, le tri fusion garantit $O(n \\log n)$ au pire",
          "correct": true,
          "feedback": "Bonne réponse : le tri rapide partitionne autour d'un\npivot et est en place, mais avec un mauvais pivot, il\ndégénère à $O(n^2)$ au pire. Le tri fusion garantit\n$O(n \\log n)$ dans tous les cas, mais il n'est pas en\nplace et utilise plus de mémoire.\n"
        },
        {
          "text": "Le tri rapide est nettement plus simple à comprendre et à coder que le tri fusion",
          "correct": false,
          "feedback": "Erreur : ces deux algorithmes ont une difficulté\ncomparable. Ce n'est pas la raison de leur distinction.\n"
        },
        {
          "text": "Le tri rapide ne fonctionne que sur des entiers, le tri fusion sur tout type comparable",
          "correct": false,
          "feedback": "Erreur : les deux fonctionnent sur tout type comparable.\n"
        }
      ],
      "explanation": "Choisir entre les deux dépend du contexte : tri rapide pour\ngagner de la mémoire (et accepter le risque du pire cas),\ntri fusion pour la prévisibilité. Les bibliothèques\nstandard utilisent souvent des tris hybrides comme\nTimsort."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "diviser",
        "decoupage"
      ],
      "title": "Découpage déséquilibré",
      "statement": "Que se passe-t-il si le découpage en sous-problèmes est très\n**déséquilibré** (par exemple, une moitié contenant $1$\nélément et l'autre $n-1$ éléments) ?",
      "options": [
        {
          "text": "La complexité reste $O(n)$",
          "correct": false,
          "feedback": "Erreur : un découpage déséquilibré ne réduit pas la\ntaille suffisamment vite pour rester en $O(n)$.\n"
        },
        {
          "text": "La complexité reste en $O(n \\log n)$",
          "correct": false,
          "feedback": "Erreur : la complexité $O(n \\log n)$ suppose un\ndécoupage **équilibré** (en deux moitiés de même taille).\nUn découpage déséquilibré dégrade fortement la\nperformance.\n"
        },
        {
          "text": "La complexité dégénère à $O(n^2)$",
          "correct": true,
          "feedback": "Bonne réponse : avec ce découpage, on a $n$ niveaux de\nrécursion (au lieu de $\\log n$), chacun coûtant $O(n)$.\nLe total devient $O(n^2)$. C'est exactement ce qui se\nproduit avec un mauvais pivot dans le tri rapide.\n"
        },
        {
          "text": "L'algorithme ne termine pas",
          "correct": false,
          "feedback": "Erreur : tant que le découpage diminue strictement la\ntaille du sous-problème, l'algorithme termine. Le\nproblème est la **performance**, pas la terminaison.\n"
        }
      ],
      "explanation": "C'est pour cette raison que le choix du **pivot** est\ncrucial dans le tri rapide. Des stratégies sont utilisées\n(médian de trois, pivot aléatoire) pour éviter les pires\ncas pratiques."
    },
    {
      "id": "q22",
      "difficulty": 3,
      "skills": [
        "recursivite",
        "pile"
      ],
      "title": "Profondeur de la pile en tri fusion",
      "statement": "Pour trier un tableau de $1\\ 000$ éléments par tri fusion\nrécursif, quelle est la **profondeur maximale** de la pile\nd'appels ?",
      "options": [
        {
          "text": "$500$",
          "correct": false,
          "feedback": "Erreur : confusion possible avec « la moitié ». La\nprofondeur n'est pas $n/2$ mais bien $\\log n$.\n"
        },
        {
          "text": "$10$",
          "correct": true,
          "feedback": "Bonne réponse : $\\log_2(1\\ 000) \\approx 10$ (environ).\nLa pile d'appels reste donc très peu profonde, ce qui\névite les `RecursionError` même pour de grandes entrées.\n"
        },
        {
          "text": "$1$",
          "correct": false,
          "feedback": "Erreur : un seul appel ne suffit pas à diviser\n$1\\ 000$ éléments jusqu'au cas de base.\n"
        },
        {
          "text": "$1\\ 000$",
          "correct": false,
          "feedback": "Erreur : on ne descend pas $1\\ 000$ niveaux. Le\ndécoupage divise par deux à chaque niveau, donc la\nprofondeur croît comme $\\log n$.\n"
        }
      ],
      "explanation": "C'est une autre force du diviser pour régner avec\ndécoupage équilibré : la profondeur logarithmique évite\ntout problème de débordement de pile, même pour des\nmilliards d'éléments."
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "exponentiation",
        "application"
      ],
      "title": "Exponentiation modulaire",
      "statement": "Pour calculer $a^n \\mod m$ avec de très grands entiers\n(cryptographie), la méthode efficace est :",
      "options": [
        {
          "text": "Appliquer l'exponentiation rapide en réduisant modulo $m$ à chaque étape",
          "correct": true,
          "feedback": "Bonne réponse : $(a \\cdot b) \\mod m = ((a \\mod m) \\cdot (b \\mod m)) \\mod m$,\ndonc on peut réduire à chaque étape sans changer le\nrésultat final. Cela maintient les entiers\nintermédiaires petits ($< m^2$), tout en bénéficiant\nde la complexité $O(\\log n)$.\n"
        },
        {
          "text": "Calculer $a^n$ entièrement, puis prendre le modulo",
          "correct": false,
          "feedback": "Erreur : pour $a = 1024$ et $n = 10^9$, $a^n$ a plus\nd'un milliard de chiffres et n'est pas calculable en\npratique. Il faut prendre le modulo à chaque étape.\n"
        },
        {
          "text": "Utiliser une recherche dichotomique sur les diviseurs de $m$",
          "correct": false,
          "feedback": "Erreur : la recherche dichotomique n'a aucun rapport\navec ce calcul.\n"
        },
        {
          "text": "Utiliser un tri fusion appliqué aux chiffres décimaux du nombre",
          "correct": false,
          "feedback": "Erreur : aucun rapport entre tri et exponentiation\nmodulaire.\n"
        }
      ],
      "explanation": "L'exponentiation modulaire rapide est l'opération\nfondamentale du chiffrement RSA. Elle permet de calculer\n$a^n \\mod m$ pour des entiers de plusieurs milliers de\nbits, en quelques millisecondes."
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "analyse"
      ],
      "title": "Analyse d'un nouvel algorithme DPR",
      "statement": "On invente un algorithme qui, pour un tableau de $n$\néléments, le découpe en **trois tiers**, traite chaque tiers\nrécursivement, puis fait un travail $O(n)$ pour combiner.\nQuelle est sa complexité ?",
      "options": [
        {
          "text": "$O(n^3)$",
          "correct": false,
          "feedback": "Erreur : on découpe en trois, mais chaque sous-problème\nfait $1/3$ de la taille originale, pas $n/2$. Le\nrésultat reste $O(n \\log n)$.\n"
        },
        {
          "text": "$O(n)$",
          "correct": false,
          "feedback": "Erreur : la combinaison à elle seule est en $O(n)$, mais\nil y a aussi $\\log_3 n$ niveaux à parcourir.\n"
        },
        {
          "text": "$O(3^n)$",
          "correct": false,
          "feedback": "Erreur : la complexité exponentielle apparaît avec des\nappels qui ne réduisent pas la taille. Ici, on divise\nla taille à chaque étape.\n"
        },
        {
          "text": "$O(n \\log_3 n)$",
          "correct": true,
          "feedback": "Bonne réponse : la récurrence est\n$T(n) = 3 T(n/3) + O(n)$. Le théorème maître donne\n$T(n) = O(n \\log n)$ (le facteur $\\log_3$ étant\néquivalent à $\\log_2$ à une constante près).\n"
        }
      ],
      "explanation": "En général, $a$ sous-problèmes de taille $n/b$ avec une\ncombinaison en $O(n)$ donne $O(n \\log n)$ tant que\n$\\log_b a = 1$. C'est la « zone d'équilibre » du théorème\nmaître."
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Reconnaître un algorithme DPR",
      "statement": "Parmi les algorithmes suivants, lequel **n'est pas** un\nalgorithme diviser pour régner ?",
      "options": [
        {
          "text": "Le tri par insertion",
          "correct": true,
          "feedback": "Bonne réponse : le tri par insertion ne divise pas le\nproblème. Il itère sur les éléments et place chacun à\nsa place dans la portion déjà triée. C'est un\nalgorithme **itératif** classique, pas un DPR.\n"
        },
        {
          "text": "L'exponentiation rapide",
          "correct": false,
          "feedback": "Erreur : c'est un DPR. On réduit l'exposant de moitié\nà chaque appel.\n"
        },
        {
          "text": "La recherche dichotomique",
          "correct": false,
          "feedback": "Erreur : c'est aussi un DPR. On divise l'intervalle de\nrecherche par deux à chaque étape.\n"
        },
        {
          "text": "Le tri fusion",
          "correct": false,
          "feedback": "Erreur : c'est un cas d'école du paradigme. On découpe\nen deux moitiés, on trie chaque moitié récursivement,\non fusionne.\n"
        }
      ],
      "explanation": "Pour identifier un DPR, chercher : (1) un découpage en\nsous-problèmes plus petits de même nature, (2) une\nrésolution récursive, (3) une étape de combinaison. Le tri\npar insertion ne fait pas de découpage récursif, donc ce\nn'est pas un DPR."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "exponentiation-rapide",
        "code"
      ],
      "title": "Code de l'exponentiation rapide",
      "statement": "Quel code Python implémente correctement\nl'exponentiation rapide récursive de $a$ à la\npuissance $n$ (avec $n \\geq 0$) ?",
      "options": [
        {
          "text": "```\ndef puissance(a, n):\n    if n == 0:\n        return 1\n    if n % 2 == 0:\n        return puissance(a, n // 2) * puissance(a, n // 2)\n    return a * puissance(a, n // 2) * puissance(a, n // 2)\n```\n",
          "correct": false,
          "feedback": "Erreur : on calcule **deux fois** la même puissance\nau lieu de la stocker dans une variable. La\ncomplexité retombe à $O(n)$, ce qui annule l'avantage\nde l'exponentiation rapide. Bug classique du\ndébutant.\n"
        },
        {
          "text": "```\ndef puissance(a, n):\n    if n == 0:\n        return 1\n    return a * puissance(a, n - 1)\n```\n",
          "correct": false,
          "feedback": "Erreur : c'est l'exponentiation **naïve**, en\n$O(n)$ multiplications. Elle décrémente l'exposant\nde $1$ à chaque étape au lieu de le diviser par\ndeux. La question demandait l'exponentiation\n**rapide**.\n"
        },
        {
          "text": "```\ndef puissance(a, n):\n    return a ** n\n```\n",
          "correct": false,
          "feedback": "Erreur : on utilise l'opérateur Python intégré, ce\nqui n'illustre pas le paradigme étudié. La question\nportait sur l'écriture **explicite** de\nl'exponentiation rapide.\n"
        },
        {
          "text": "```\ndef puissance(a, n):\n    if n == 0:\n        return 1\n    demi = puissance(a, n // 2)\n    if n % 2 == 0:\n        return demi * demi\n    return a * demi * demi\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : on calcule **une seule fois** la\npuissance pour la moitié de l'exposant, puis on\nmultiplie ce résultat par lui-même (et par `a`\nsupplémentaire si `n` est impair). Complexité\n$O(\\log n)$ multiplications.\n"
        }
      ],
      "explanation": "Idée centrale : sauvegarder le résultat de\n`puissance(a, n // 2)` dans une variable. Sans cette\nmémorisation, l'algorithme dégénère en\nexponentiation naïve. C'est l'illustration la plus\nsimple du diviser pour régner sur un seul\nsous-problème."
    },
    {
      "id": "q27",
      "difficulty": 2,
      "skills": [
        "tri-rapide",
        "partition",
        "approfondissement"
      ],
      "title": "Partition du tri rapide",
      "statement": "Dans le tri rapide, l'opération de\n**partitionnement** autour d'un pivot consiste à :",
      "options": [
        {
          "text": "Placer les éléments inférieurs au pivot à sa\ngauche et les supérieurs à sa droite\n",
          "correct": true,
          "feedback": "Bonne réponse : c'est l'étape clé du tri rapide.\nLe pivot atteint alors sa position définitive dans\nle tableau trié. Une fois le pivot positionné, on\ntrie récursivement chacune des deux parties. La\npartition se fait en $O(n)$ avec un parcours\nlinéaire.\n"
        },
        {
          "text": "Couper le tableau en deux moitiés de même taille,\nsans regarder les valeurs qu'il contient\n",
          "correct": false,
          "feedback": "Erreur : c'est ce que fait le tri **fusion**, pas\nle tri rapide. Le tri rapide partitionne autour\nd'une valeur (le pivot), pas autour de la position\nmédiane. Les deux parties peuvent être de tailles\ntrès différentes.\n"
        },
        {
          "text": "Compter le nombre d'inversions présentes dans le\ntableau afin de choisir le meilleur pivot\n",
          "correct": false,
          "feedback": "Erreur : compter les inversions est une opération\nd'analyse (elle mesure le désordre d'un tableau),\npas une étape du tri rapide, et le choix du pivot\nne s'appuie pas dessus. Aucun lien avec le\npartitionnement.\n"
        },
        {
          "text": "Trier le tableau au préalable, de façon à pouvoir\nprendre sa médiane comme pivot\n",
          "correct": false,
          "feedback": "Erreur : ce serait absurde, puisque le but du tri\nrapide est précisément de trier ; un tableau déjà\ntrié n'aurait plus besoin de pivot. La partition\nest une étape **préliminaire** au tri, sans tri\npréalable.\n"
        }
      ],
      "explanation": "Conséquence : si le pivot est mal choisi (extremum), la\npartition peut produire deux moitiés très\ndéséquilibrées (par exemple $0$ et $n-1$ éléments), ce\nqui dégrade la complexité à $O(n^2)$. C'est pourquoi\nle choix du pivot (médiane, aléatoire) est crucial."
    },
    {
      "id": "q28",
      "difficulty": 3,
      "skills": [
        "theoreme-maitre",
        "application"
      ],
      "title": "DPR sans combinaison coûteuse",
      "statement": "La recherche dichotomique satisfait la récurrence\n$T(n) = T(n/2) + O(1)$. Pourquoi obtient-on\n$O(\\log n)$ et non $O(n \\log n)$ comme dans le tri\nfusion ?",
      "options": [
        {
          "text": "Parce qu'il n'y a qu'un seul sous-problème par\nappel et que la combinaison est en temps constant\n",
          "correct": true,
          "feedback": "Bonne réponse : la dichotomie ne fait qu'**un seul**\nappel récursif (sur la moitié pertinente, au lieu\nde deux) et la combinaison se résume à un test\nd'égalité ou de comparaison, en $O(1)$ au lieu de\n$O(n)$. La somme géométrique des coûts donne\n$O(\\log n)$. Au tri fusion, on a deux appels et une\nfusion en $O(n)$, ce qui donne $O(n \\log n)$.\n"
        },
        {
          "text": "Parce que la recherche dichotomique ne relève pas\ndu paradigme diviser pour régner\n",
          "correct": false,
          "feedback": "Erreur : elle relève bien du paradigme. Elle\ndivise l'espace de recherche par deux à chaque\nappel, illustration classique du DPR.\n"
        },
        {
          "text": "Parce que la recherche dichotomique fonctionne\nuniquement sur des tableaux de taille puissance de\ndeux\n",
          "correct": false,
          "feedback": "Erreur : la recherche dichotomique fonctionne pour\ntoute taille. Cette propriété n'a aucun rapport\navec sa complexité.\n"
        },
        {
          "text": "Parce que les comparaisons entre éléments sont\ngratuites en Python et ne comptent pas dans la\ncomplexité\n",
          "correct": false,
          "feedback": "Erreur : les comparaisons coûtent du temps comme\ntoute opération et comptent bien dans la\ncomplexité. Mais elles sont en nombre constant à\nchaque appel récursif, ce qui fait la différence\navec une fusion en $O(n)$.\n"
        }
      ],
      "explanation": "Schéma général : pour la récurrence\n$T(n) = a \\cdot T(n/b) + O(n^c)$ : si $a < b^c$, le\ncoût est dominé par la combinaison, complexité\n$O(n^c)$ ; si $a = b^c$, on a $O(n^c \\log n)$ ; si\n$a > b^c$, on a $O(n^{\\log_b a})$. Pour la\ndichotomie : $a = 1$, $b = 2$, $c = 0$, donc\n$a = b^c = 1$, complexité $O(\\log n)$."
    },
    {
      "id": "q29",
      "difficulty": 3,
      "skills": [
        "trace-tri-fusion"
      ],
      "title": "Trace du tri fusion",
      "statement": "On applique le tri fusion à la liste\n`[5, 2, 8, 3]`. Quelles sont les listes\npassées à la fusion finale (la dernière\nétape, qui produit la liste triée\ncomplète) ?",
      "options": [
        {
          "text": "`[2, 3, 5, 8]` et `[]`\n",
          "correct": false,
          "feedback": "Erreur : la fusion combine deux\nmoitiés non vides. Une moitié vide\nsignifierait qu'on a fait un seul tri\nrécursif suivi d'une fusion sans\ncontrepartie, ce qui n'est pas le\ntri fusion.\n"
        },
        {
          "text": "`[5]`, `[2]`, `[8]`, `[3]`\n",
          "correct": false,
          "feedback": "Erreur : ce sont les listes singletons\nobtenues à la fin du découpage. La\nfusion finale ne combine que **deux**\nlistes, pas quatre. Quatre singletons\nimpliqueraient deux fusions\nintermédiaires avant la fusion finale.\n"
        },
        {
          "text": "`[5, 2]` et `[8, 3]`\n",
          "correct": false,
          "feedback": "Erreur : ce sont les deux moitiés\nobtenues après le **premier** découpage,\nmais avant les tris récursifs. À la\nfusion finale, ces moitiés ont déjà\nété triées et sont devenues `[2, 5]`\net `[3, 8]`.\n"
        },
        {
          "text": "`[2, 5]` et `[3, 8]`\n",
          "correct": true,
          "feedback": "Bonne réponse : on découpe d'abord en\n`[5, 2]` et `[8, 3]`. On trie\nrécursivement chaque moitié : `[5, 2]`\ndevient `[2, 5]` après fusion de `[5]`\net `[2]` ; `[8, 3]` devient `[3, 8]`\naprès fusion de `[8]` et `[3]`. La\nfusion finale combine `[2, 5]` et\n`[3, 8]` pour produire `[2, 3, 5, 8]`.\n"
        }
      ],
      "explanation": "Schéma global du tri fusion : une phase\ndescendante de découpage récursif (jusqu'aux\nsingletons), suivie d'une phase remontante\nde fusions successives. Pour $n = 4$, la\nstructure d'appels forme un arbre binaire\ncomplet de hauteur $\\log_2 4 = 2$. À chaque\nniveau, le coût total des fusions est en\n$O(n)$, d'où la complexité globale en\n$O(n \\log n)$."
    },
    {
      "id": "q30",
      "difficulty": 3,
      "skills": [
        "code-fusion"
      ],
      "title": "Code de la fonction de fusion",
      "statement": "Quelle fonction Python implémente\ncorrectement la **fusion** de deux listes\ndéjà triées en une seule liste triée, en\ntirant parti du tri préalable (temps\nlinéaire, sans retrier) ?",
      "options": [
        {
          "text": "```\ndef fusion(a, b):\n    i, j = 0, 0\n    resultat = []\n    while i < len(a) and j < len(b):\n        if a[i] <= b[j]:\n            resultat.append(a[i])\n            i += 1\n        else:\n            resultat.append(b[j])\n            j += 1\n    return resultat\n```\n",
          "correct": false,
          "feedback": "Erreur : la boucle s'arrête dès que\nl'une des deux listes est épuisée, et\nles éléments restants de l'autre liste\nsont perdus. Avec `a = [1, 4, 7]` et\n`b = [2, 3]`, on obtient `[1, 2, 3]`\nau lieu de `[1, 2, 3, 4, 7]`. Il faut\nrecopier la fin de la liste non\népuisée après la boucle.\n"
        },
        {
          "text": "```\ndef fusion(a, b):\n    return sorted(a + b)\n```\n",
          "correct": false,
          "feedback": "Cette version fonctionne mais\nn'illustre pas l'algorithme : on délègue\nau tri intégré, qui ne tire pas parti\ndu fait que `a` et `b` sont déjà\ntriées. Complexité : $O((n+m) \\log(n+m))$\nau lieu de $O(n + m)$.\n"
        },
        {
          "text": "```\ndef fusion(a, b):\n    resultat = []\n    for x in a:\n        for y in b:\n            if x <= y:\n                resultat.append(x)\n            else:\n                resultat.append(y)\n    return resultat\n```\n",
          "correct": false,
          "feedback": "Erreur : la double boucle imbriquée a\nune complexité $O(n \\cdot m)$ au lieu\nde $O(n + m)$. De plus, on ajoute des\néléments en double (chaque élément de\n`a` est comparé à chaque élément de\n`b`, et chaque comparaison ajoute une\nvaleur). Le résultat n'est ni trié, ni\nde la bonne taille.\n"
        },
        {
          "text": "```\ndef fusion(a, b):\n    i, j = 0, 0\n    resultat = []\n    while i < len(a) and j < len(b):\n        if a[i] <= b[j]:\n            resultat.append(a[i])\n            i += 1\n        else:\n            resultat.append(b[j])\n            j += 1\n    resultat.extend(a[i:])\n    resultat.extend(b[j:])\n    return resultat\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : on utilise deux indices\n`i` et `j` pour parcourir les deux\nlistes en parallèle. À chaque tour, on\najoute le plus petit des deux éléments\nde tête, et on avance l'indice\ncorrespondant. Quand l'une des listes\nest épuisée, on copie simplement le\nreste de l'autre. Complexité : $O(n + m)$\noù $n$ et $m$ sont les longueurs des\ndeux listes. Le `<=` (plutôt que `<`)\ndans la comparaison garantit la\nstabilité du tri.\n"
        }
      ],
      "explanation": "Cette fonction est le cœur du tri fusion :\nla qualité de l'algorithme global vient\nde l'efficacité linéaire de la fusion.\nVariante intéressante : implémenter la\nfusion **en place** dans la liste\nd'origine, ce qui économise de la\nmémoire. C'est cependant plus délicat à\ncoder et n'apporte pas de gain en\ncomplexité asymptotique."
    },
    {
      "id": "q31",
      "difficulty": 3,
      "skills": [
        "partition-tri-rapide",
        "approfondissement"
      ],
      "title": "Code du partitionnement",
      "statement": "Dans le tri rapide, l'étape de\n**partitionnement** réorganise un tableau\nautour d'un pivot. Quel code Python\nimplémente correctement cette étape (avec\nle pivot pris comme dernier élément du\nsous-tableau, schéma de Lomuto) ?",
      "options": [
        {
          "text": "```\ndef partitionner(tab, debut, fin):\n    gauche = [x for x in tab if x <= tab[fin]]\n    droite = [x for x in tab if x > tab[fin]]\n    return gauche + droite\n```\n",
          "correct": false,
          "feedback": "Erreur : ce code ignore les bornes\n`debut` et `fin` : les compréhensions\nparcourent le tableau **entier**, donc\nchaque appel récursif repartitionne\ntout `tab` au lieu du sous-tableau\nvisé. De plus, il ne modifie pas `tab`\nen place (il renvoie une nouvelle\nliste) et ne renvoie pas l'indice du\npivot : la récursion du tri rapide\nne peut pas fonctionner avec lui.\nCe n'est pas qu'un défaut de mémoire,\nc'est faux dans ce contexte.\n"
        },
        {
          "text": "```\ndef partitionner(tab, debut, fin):\n    pivot = tab[fin]\n    i = debut - 1\n    for j in range(debut, fin):\n        if tab[j] <= pivot:\n            i += 1\n            tab[i], tab[j] = tab[j], tab[i]\n    tab[i + 1], tab[fin] = tab[fin], tab[i + 1]\n    return i + 1\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : c'est le schéma de\nLomuto. L'indice `i` représente la\nfrontière entre les éléments « plus\npetits que le pivot » (avant `i`) et\nceux « plus grands » (entre `i` et\n`j`). À la fin, on place le pivot à sa\nposition définitive (entre les deux\nzones), et la fonction renvoie cette\nposition. Complexité : $O(n)$ pour le\npartitionnement seul, $O(n \\log n)$ en\nmoyenne pour le tri complet.\n"
        },
        {
          "text": "```\ndef partitionner(tab, debut, fin):\n    pivot = tab[fin]\n    i = debut - 1\n    for j in range(debut, fin):\n        if tab[j] <= pivot:\n            i += 1\n            tab[i], tab[j] = tab[j], tab[i]\n    return i + 1\n```\n",
          "correct": false,
          "feedback": "Erreur : il manque l'échange final qui\nplace le pivot à l'indice `i + 1`. Le\npivot reste à l'indice `fin`, alors que\nla fonction annonce `i + 1` comme sa\nposition : cette case contient en\ngénéral un élément plus grand que le\npivot, que la récursion du tri rapide\nne déplacera plus jamais.\n"
        },
        {
          "text": "```\ndef partitionner(tab, debut, fin):\n    pivot = tab[fin]\n    i = debut - 1\n    for j in range(debut, fin):\n        if tab[j] <= pivot:\n            i += 1\n            tab[i], tab[j] = tab[j], tab[i]\n    tab[i], tab[fin] = tab[fin], tab[i]\n    return i\n```\n",
          "correct": false,
          "feedback": "Erreur : le pivot doit aller à l'indice\n`i + 1`, juste après le dernier élément\ninférieur ou égal, et non à l'indice\n`i`. Sur `[5, 1, 3]` (pivot $3$), ce\ncode produit `[3, 5, 1]` et annonce le\npivot à l'indice $0$, alors que $1$ se\ntrouve à sa droite.\n"
        }
      ],
      "explanation": "Le schéma de Lomuto est le plus simple à\nretenir, mais celui de **Hoare** (avec\ndeux indices qui se rapprochent l'un de\nl'autre) est en général un peu plus\nefficace en pratique. Le choix du pivot\nest crucial : avec un mauvais pivot\n(extremum), la complexité dégénère à\n$O(n^2)$. Stratégies courantes : pivot\naléatoire, médiane de trois, etc."
    }
  ]
}