{
  "chapter": {
    "id": "complexite",
    "level": "premiere",
    "theme": "Algorithmique",
    "title": "Complexité algorithmique",
    "description": "Notion de complexité asymptotique, notation O, classes\nclassiques (constante, logarithmique, linéaire, quasi\nlinéaire, quadratique, exponentielle), calcul à partir\ndu code, comparaison expérimentale, échelle de\ncroissance.",
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition"
      ],
      "title": "Définition de la complexité",
      "statement": "Que mesure la **complexité algorithmique** ?",
      "options": [
        {
          "text": "Le nombre de lignes de code source",
          "correct": false,
          "feedback": "Erreur : la complexité algorithmique mesure\nl'évolution des **ressources** (temps,\nmémoire), pas la longueur du code.\n"
        },
        {
          "text": "La quantité de ressources (temps ou mémoire) consommée par un algorithme en fonction de la taille de l'entrée",
          "correct": true,
          "feedback": "Bonne réponse : on étudie comment le coût\névolue lorsque la taille n grandit.\n"
        },
        {
          "text": "La beauté du code",
          "correct": false,
          "feedback": "Subjectif, sans rapport.\n"
        },
        {
          "text": "La difficulté de comprendre le code",
          "correct": false,
          "feedback": "C'est la « complexité cognitive », notion\ndifférente, non quantifiée formellement en\nNSI.\n"
        }
      ],
      "explanation": "Deux types de complexité : **temporelle** (nombre\nd'opérations) et **spatiale** (mémoire utilisée).\nEn NSI Première, on s'intéresse surtout à la\ncomplexité temporelle."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "notation-O"
      ],
      "title": "Notation O",
      "statement": "Que signifie l'écriture `O(n)` (« grand O de n ») ?",
      "options": [
        {
          "text": "L'algorithme effectue exactement `n` opérations",
          "correct": false,
          "feedback": "Erreur : `O(n)` est une borne **asymptotique**,\npas un compte exact. L'algorithme peut faire\n`2n + 5` opérations, on note `O(n)`.\n"
        },
        {
          "text": "Le nombre d'opérations croît proportionnellement à n (à une constante près)",
          "correct": true,
          "feedback": "Bonne réponse : O(n) signifie « linéaire ».\nDoubler n double approximativement le temps.\nLes constantes et les termes d'ordre\ninférieur sont ignorés.\n"
        },
        {
          "text": "L'algorithme utilise n octets de mémoire",
          "correct": false,
          "feedback": "O(n) peut s'appliquer à la mémoire, mais ce\nn'est pas une définition générale.\n"
        },
        {
          "text": "L'algorithme se termine en moins de n secondes",
          "correct": false,
          "feedback": "Erreur : O ne mesure pas un temps absolu mais\nune **proportion**.\n"
        }
      ],
      "explanation": "O(n) caractérise une famille de fonctions qui\ncroissent au plus comme c × n pour une constante\nc. C'est une notation d'ordre de grandeur."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "classes"
      ],
      "title": "Classes classiques",
      "statement": "Parmi les complexités suivantes, laquelle est la\n**plus rapide** asymptotiquement ?",
      "options": [
        {
          "text": "O(n²)",
          "correct": false,
          "feedback": "Erreur : quadratique, l'une des plus lentes\ndans cette liste.\n"
        },
        {
          "text": "O(n log n)",
          "correct": false,
          "feedback": "Bonne, mais pas la plus rapide.\n"
        },
        {
          "text": "O(log n)",
          "correct": true,
          "feedback": "Bonne réponse : logarithmique. Pour n = 10⁶,\nlog₂(n) ≈ 20. C'est la complexité de la\nrecherche dichotomique.\n"
        },
        {
          "text": "O(n)",
          "correct": false,
          "feedback": "Linéaire : plus rapide que quadratique mais\nplus lente que logarithmique.\n"
        }
      ],
      "explanation": "Hiérarchie classique (du plus rapide au plus\nlent) : O(1) < O(log n) < O(n) < O(n log n) <\nO(n²) < O(n³) < O(2ⁿ) < O(n!)."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "linear"
      ],
      "title": "Boucle simple",
      "statement": "Quelle est la complexité de cette fonction en\nfonction de `n = len(liste)` ?\n```python\ndef somme(liste):\n    total = 0\n    for x in liste:\n        total += x\n    return total\n```",
      "options": [
        {
          "text": "O(log n)",
          "correct": false,
          "feedback": "Erreur : aucun mécanisme dichotomique ici.\n"
        },
        {
          "text": "O(1)",
          "correct": false,
          "feedback": "Erreur : la boucle dépend de la taille de\nla liste.\n"
        },
        {
          "text": "O(n)",
          "correct": true,
          "feedback": "Bonne réponse : une boucle simple sur n\néléments avec une opération constante par\nitération donne O(n).\n"
        },
        {
          "text": "O(n²)",
          "correct": false,
          "feedback": "Erreur : pas de double boucle.\n"
        }
      ],
      "explanation": "Règle pratique : une boucle imbriquée 0 fois\n→ O(1) ; 1 fois → O(n) ; 2 fois → O(n²) ; etc."
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "quadratic"
      ],
      "title": "Double boucle",
      "statement": "Quelle est la complexité de cette fonction ?\n```python\ndef doublons(liste):\n    n = len(liste)\n    for i in range(n):\n        for j in range(n):\n            if i != j and liste[i] == liste[j]:\n                return True\n    return False\n```",
      "options": [
        {
          "text": "O(n)",
          "correct": false,
          "feedback": "Erreur : le `for j in range(n)` imbriqué\najoute un facteur n.\n"
        },
        {
          "text": "O(1)",
          "correct": false,
          "feedback": "Erreur : double boucle, donc dépend de n.\n"
        },
        {
          "text": "O(n!)",
          "correct": false,
          "feedback": "Erreur : factorielle, c'est la complexité\nd'algorithmes énumérant toutes les\npermutations.\n"
        },
        {
          "text": "O(n²)",
          "correct": true,
          "feedback": "Bonne réponse : deux boucles imbriquées sur\nn éléments donnent n × n = n² opérations\nau pire.\n"
        }
      ],
      "explanation": "Double boucle = O(n²). Astuce : on peut faire\nmieux ici en O(n) avec un `set` (test\nd'appartenance en O(1))."
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "constante"
      ],
      "title": "Constante",
      "statement": "Quelle est la complexité de l'accès `liste[i]`\nsur une **liste Python** ?",
      "options": [
        {
          "text": "O(n²)",
          "correct": false,
          "feedback": "Erreur : un accès par indice est trivial.\n"
        },
        {
          "text": "O(log n)",
          "correct": false,
          "feedback": "Erreur : aucune dichotomie nécessaire,\naccès direct.\n"
        },
        {
          "text": "O(n)",
          "correct": false,
          "feedback": "Erreur : pour une `list` Python, l'accès par\nindice est immédiat, on n'a pas besoin de\nparcourir la liste.\n"
        },
        {
          "text": "O(1)",
          "correct": true,
          "feedback": "Bonne réponse : les listes Python sont des\ntableaux, l'accès par indice est en temps\nconstant indépendamment de la taille.\n"
        }
      ],
      "explanation": "Distinguer : `liste[i]` = O(1) ; `cible in liste`\n= O(n) ; `liste.append(x)` = O(1) en moyenne ;\n`liste.insert(0, x)` = O(n)."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "taille-entree"
      ],
      "title": "Taille de l'entrée",
      "statement": "Pour une liste, qu'est-ce que la « taille de\nl'entrée » `n` ?",
      "options": [
        {
          "text": "Le numéro de ligne où la liste est définie",
          "correct": false,
          "feedback": "Le numéro de ligne du\ncode source ne joue\naucun rôle dans le coût\nd'exécution. La taille\nd'entrée caractérise le\nvolume de données traité,\nici le nombre d'éléments.\n"
        },
        {
          "text": "La valeur du plus grand élément",
          "correct": false,
          "feedback": "Erreur : la complexité ne dépend\ngénéralement pas des **valeurs**, mais de\nla **structure**.\n"
        },
        {
          "text": "La quantité de mémoire qu'elle occupe en octets",
          "correct": false,
          "feedback": "C'est lié, mais pas la définition standard.\n"
        },
        {
          "text": "Le nombre d'éléments de la liste (sa longueur)",
          "correct": true,
          "feedback": "Bonne réponse : la taille pertinente pour\nla complexité d'une liste est sa longueur.\n"
        }
      ],
      "explanation": "Selon le contexte, la « taille » peut être\ndifférente : nombre de chiffres pour un entier,\nnombre de pixels pour une image, nombre de lignes\npour un fichier, etc."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "pire-moyen-meilleur"
      ],
      "title": "Pire / moyen / meilleur cas",
      "statement": "Pourquoi distingue-t-on **pire cas, cas moyen,\nmeilleur cas** dans l'analyse de complexité ?",
      "options": [
        {
          "text": "Pour faire les énoncés plus longs",
          "correct": false,
          "feedback": "La distinction des trois\ncas a une véritable raison\nmathématique : la complexité\nd'un algorithme peut varier\nfortement selon les données\nen entrée, et il faut donc\npréciser quel scénario on\nanalyse.\n"
        },
        {
          "text": "Parce que les langages de programmation diffèrent",
          "correct": false,
          "feedback": "Sans rapport : la complexité est\nintrinsèque à l'algorithme.\n"
        },
        {
          "text": "Parce que la complexité peut dépendre du contenu de l'entrée, pas seulement de sa taille",
          "correct": true,
          "feedback": "Bonne réponse : pour le tri par insertion,\nune liste déjà triée donne O(n), une liste\nrenversée donne O(n²). Le pire cas est le\nplus important pour les garanties.\n"
        },
        {
          "text": "Parce que l'ordinateur a parfois la flemme",
          "correct": false,
          "feedback": "Un ordinateur exécute\nsystématiquement les\ninstructions qui lui\nsont fournies. La variation\nde coût provient de\nl'algorithme et des données,\npas du matériel.\n"
        }
      ],
      "explanation": "En général, on retient le **pire cas** comme\nréférence : il garantit qu'au-delà, le coût ne\ndépassera pas. Le cas moyen est utile pour\nl'analyse fine en pratique."
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "comparaison-tailles"
      ],
      "title": "Échelle de croissance",
      "statement": "Pour n = 1000, qui est le plus grand ?",
      "options": [
        {
          "text": "n = 1000",
          "correct": false,
          "feedback": "Erreur : intermédiaire.\n"
        },
        {
          "text": "2ⁿ ≈ 10³⁰¹ (un nombre de plus de 300 chiffres)",
          "correct": true,
          "feedback": "Bonne réponse : 2¹⁰⁰⁰ est astronomiquement\nplus grand que 10⁶ ou 10³. C'est pourquoi\non évite à tout prix les algorithmes\nexponentiels sur de grandes entrées.\n"
        },
        {
          "text": "n² = 1 000 000",
          "correct": false,
          "feedback": "Erreur : c'est grand mais pas le plus grand.\n"
        },
        {
          "text": "log₂(n) ≈ 10",
          "correct": false,
          "feedback": "Erreur : c'est la valeur la plus petite\ndes quatre.\n"
        }
      ],
      "explanation": "Échelle pour n = 1000 : log n = 10, n = 10³,\nn² = 10⁶, 2ⁿ ≈ 10³⁰¹. Différence colossale."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "exemple-recherche"
      ],
      "title": "Recherche",
      "statement": "Quelle est la complexité de la **recherche\ndichotomique** dans une liste triée ?",
      "options": [
        {
          "text": "O(n)",
          "correct": false,
          "feedback": "Erreur : c'est la recherche **linéaire**,\npas dichotomique.\n"
        },
        {
          "text": "O(log n)",
          "correct": true,
          "feedback": "Bonne réponse : à chaque étape, on divise\nla liste en deux. C'est la complexité de\nréférence pour les algorithmes de\ndichotomie.\n"
        },
        {
          "text": "O(n²)",
          "correct": false,
          "feedback": "Erreur : aucun double parcours.\n"
        },
        {
          "text": "O(1)",
          "correct": false,
          "feedback": "Erreur : on doit comparer plusieurs\néléments dans le pire cas.\n"
        }
      ],
      "explanation": "Hiérarchie classique : O(log n) (dichotomie)\n<< O(n) (parcours simple) << O(n²) (double\nparcours) << O(2ⁿ) (énumération exponentielle)."
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "calcul-imbrication"
      ],
      "title": "Boucles imbriquées",
      "statement": "Quelle est la complexité de :\n```python\nfor i in range(n):\n    for j in range(n):\n        for k in range(n):\n            print(i, j, k)\n```",
      "options": [
        {
          "text": "O(n)",
          "correct": false,
          "feedback": "Erreur : trois boucles imbriquées.\n"
        },
        {
          "text": "O(n²)",
          "correct": false,
          "feedback": "Erreur : c'est deux boucles imbriquées,\npas trois.\n"
        },
        {
          "text": "O(3n)",
          "correct": false,
          "feedback": "Erreur : ce serait trois boucles **non\nimbriquées**, soit O(n).\n"
        },
        {
          "text": "O(n³)",
          "correct": true,
          "feedback": "Bonne réponse : trois boucles imbriquées\nsur n donnent n × n × n = n³ opérations.\n"
        }
      ],
      "explanation": "Règle simple : pour des boucles imbriquées\nsur n, la complexité est n^(profondeur)."
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "boucles-non-imbriquees"
      ],
      "title": "Deux boucles séparées",
      "statement": "Quelle est la complexité de :\n```python\nfor i in range(n):\n    print(i)\nfor j in range(n):\n    print(j)\n```",
      "options": [
        {
          "text": "O(2)",
          "correct": false,
          "feedback": "Erreur : le coût dépend de n.\n"
        },
        {
          "text": "O(n²)",
          "correct": false,
          "feedback": "Erreur : les boucles ne sont **pas\nimbriquées**, on les additionne, on ne les\nmultiplie pas.\n"
        },
        {
          "text": "O(n) (et non O(2n), car les constantes sont absorbées)",
          "correct": true,
          "feedback": "Bonne réponse : 2n est un O(n). On n'écrit\njamais O(2n), seulement O(n).\n"
        },
        {
          "text": "O(n × m) où m = n",
          "correct": false,
          "feedback": "Erreur : c'est O(n + n) = O(2n) = O(n).\n"
        }
      ],
      "explanation": "Règle : O(f) + O(g) = O(max(f, g)). La\ncomplexité d'une séquence est dominée par sa\npartie la plus coûteuse."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "domination"
      ],
      "title": "Termes dominants",
      "statement": "Quelle est la complexité de l'expression\n`T(n) = 3n² + 100n + 50` ?",
      "options": [
        {
          "text": "O(3n²)",
          "correct": false,
          "feedback": "Erreur : on n'écrit pas la constante\nmultiplicative dans la notation O.\n"
        },
        {
          "text": "O(n²)",
          "correct": true,
          "feedback": "Bonne réponse : le terme **dominant** pour\nn grand est `3n²`. Les autres termes (100n,\n50) deviennent négligeables.\n"
        },
        {
          "text": "O(50)",
          "correct": false,
          "feedback": "Erreur : 50 est une constante mais elle est\ndominée par les termes en n.\n"
        },
        {
          "text": "O(100n)",
          "correct": false,
          "feedback": "Erreur : on n'écrit pas la constante. De\nplus, c'est dominé par n².\n"
        }
      ],
      "explanation": "Règle systématique : ne garder que le terme\nle plus puissant et supprimer la constante\nmultiplicative. C'est l'essence de la notation\nasymptotique."
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "croissance-pratique"
      ],
      "title": "Doublement de la taille",
      "statement": "Un algorithme en O(n²) traite n = 1000\néléments en 1 seconde. Combien de temps lui\nfaudra-t-il pour n = 2000 ?",
      "options": [
        {
          "text": "1 seconde (le temps ne change pas)",
          "correct": false,
          "feedback": "Erreur : ce serait O(1).\n"
        },
        {
          "text": "4 secondes (n doublé → temps quadruplé)",
          "correct": true,
          "feedback": "Bonne réponse : O(n²) signifie temps ∝ n².\nDoubler n multiplie le temps par 4\n(= 2²). Tripler n multiplierait par 9\n(= 3²).\n"
        },
        {
          "text": "1000 secondes",
          "correct": false,
          "feedback": "Surestimation grossière.\n"
        },
        {
          "text": "2 secondes (n doublé → temps doublé)",
          "correct": false,
          "feedback": "Erreur : ce serait O(n).\n"
        }
      ],
      "explanation": "Compétence essentielle : extrapoler le temps\nd'exécution. Cela permet d'**anticiper** si un\nalgorithme va supporter la charge."
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "meilleur-pire"
      ],
      "title": "Pire vs meilleur cas",
      "statement": "La recherche linéaire est en `O(n)` au pire et\n`O(1)` au meilleur. Que dire si l'on parle de\n« complexité » sans précision ?",
      "options": [
        {
          "text": "On parle du cas moyen",
          "correct": false,
          "feedback": "Pas la convention standard, sauf\nprécision.\n"
        },
        {
          "text": "On parle implicitement du meilleur cas",
          "correct": false,
          "feedback": "Erreur : par convention, on prend le pire\ncas si rien n'est précisé.\n"
        },
        {
          "text": "On parle du cas le plus probable",
          "correct": false,
          "feedback": "Pas une notion bien définie sans modèle\nprobabiliste.\n"
        },
        {
          "text": "On parle implicitement du pire cas",
          "correct": true,
          "feedback": "Bonne réponse : c'est la convention en NSI\net plus largement. Le pire cas garantit\nune borne supérieure.\n"
        }
      ],
      "explanation": "Le pire cas a une signification claire et\ngarantit la performance « au pire ». Le cas\nmoyen demanderait de modéliser la\ndistribution de probabilité des entrées."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "memoire"
      ],
      "title": "Complexité spatiale",
      "statement": "Quelle est la complexité **spatiale** (mémoire\nadditionnelle) du tri par insertion ?",
      "options": [
        {
          "text": "O(1)",
          "correct": true,
          "feedback": "Bonne réponse : tri en place, ne nécessite\nque quelques variables temporaires\nindépendamment de n.\n"
        },
        {
          "text": "O(n²)",
          "correct": false,
          "feedback": "Erreur : on n'alloue pas de tableau 2D.\n"
        },
        {
          "text": "O(log n)",
          "correct": false,
          "feedback": "Erreur : pas de mécanisme dichotomique ici\n(la boucle parcourt n éléments).\n"
        },
        {
          "text": "O(n)",
          "correct": false,
          "feedback": "Erreur : on ne crée pas de copie de la\nliste.\n"
        }
      ],
      "explanation": "Distinction temps / espace : le tri par\ninsertion est lent (O(n²) en temps) mais\néconome en mémoire (O(1) en espace). Le tri\nfusion est rapide (O(n log n)) mais coûteux\nen mémoire (O(n))."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "n-log-n"
      ],
      "title": "O(n log n)",
      "statement": "Pour quel type d'algorithme la complexité\nO(n log n) est-elle typique ?",
      "options": [
        {
          "text": "Le calcul de la somme d'une liste",
          "correct": false,
          "feedback": "Erreur : O(n).\n"
        },
        {
          "text": "La recherche linéaire",
          "correct": false,
          "feedback": "Erreur : O(n).\n"
        },
        {
          "text": "La recherche dichotomique",
          "correct": false,
          "feedback": "Erreur : la recherche dichotomique est en\nO(log n), pas O(n log n).\n"
        },
        {
          "text": "Les tris efficaces (fusion, rapide, Timsort)",
          "correct": true,
          "feedback": "Bonne réponse : les tris « diviser pour\nrégner » atteignent O(n log n), prouvée\noptimale pour les tris par comparaisons.\n"
        }
      ],
      "explanation": "O(n log n) ≈ O(n) sur de petites entrées,\nmais bien meilleur que O(n²) sur de grandes.\nPour n = 10⁶ : n log n ≈ 2 × 10⁷, n² = 10¹².\nDifférence colossale."
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "exponentielle"
      ],
      "title": "Algorithmes exponentiels",
      "statement": "Pourquoi évite-t-on les algorithmes en O(2ⁿ) ?",
      "options": [
        {
          "text": "Parce qu'ils prennent trop de place sur le disque",
          "correct": false,
          "feedback": "La consommation mémoire n'est pas la\nraison principale.\n"
        },
        {
          "text": "Parce qu'ils n'existent pas",
          "correct": false,
          "feedback": "Erreur : ils existent (par exemple,\nénumérer tous les sous-ensembles).\n"
        },
        {
          "text": "Parce que pour n modeste (n = 50), 2ⁿ ≈ 10¹⁵, ce qui est hors d'atteinte même sur un super-calculateur",
          "correct": true,
          "feedback": "Bonne réponse : la croissance\nexponentielle rend impraticable un\nalgorithme dès que n dépasse quelques\ndizaines.\n"
        },
        {
          "text": "Parce qu'ils sont mal écrits",
          "correct": false,
          "feedback": "Erreur : un algorithme exponentiel peut être\nparfaitement écrit, c'est sa **structure**\nqui pose problème.\n"
        }
      ],
      "explanation": "Un algorithme exponentiel sur n = 100 prendrait\ndes milliards d'années même sur le meilleur\nordinateur du monde. Pour ces problèmes, on\nutilise des heuristiques ou des algorithmes\napprochés."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "calcul-temps"
      ],
      "title": "Estimer un temps",
      "statement": "Un algorithme effectue 10⁹ opérations\nélémentaires. Sur un PC moderne (~ 10⁹\nopérations / seconde), combien de temps\nattendra-t-on ?",
      "options": [
        {
          "text": "Environ une seconde",
          "correct": true,
          "feedback": "Bonne réponse : règle d'or, un PC\nmoderne effectue grosso modo 10⁸ à 10⁹\nopérations Python par seconde. 10⁹ ≈\nquelques secondes.\n"
        },
        {
          "text": "Plusieurs années",
          "correct": false,
          "feedback": "Erreur : valeur attendue pour ~ 10¹⁵\nopérations.\n"
        },
        {
          "text": "Plusieurs jours",
          "correct": false,
          "feedback": "Erreur : on aurait dépassé largement les 10⁹\nopérations.\n"
        },
        {
          "text": "Quelques nanosecondes",
          "correct": false,
          "feedback": "Erreur : 10⁹ opérations à 10⁹/s = 1\nseconde, pas une nanoseconde.\n"
        }
      ],
      "explanation": "Repère mental : 10⁸ par seconde en Python (un\npeu lent), 10⁹ ou plus en C/C++. Permet\nd'anticiper si un algorithme passera ou non\nla contrainte de temps."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "comparaison-grand-petit"
      ],
      "title": "Petit vs grand n",
      "statement": "Pour n petit (n = 10), un algorithme O(n²) est-il\nforcément moins efficace qu'un O(n log n) ?",
      "options": [
        {
          "text": "Aucun rapport entre les deux",
          "correct": false,
          "feedback": "Erreur : asymptotiquement, O(n log n) bat\nO(n²).\n"
        },
        {
          "text": "Oui, O(n²) est toujours moins efficace",
          "correct": false,
          "feedback": "Erreur : la notation O cache des\n**constantes** qui peuvent renverser le\nclassement sur les petites entrées.\n"
        },
        {
          "text": "Non, sur les petites entrées les constantes peuvent dominer et l'algorithme « plus simple » peut être plus rapide",
          "correct": true,
          "feedback": "Bonne réponse : Python utilise Timsort\n(n log n) mais bascule sur le tri par\ninsertion (O(n²)) pour les sous-tableaux\nde moins de ~ 64 éléments. Preuve par la\npratique.\n"
        },
        {
          "text": "Cela dépend de la couleur de l'écran",
          "correct": false,
          "feedback": "La complexité d'un\nalgorithme est une\nmesure mathématique\nintrinsèque, indépendante\nde l'affichage. Elle ne\ndépend que du nombre\nd'opérations effectuées\nen fonction de la taille\nd'entrée.\n"
        }
      ],
      "explanation": "Maxime : « la notation O est asymptotique ».\nPour les petits n, des constantes plus petites\npeuvent compenser un ordre de grandeur plus\ngrand. Asymptotiquement, l'ordre l'emporte."
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "analyse-detaillee"
      ],
      "title": "Analyse fine",
      "statement": "Quelle est la complexité de :\n```python\ndef f(n):\n    for i in range(n):\n        for j in range(i):\n            print(i, j)\n```",
      "options": [
        {
          "text": "O(n²)",
          "correct": true,
          "feedback": "Bonne réponse : la boucle interne fait i\nitérations. Total = 0 + 1 + ... + (n-1)\n= n(n-1)/2 ≈ n²/2. C'est O(n²).\n"
        },
        {
          "text": "O(n log n)",
          "correct": false,
          "feedback": "Erreur : aucun mécanisme dichotomique.\n"
        },
        {
          "text": "O(n)",
          "correct": false,
          "feedback": "Erreur : double boucle.\n"
        },
        {
          "text": "O(n³)",
          "correct": false,
          "feedback": "Erreur : trois boucles imbriquées\ndonneraient n³.\n"
        }
      ],
      "explanation": "Astuce : même si la boucle interne ne va pas\njusqu'à n, la somme reste en n². Cas typique\ndes tris en O(n²)."
    },
    {
      "id": "q22",
      "difficulty": 3,
      "skills": [
        "boucle-divise"
      ],
      "title": "Boucle qui divise",
      "statement": "Une boucle parcourt une variable `n` en la\n**divisant par 2** à chaque tour, jusqu'à\natteindre 1 (à chaque tour, travail\nconstant). Quel est le coût total ?",
      "options": [
        {
          "text": "Quadratique",
          "correct": false,
          "feedback": "Erreur : aucun double parcours.\n"
        },
        {
          "text": "Constant",
          "correct": false,
          "feedback": "Erreur : il y a plusieurs tours.\n"
        },
        {
          "text": "Logarithmique",
          "correct": true,
          "feedback": "Bonne réponse : on passe de n à n/2 à n/4\nà ... à 1, soit log₂(n) étapes. C'est\nexactement le coût de la dichotomie.\n"
        },
        {
          "text": "Linéaire",
          "correct": false,
          "feedback": "Erreur : on divise par 2, pas par\nsoustraction de 1.\n"
        }
      ],
      "explanation": "Schéma caractéristique : à chaque itération,\nla quantité de travail restant est divisée\npar 2. Donne un coût logarithmique. C'est\nl'analyse de la recherche dichotomique."
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "empirique"
      ],
      "title": "Mesure empirique",
      "statement": "Pour vérifier la complexité d'un algorithme\nempiriquement, on mesure son temps `t` pour\ndifférentes tailles `n`. Si `t` est\nproportionnel à `n²`, comment cela apparaît-il\nsur un graphique log-log (axes logarithmiques) ?",
      "options": [
        {
          "text": "Une parabole",
          "correct": false,
          "feedback": "Erreur : c'est ce qu'on aurait sur des axes\nlinéaires, pas logarithmiques.\n"
        },
        {
          "text": "Une courbe exponentielle",
          "correct": false,
          "feedback": "Erreur : sur un graphe log-log, n²\napparaît linéaire.\n"
        },
        {
          "text": "Une droite de pente 2",
          "correct": true,
          "feedback": "Bonne réponse : si t = c × n², alors\nlog(t) = log(c) + 2 × log(n). C'est une\ndroite de pente 2 sur un graphe log-log.\nPour une complexité O(n^k), la pente est\nexactement k.\n"
        },
        {
          "text": "Une horizontale",
          "correct": false,
          "feedback": "Erreur : ce serait pour O(1).\n"
        }
      ],
      "explanation": "Astuce expérimentale : tracer en log-log et\nmesurer la pente. C'est ainsi qu'on confirme\nvisuellement la complexité d'un algorithme\nen pratique."
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "inclusion-classes"
      ],
      "title": "Inclusion des classes",
      "statement": "Une fonction en O(n) est-elle forcément aussi\nen O(n²) ?",
      "options": [
        {
          "text": "Non, ce sont des classes différentes",
          "correct": false,
          "feedback": "Erreur : O(n²) inclut O(n).\n"
        },
        {
          "text": "Oui : O(n²) est une borne supérieure plus large, qui inclut O(n)",
          "correct": true,
          "feedback": "Bonne réponse : O(f) signifie « majoré par\nc × f ». Si f croît plus vite que g (au\nsens asymptotique), alors O(g) ⊆ O(f).\nDonc O(n) ⊆ O(n²) ⊆ O(n³)...\n"
        },
        {
          "text": "Oui, mais seulement pour des n positifs",
          "correct": false,
          "feedback": "Précision inutile : la complexité s'étudie\npour n grand.\n"
        },
        {
          "text": "Non, sauf si n est petit",
          "correct": false,
          "feedback": "Erreur : la propriété est asymptotique.\n"
        }
      ],
      "explanation": "C'est pour cela qu'on dit « la complexité\n**est** O(n²) » plutôt que « la complexité\n**vaut** O(n²) » : c'est un majorant. La\nborne **serrée** s'écrit Θ(n²) (« thêta »),\nhors programme NSI."
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Synthèse",
      "statement": "Parmi les affirmations suivantes sur la\ncomplexité, laquelle est **fausse** ?",
      "options": [
        {
          "text": "La complexité spatiale mesure la mémoire additionnelle utilisée",
          "correct": false,
          "feedback": "Vrai : c'est sa définition.\n"
        },
        {
          "text": "Un algorithme O(n) est toujours plus rapide qu'un algorithme O(n²)",
          "correct": true,
          "feedback": "Faux (donc bonne réponse) : O(n) est plus\nrapide **asymptotiquement**, mais sur de\npetites entrées un O(n²) avec une petite\nconstante peut battre un O(n) avec une\ngrosse constante. Exemple : Timsort\nbascule sur du tri par insertion pour\nn < 64.\n"
        },
        {
          "text": "Pour les boucles imbriquées, on multiplie les complexités ; pour les séquences, on prend le maximum",
          "correct": false,
          "feedback": "Vrai : règle de calcul standard.\n"
        },
        {
          "text": "La complexité O ignore les constantes multiplicatives",
          "correct": false,
          "feedback": "Vrai : O(2n) = O(n) = O(100n).\n"
        }
      ],
      "explanation": "Maxime : « la complexité asymptotique est un\nguide, pas une vérité absolue ». Il faut\ntoujours tester en pratique pour les cas\nréels, surtout sur de petites entrées."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "while-decroissant"
      ],
      "title": "Boucle while soustractive",
      "statement": "Quelle est la complexité de la fonction\nsuivante en fonction de `n` ?\n```python\ndef f(n):\n    while n > 0:\n        print(n)\n        n = n - 3\n```",
      "options": [
        {
          "text": "O(log n)",
          "correct": false,
          "feedback": "Erreur : la division par deux donne du\nlogarithmique. Ici on **soustrait** une\nconstante à chaque tour, donc le nombre\nd'itérations est proportionnel à `n`, pas à\n`log n`.\n"
        },
        {
          "text": "O(1)",
          "correct": false,
          "feedback": "Erreur : le nombre d'itérations dépend\nclairement de `n`. Pour `n = 30`, la boucle\ntourne dix fois ; pour `n = 300`, elle\ntourne cent fois. Ce n'est pas constant.\n"
        },
        {
          "text": "O(n)",
          "correct": true,
          "feedback": "Bonne réponse : `n` décroît par pas de 3.\nLe nombre d'itérations vaut environ `n / 3`,\nce qui est O(n) car on absorbe la constante\nmultiplicative. Toute boucle `while` qui\nsoustrait une constante à `n` est en O(n).\n"
        },
        {
          "text": "O(n²)",
          "correct": false,
          "feedback": "Erreur : il n'y a pas de double boucle. Le\ncoût est dominé par le nombre d'itérations\nde l'unique `while`.\n"
        }
      ],
      "explanation": "À retenir : soustraire une constante à `n` à\nchaque itération donne un coût linéaire ;\ndiviser `n` par une constante donne un coût\nlogarithmique. Cette distinction est la clé\npour reconnaître les algorithmes\nlogarithmiques."
    },
    {
      "id": "q27",
      "difficulty": 3,
      "skills": [
        "taille-vs-valeur"
      ],
      "title": "Taille vs valeur de l'entrée",
      "statement": "On considère la fonction qui compte les\nchiffres d'un entier positif :\n```python\ndef chiffres(n):\n    c = 0\n    while n > 0:\n        n = n // 10\n        c += 1\n    return c\n```\nQuelle est sa complexité ?",
      "options": [
        {
          "text": "O(n) en fonction de la valeur n",
          "correct": false,
          "feedback": "Réponse partielle : c'est faux. À chaque\nitération, `n` est divisé par 10. Donc le\nnombre d'itérations est de l'ordre de\n`log₁₀(n)`, pas de `n`. Pour `n = 10⁶`, la\nboucle ne tourne que sept fois, pas un\nmillion.\n"
        },
        {
          "text": "O(1) car n est une simple variable",
          "correct": false,
          "feedback": "Erreur : la boucle dépend de la valeur de\n`n`. Plus `n` est grand, plus elle tourne\nlongtemps.\n"
        },
        {
          "text": "O(log n) en fonction de la valeur n (autrement dit, linéaire en fonction du nombre de chiffres)",
          "correct": true,
          "feedback": "Bonne réponse : on divise `n` par 10 à\nchaque itération, donc `log₁₀(n)` étapes.\nC'est aussi exactement le **nombre de\nchiffres** de `n` dans son écriture\ndécimale. Une autre façon de le dire : la\ncomplexité est **linéaire** par rapport à\nla **taille** (nombre de chiffres) de `n`.\n"
        },
        {
          "text": "O(n²)",
          "correct": false,
          "feedback": "Erreur : aucune double boucle ni structure\nquadratique. Le coût ne grandit pas comme\n`n²`.\n"
        }
      ],
      "explanation": "Subtilité importante : la « taille » d'un\nentier `n` est son **nombre de chiffres**,\ndonc de l'ordre de `log(n)`. Les algorithmes\nclassiques sur les entiers sont dits\n« linéaires » quand leur coût est\nproportionnel au nombre de chiffres, c'est-à-\ndire `O(log n)` en fonction de la valeur."
    },
    {
      "id": "q28",
      "difficulty": 2,
      "skills": [
        "operations-listes"
      ],
      "title": "Coût des opérations sur les listes",
      "statement": "Parmi ces opérations sur une liste Python de\ntaille `n`, laquelle est la plus **coûteuse**\nau pire ?",
      "options": [
        {
          "text": "`liste[0]` (lecture du premier élément)",
          "correct": false,
          "feedback": "Erreur : l'accès par indice est en O(1) sur\nune liste Python (qui est en réalité un\ntableau dynamique). Pas besoin de\nparcourir.\n"
        },
        {
          "text": "`liste.insert(0, x)` (insertion en tête)",
          "correct": true,
          "feedback": "Bonne réponse : insérer en tête oblige à\n**décaler tous les éléments existants**\nd'une case vers la droite, ce qui coûte\nO(n). Sur une liste de un million\nd'éléments, c'est très lent. Pour un usage\nintensif d'insertion en tête, utiliser un\n`collections.deque` (O(1)).\n"
        },
        {
          "text": "`liste.append(x)` (ajout en fin)",
          "correct": false,
          "feedback": "Erreur : `append` est en O(1) en moyenne. Une\nréallocation occasionnelle peut prendre\nplus de temps, mais le **coût amorti** par\nopération reste constant.\n"
        },
        {
          "text": "`len(liste)` (longueur de la liste)",
          "correct": false,
          "feedback": "Erreur : `len` est en O(1). Python stocke la\nlongueur dans un attribut de la liste, pas\nbesoin de compter à chaque appel.\n"
        }
      ],
      "explanation": "Repères à connaître pour les listes Python :\naccès par indice et `len` en O(1) ; `append`,\n`pop()` (en fin) en O(1) amorti ; `insert(0,\nx)`, `pop(0)`, `remove`, `in` en O(n). Ces\nordres de grandeur déterminent le choix de la\nstructure de données pour un algorithme\ndonné."
    },
    {
      "id": "q29",
      "difficulty": 3,
      "skills": [
        "seuils-pratiques"
      ],
      "title": "Choisir selon la taille",
      "statement": "Sur un PC moderne, on dispose d'**une seconde**\npour exécuter un algorithme (~ 10⁸ opérations\nPython). Quelle est la **plus grande taille**\nd'entrée que l'on peut traiter avec un\nalgorithme en O(n²) ?",
      "options": [
        {
          "text": "Environ 30 (trentaine)",
          "correct": false,
          "feedback": "Erreur : ce serait pour un algorithme\nexponentiel `O(2ⁿ)`. Avec n = 30, on a\ndéjà `10⁹` opérations, soit la limite. Mais\npour quadratique, on peut traiter\nbeaucoup plus.\n"
        },
        {
          "text": "Environ 10⁸ (cent millions)",
          "correct": false,
          "feedback": "Erreur : ce serait pour un algorithme en\nO(n) (linéaire). Pour un algorithme\nquadratique, il faut prendre la racine\ncarrée du budget d'opérations.\n"
        },
        {
          "text": "Environ 10⁴ (dix mille)",
          "correct": true,
          "feedback": "Bonne réponse : on cherche `n` tel que\n`n² ≤ 10⁸`, donc `n ≤ √(10⁸) = 10⁴`. À\nretenir : un algorithme quadratique\ndevient lent dès quelques dizaines de\nmilliers d'éléments. Comparer avec O(n\nlog n) qui supporte plusieurs millions.\n"
        },
        {
          "text": "Environ 10⁶ (un million)",
          "correct": false,
          "feedback": "Erreur : pour `n = 10⁶`, un algorithme\nquadratique fait `10¹²` opérations, ce qui\nprendrait environ trois heures. Hors\nbudget.\n"
        }
      ],
      "explanation": "Repères de seuils pratiques (1 seconde de\ncalcul, ~ 10⁸ opérations) : O(2ⁿ) → n ≈ 25 ;\nO(n³) → n ≈ 500 ; O(n²) → n ≈ 10⁴ ; O(n log\nn) → n ≈ 10⁶ ; O(n) → n ≈ 10⁸ ; O(log n) → n\ngigantesque. Indispensable pour évaluer la\nfaisabilité d'un algorithme avant de\nl'écrire."
    }
  ]
}