{
  "chapter": {
    "id": "knn",
    "level": "premiere",
    "theme": "Algorithmique",
    "title": "k plus proches voisins (k-NN)",
    "description": "Algorithme des k plus proches voisins (k-NN) : exemple\nd'algorithme d'apprentissage supervisé. Distance entre\npoints, vote majoritaire, choix de k, applications,\nforces et limites.",
    "parties": [
      {
        "id": "principe",
        "title": "Principe de l'algorithme"
      },
      {
        "id": "distance",
        "title": "Distances et voisins"
      },
      {
        "id": "mise-en-oeuvre",
        "title": "Mise en œuvre et jeux de données"
      },
      {
        "id": "limites",
        "title": "Choix de k, forces et limites"
      }
    ],
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition"
      ],
      "title": "k-NN : qu'est-ce que c'est ?",
      "partie": "principe",
      "statement": "Que fait l'algorithme des **k plus proches voisins**\n(k-NN, *k-Nearest Neighbors*) ?",
      "options": [
        {
          "text": "Prédire la classe d'un point selon ses voisins",
          "correct": true,
          "feedback": "Bonne réponse : c'est un algorithme\nd'**apprentissage supervisé** par\nmémorisation. On classe un nouveau point\nselon ses voisins.\n"
        },
        {
          "text": "Mesure la complexité d'un programme",
          "correct": false,
          "feedback": "Erreur : la mesure de\ncomplexité algorithmique\nn'a aucun rapport avec\nl'algorithme des k plus\nproches voisins.\n"
        },
        {
          "text": "Cherche un élément dans une liste triée",
          "correct": false,
          "feedback": "Erreur : c'est la recherche dichotomique.\n"
        },
        {
          "text": "Trie une liste par ordre croissant",
          "correct": false,
          "feedback": "Erreur : aucun rapport avec le tri.\n"
        }
      ],
      "explanation": "Apprentissage supervisé : on dispose d'exemples\nétiquetés (avec leur classe connue), on\napprend à prédire la classe pour de nouveaux\nexemples."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "import-csv",
        "types"
      ],
      "title": "Des mesures lues dans un fichier CSV",
      "partie": "mise-en-oeuvre",
      "statement": "Pour préparer un k-NN, on lit le fichier `fruits.csv` avec\n`csv.reader`. Sa première ligne contient `102,7,pomme` et devient\nla liste `ligne`. Quel est le type de `ligne[0]` ?",
      "options": [
        {
          "text": "Un entier, car la valeur 102 n'a pas de partie décimale",
          "correct": false,
          "feedback": "Erreur : `csv.reader` ne convertit aucune valeur, quelle que soit\nson écriture. `ligne[0]` est la chaîne `'102'`, qu'il faut\nconvertir avec `int` ou `float` avant de calculer.\n"
        },
        {
          "text": "Un flottant, car le module `csv` convertit les nombres qu'il lit",
          "correct": false,
          "feedback": "Erreur : le module `csv` ne devine pas le type des valeurs, il\nrenvoie toujours des chaînes. `ligne[0]` vaut `'102'`, pas\n`102.0`.\n"
        },
        {
          "text": "Une chaîne, à convertir avant de calculer une distance",
          "correct": true,
          "feedback": "Bonne réponse : `csv.reader` découpe chaque ligne en chaînes de\ncaractères, sans rien convertir : `ligne[0]` vaut `'102'`. Pour\ncalculer une distance, il faut écrire `float(ligne[0])` ; sinon la\nsoustraction avec un nombre provoque `TypeError`.\n"
        },
        {
          "text": "Un p-uplet de trois caractères, un par chiffre de 102",
          "correct": false,
          "feedback": "Erreur : la ligne est découpée aux virgules, pas chiffre par\nchiffre. `ligne[0]` est la chaîne entière `'102'`.\n"
        }
      ],
      "explanation": "Une table importée depuis un fichier CSV ne contient que du texte.\nAvant d'appliquer k-NN, on convertit les colonnes numériques avec\n`float` pour pouvoir calculer les distances."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "distance"
      ],
      "title": "Distance euclidienne",
      "partie": "distance",
      "statement": "Comment calcule-t-on la **distance euclidienne**\nentre deux points $A=(x_A, y_A)$ et $B=(x_B,\ny_B)$ du plan ?",
      "options": [
        {
          "text": "$d = \\sqrt{(x_B - x_A)^2 + (y_B - y_A)^2}$",
          "correct": true,
          "feedback": "Bonne réponse : formule de Pythagore.\nC'est la distance « à vol d'oiseau »\nentre les deux points.\n"
        },
        {
          "text": "$d = (x_B - x_A)^2 + (y_B - y_A)^2$",
          "correct": false,
          "feedback": "Erreur : c'est le **carré** de la distance,\nil manque la racine carrée. Comparer des\ncarrés de distances reste possible pour\nk-NN, mais ce n'est pas la distance.\n"
        },
        {
          "text": "$d = \\sqrt{(x_B - x_A) + (y_B - y_A)}$",
          "correct": false,
          "feedback": "Erreur : sans les carrés, les écarts\nnégatifs se compensent et l'expression\nsous la racine peut être négative.\n"
        },
        {
          "text": "$d = |x_B - x_A| + |y_B - y_A|$",
          "correct": false,
          "feedback": "Erreur : c'est la distance de Manhattan\n(somme des écarts absolus), une autre\ndistance possible, mais pas l'euclidienne.\n"
        }
      ],
      "explanation": "Cette formule se généralise en dimension n :\n$d = \\\\sqrt{\\\\sum_{i=1}^{n}(b_i - a_i)^2}$.\nPour comparer des distances, on peut omettre\nla racine carrée."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "vote-majoritaire"
      ],
      "title": "Vote majoritaire",
      "partie": "principe",
      "statement": "Une fois les k plus proches voisins\nidentifiés, comment k-NN détermine-t-il la\nclasse à attribuer ?",
      "options": [
        {
          "text": "Au hasard",
          "correct": false,
          "feedback": "Erreur : k-NN est déterministe.\n"
        },
        {
          "text": "En choisissant la classe du voisin le plus proche",
          "correct": false,
          "feedback": "C'est k-NN avec k = 1, cas particulier. En\ngénéral on utilise plusieurs voisins pour\nplus de robustesse.\n"
        },
        {
          "text": "Par vote majoritaire parmi les k voisins",
          "correct": true,
          "feedback": "Bonne réponse : si parmi les 5 voisins\nles plus proches, 3 sont de la classe A et\n2 de la classe B, on prédit A.\n"
        },
        {
          "text": "En faisant la moyenne des classes",
          "correct": false,
          "feedback": "Erreur : on ne fait pas la moyenne d'une\nclasse (c'est une catégorie, pas un\nnombre).\n"
        }
      ],
      "explanation": "Variante : **vote pondéré** par la distance\n(les voisins plus proches comptent plus).\nPermet de gérer les ex æquo."
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "choix-k"
      ],
      "title": "Choix de k",
      "partie": "limites",
      "statement": "Pourquoi prend-on souvent k **impair** quand\non classe en deux classes ?",
      "options": [
        {
          "text": "Pour éviter les égalités lors du vote",
          "correct": true,
          "feedback": "Bonne réponse : avec k pair (par exemple\nk = 4), on peut avoir 2 votes pour chaque\nclasse → indécision. Avec k impair (k = 5),\nimpossible.\n"
        },
        {
          "text": "Aucune raison particulière",
          "correct": false,
          "feedback": "Cette parité répond au\ncontraire à un besoin\ntechnique précis : éviter\nles égalités lors du\nvote, comme expliqué dans\nla bonne réponse.\n"
        },
        {
          "text": "Parce que les nombres impairs sont plus rapides à calculer",
          "correct": false,
          "feedback": "Aucun lien n'existe entre la\nparité d'un nombre et la\nrapidité de calcul.\n"
        },
        {
          "text": "Parce que k pair ne fonctionne pas",
          "correct": false,
          "feedback": "Erreur : ça fonctionne, mais on doit gérer\nles ex æquo.\n"
        }
      ],
      "explanation": "Pour trois classes ou plus, un\n$k$ impair n'évite pas\nforcément les ex æquo : trois\nclasses peuvent par exemple\nobtenir chacune $k/3$ voix.\nOn peut alors recourir à la\nclasse du voisin le plus\nproche, ou à un vote pondéré\npar la distance."
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "exemple-iris"
      ],
      "title": "Exemple typique",
      "partie": "mise-en-oeuvre",
      "statement": "Lequel de ces problèmes est un **bon candidat**\npour l'algorithme k-NN ?",
      "options": [
        {
          "text": "Compresser un fichier pour gagner de la place",
          "correct": false,
          "feedback": "La compression de fichiers\nest une tâche très\ndifférente, sans rapport\navec la classification.\n"
        },
        {
          "text": "Trier une liste de nombres",
          "correct": false,
          "feedback": "Erreur : ce n'est pas un problème de\nclassification.\n"
        },
        {
          "text": "Reconnaître un spam à partir d'exemples",
          "correct": true,
          "feedback": "Bonne réponse : tâche de classification\nbinaire (spam / non spam). On peut\ncomparer le mail aux exemples connus\nselon des caractéristiques (mots-clés,\nlongueur, etc.).\n"
        },
        {
          "text": "Calculer la racine carrée exacte d'un nombre",
          "correct": false,
          "feedback": "Erreur : pas un problème d'apprentissage.\n"
        }
      ],
      "explanation": "Autres exemples : reconnaissance de\nchiffres manuscrits, classification de\nfleurs (jeu de données Iris), recommandation\nde films."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "absence-apprentissage"
      ],
      "title": "Pas d'entraînement explicite",
      "partie": "principe",
      "statement": "Quelle particularité de k-NN le distingue\nd'autres algorithmes d'apprentissage ?",
      "options": [
        {
          "text": "Presque aucun entraînement, calcul à la prédiction",
          "correct": true,
          "feedback": "Bonne réponse : k-NN est un algorithme\ndit « paresseux ». Pas de modèle interne ;\ntout le travail est fait à la prédiction.\n"
        },
        {
          "text": "Il fonctionne sans aucune donnée d'entraînement",
          "correct": false,
          "feedback": "Au contraire, l'algorithme\na besoin d'exemples\nétiquetés pour pouvoir\ncomparer le nouveau\npoint.\n"
        },
        {
          "text": "Il a une phase d'entraînement très longue",
          "correct": false,
          "feedback": "Erreur : c'est l'inverse. La phase\nd'entraînement consiste juste à\n**mémoriser** les exemples.\n"
        },
        {
          "text": "Il invente de nouveaux exemples au cours de la prédiction",
          "correct": false,
          "feedback": "Aucun exemple n'est\ninventé. L'algorithme se\ncontente de comparer le\npoint à classer aux\nexemples déjà connus.\n"
        }
      ],
      "explanation": "Conséquence : prédiction lente quand le jeu\nd'entraînement est gros (il faut calculer la\ndistance à tous les exemples). Compromis :\nstockage important, calcul à la prédiction."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "pseudocode"
      ],
      "title": "Squelette de l'algorithme",
      "partie": "principe",
      "statement": "Quelle est la **structure générale** de\nl'algorithme k-NN pour classer un nouveau\npoint `p` ?",
      "options": [
        {
          "text": "1. Calculer la distance de `p` à chaque exemple connu.\n2. Trier les exemples par distance croissante.\n3. Garder les k premiers.\n4. Retourner la classe majoritaire parmi ces k voisins.\n",
          "correct": true,
          "feedback": "Bonne réponse : algorithme en quatre\nétapes claires. Les étapes 1 et 2\ndominent le coût.\n"
        },
        {
          "text": "1. Calculer la distance de `p` à chaque exemple connu.\n2. Trier les exemples par distance décroissante.\n3. Garder les k premiers.\n4. Retourner la classe majoritaire parmi ces k voisins.\n",
          "correct": false,
          "feedback": "Erreur : trié par distance décroissante,\nles k premiers sont les k exemples les\nplus **éloignés** de `p`, l'inverse de ce\nque l'on veut.\n"
        },
        {
          "text": "1. Calculer la distance de `p` à chaque exemple connu.\n2. Trier les exemples par distance croissante.\n3. Garder les k premiers.\n4. Retourner la classe du plus proche d'entre eux.\n",
          "correct": false,
          "feedback": "Erreur : retourner la classe du seul plus\nproche voisin revient à prendre k = 1 ; le\nvote majoritaire parmi les k voisins est\njustement ce qui rend k-NN robuste au\nbruit.\n"
        },
        {
          "text": "1. Calculer la moyenne des exemples de chaque classe.\n2. Calculer la distance de `p` à chacune de ces moyennes.\n3. Garder la moyenne la plus proche.\n4. Retourner sa classe.\n",
          "correct": false,
          "feedback": "Erreur : c'est un autre algorithme (le\nplus proche centre). k-NN ne calcule\naucune moyenne : il compare `p` aux\nexemples eux-mêmes.\n"
        }
      ],
      "explanation": "En Python, les trois premières étapes s'écrivent\npar exemple :\n```python\ndef cle(couple):\n    return couple[0]\n\ndistances = [(dist(p, e), c) for (e, c) in exemples]\ndistances = sorted(distances, key=cle)\nvoisins = [distances[i] for i in range(k)]\n```"
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "parametres"
      ],
      "title": "Paramètre k",
      "partie": "principe",
      "statement": "Que se passe-t-il si l'on prend **k = 1** ?",
      "options": [
        {
          "text": "La classe du seul voisin, très sensible au bruit",
          "correct": true,
          "feedback": "Bonne réponse : un seul exemple aberrant\npeut fausser la prédiction. Augmenter k\nrend la décision plus robuste.\n"
        },
        {
          "text": "La complexité du calcul devient alors logarithmique",
          "correct": false,
          "feedback": "Le coût d'un calcul de\nprédiction ne dépend pas\ndu choix de $k$ : il\nreste linéaire en\n$N$ (nombre d'exemples).\n"
        },
        {
          "text": "La prédiction est plus stable",
          "correct": false,
          "feedback": "Erreur : c'est l'inverse. k = 1 est très\nsensible au bruit.\n"
        },
        {
          "text": "L'algorithme ne fonctionne pas avec $k = 1$",
          "correct": false,
          "feedback": "Le choix $k = 1$ est tout\nà fait valide d'un point\nde vue algorithmique. Il\nest simplement peu\nrobuste face au bruit\ndes données.\n"
        }
      ],
      "explanation": "Choix de k : compromis. k trop petit →\nsensibilité au bruit. k trop grand → frontières\nlissées, perte de précision locale. On choisit\nsouvent k empiriquement."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "exemple-trace"
      ],
      "title": "Trace simple",
      "partie": "mise-en-oeuvre",
      "statement": "Avec k = 3, on classe un nouveau point P. Ses\n3 plus proches voisins ont les classes : A, B,\nA. Quelle classe prédit k-NN pour P ?",
      "options": [
        {
          "text": "Aucune des deux (égalité)",
          "correct": false,
          "feedback": "Erreur : il n'y a pas d'égalité (2 vs 1).\n"
        },
        {
          "text": "B (le plus proche)",
          "correct": false,
          "feedback": "Erreur : ce serait k = 1. Avec k = 3, on\nfait un vote majoritaire.\n"
        },
        {
          "text": "A (deux votes contre un pour B)",
          "correct": true,
          "feedback": "Bonne réponse : vote majoritaire. A\nobtient 2 votes, B obtient 1. La classe\nprédite est A.\n"
        },
        {
          "text": "Les deux à la fois",
          "correct": false,
          "feedback": "Erreur : k-NN renvoie une seule classe.\n"
        }
      ],
      "explanation": "Le vote se programme simplement : un dictionnaire compte les\noccurrences de chaque classe parmi les voisins, puis on retient\nla classe qui a le plus grand compte."
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "code-distance"
      ],
      "title": "Code distance",
      "partie": "distance",
      "statement": "Quel code calcule correctement la **distance euclidienne** entre deux points 2D représentés\ncomme tuples `a = (xa, ya)` et `b = (xb, yb)` ?",
      "options": [
        {
          "text": "`((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2) ** 0.5`",
          "correct": true,
          "feedback": "Bonne réponse : Pythagore en Python.\n`** 0.5` calcule la racine carrée. Variante :\n`math.sqrt(...)`.\n"
        },
        {
          "text": "`a[0] * b[0] + a[1] * b[1]`",
          "correct": false,
          "feedback": "Erreur : c'est le produit scalaire.\n"
        },
        {
          "text": "`((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2) ** 1/2`",
          "correct": false,
          "feedback": "Erreur de priorité : `**` est prioritaire\nsur `/`, donc l'expression vaut\n`(...) ** 1 / 2`, la moitié du carré de la\ndistance. Il faut `** 0.5` ou `** (1/2)`.\n"
        },
        {
          "text": "`abs(a[0] - b[0]) + abs(a[1] - b[1])`",
          "correct": false,
          "feedback": "Erreur : c'est la distance de Manhattan\n(|Δx| + |Δy|), pas l'euclidienne.\n"
        }
      ],
      "explanation": "Astuce : pour comparer des distances, on\npeut omettre la racine carrée (la fonction\nracine est croissante). Plus rapide."
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "tri-distances"
      ],
      "title": "Sélection des voisins",
      "partie": "distance",
      "statement": "Pour trouver les k plus proches voisins d'un\npoint P parmi N exemples, quelle approche est\nla plus simple ?",
      "options": [
        {
          "text": "Calculer les distances, trier, garder les k premiers",
          "correct": true,
          "feedback": "Bonne réponse : simple à implémenter. On calcule les N distances,\non les trie par ordre croissant et on garde les k premières.\n"
        },
        {
          "text": "Aucune solution simple n'existe vraiment pour ce problème",
          "correct": false,
          "feedback": "Plusieurs solutions\nexistent au contraire ;\nla plus simple est\nprécisément celle qu'on\nvient de décrire.\n"
        },
        {
          "text": "Choisir k exemples au hasard",
          "correct": false,
          "feedback": "Erreur : pas k-NN.\n"
        },
        {
          "text": "Calculer la moyenne des exemples",
          "correct": false,
          "feedback": "Erreur : ce n'est pas le rôle.\n"
        }
      ],
      "explanation": "Des méthodes plus rapides existent pour de très grands jeux de\ndonnées, mais l'approche directe suffit pour comprendre\nl'algorithme."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "plus-proche-voisin",
        "extremum"
      ],
      "title": "Le plus proche en une seule passe",
      "partie": "principe",
      "statement": "Avec k = 1, il suffit de trouver le plus proche voisin. On propose\n(la fonction `dist(a, b)` renvoie la distance entre deux points) :\n\n```python\ndef plus_proche(p, exemples):\n    # exemples : liste non vide de couples (point, classe)\n    d_min = 0\n    classe = exemples[0][1]\n    for (x, c) in exemples:\n        if dist(p, x) < d_min:\n            d_min = dist(p, x)\n            classe = c\n    return classe\n```\n\nQue renvoie cette fonction ?",
      "options": [
        {
          "text": "La classe du plus proche voisin de `p`",
          "correct": false,
          "feedback": "Erreur : c'est l'intention, mais `d_min` part de 0. Aucune\ndistance n'étant strictement inférieure à 0, la condition n'est\njamais vraie et la variable `classe` n'est jamais mise à jour.\n"
        },
        {
          "text": "La classe du premier exemple, quel que soit `p`",
          "correct": true,
          "feedback": "Bonne réponse : une distance n'est jamais strictement inférieure\nà 0, donc le test `dist(p, x) < d_min` est toujours faux. `classe`\ngarde sa valeur initiale, la classe du premier exemple. Il\nfallait initialiser `d_min` avec la distance au premier exemple.\n"
        },
        {
          "text": "La classe de l'exemple le plus éloigné de `p`",
          "correct": false,
          "feedback": "Erreur : le test `<` ne retient jamais un exemple plus éloigné.\nEn fait il ne retient aucun exemple, car aucune distance n'est\nstrictement inférieure à la valeur initiale 0.\n"
        },
        {
          "text": "La classe du dernier exemple de la liste examinée",
          "correct": false,
          "feedback": "Erreur : `classe` n'est modifiée que si la condition est vraie,\nce qui n'arrive jamais ici. Elle garde donc la classe du premier\nexemple, pas celle du dernier.\n"
        }
      ],
      "explanation": "Pour chercher un minimum, on initialise avec une valeur réellement\natteinte, par exemple la distance au premier exemple, puis on\nparcourt la liste en ne gardant que les valeurs strictement plus\npetites. Ce parcours séquentiel a un coût linéaire."
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "k-petit-grand"
      ],
      "title": "Compromis sur k",
      "partie": "limites",
      "statement": "Quel **risque** y a-t-il à choisir un k **trop grand** (par exemple k = nombre total\nd'exemples) ?",
      "options": [
        {
          "text": "Aucun risque ; un grand $k$ est toujours préférable",
          "correct": false,
          "feedback": "Au contraire, un $k$ trop\ngrand pose un problème\nspécifique, comme expliqué\ndans la bonne réponse.\n"
        },
        {
          "text": "Le coût d'un calcul explose en $n^3$",
          "correct": false,
          "feedback": "Le coût d'un calcul de\nprédiction reste linéaire\nen $N$ : il ne dépend pas\ndu choix de $k$.\n"
        },
        {
          "text": "L'algorithme cesse alors complètement de fonctionner",
          "correct": false,
          "feedback": "L'algorithme continue de\nfonctionner, mais le\nrésultat qu'il produit\nn'est plus pertinent.\n"
        },
        {
          "text": "La prédiction tend vers la classe majoritaire globale",
          "correct": true,
          "feedback": "Bonne réponse : si k = N, tous les\nexemples votent. Le résultat est toujours\nla classe la plus représentée dans tout\nle jeu de données, indépendamment de la\nposition de P. Plus rien d'utile.\n"
        }
      ],
      "explanation": "Un k petit rend la prédiction sensible au bruit, par exemple à un\nexemple mal étiqueté ; un k grand lisse les résultats mais efface\nles différences locales. On choisit k selon la taille du jeu de\ndonnées."
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "donnees-entrainement"
      ],
      "title": "Rôle des données d'entraînement",
      "partie": "mise-en-oeuvre",
      "statement": "Que sont les **données d'entraînement**\n(training set) pour k-NN ?",
      "options": [
        {
          "text": "Les nouvelles données que l'on cherche à classer",
          "correct": false,
          "feedback": "Erreur : ce sont les données de **test**\n(à classer).\n"
        },
        {
          "text": "Une formule mathématique",
          "correct": false,
          "feedback": "Les données d'entraînement\nne sont pas une formule,\nmais un ensemble\nd'exemples concrets,\nchacun étiqueté avec sa\nclasse.\n"
        },
        {
          "text": "Les exemples étiquetés servant de comparaison",
          "correct": true,
          "feedback": "Bonne réponse : plus le jeu\nd'entraînement est riche et représentatif,\nmeilleures sont les prédictions.\n"
        },
        {
          "text": "Le code source de l'algorithme",
          "correct": false,
          "feedback": "Le code source est une\nnotion distincte des\ndonnées d'entraînement,\nqui sont les exemples\nutilisés.\n"
        }
      ],
      "explanation": "En pratique, on divise un jeu étiqueté en\n**train** (entraînement) + **test**\n(évaluation) pour mesurer la performance\nsans tricher."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "code-knn"
      ],
      "title": "Code k-NN simple",
      "partie": "mise-en-oeuvre",
      "statement": "Quel code en Python implémente correctement\nk-NN pour classer un point `p` ?\n```python\n# exemples = [(point, classe), ...]\n# k entier, dist(a, b) renvoie la distance\n# majoritaire(liste) renvoie la classe la plus\n# fréquente de la liste\ndef premier(paire):\n    return paire[0]\n```",
      "options": [
        {
          "text": "```python\npaires = [(dist(p, x), c) for (x, c) in exemples]\npaires = sorted(paires, key=premier, reverse=True)\nvoisins = [paires[i][1] for i in range(k)]\nreturn majoritaire(voisins)\n```",
          "correct": false,
          "feedback": "Erreur : le tri **décroissant** fait voter\nles k exemples les plus **éloignés** de `p`.\nIl faut trier par distance croissante pour\nretenir les plus proches voisins.\n"
        },
        {
          "text": "```python\nvoisins = [exemples[i][1] for i in range(k)]\nreturn majoritaire(voisins)\n```",
          "correct": false,
          "feedback": "Erreur : on fait voter les k premiers\nexemples **dans l'ordre du tableau**, sans\ncalculer aucune distance. Le vote ne porte\ndonc pas sur les voisins de `p`.\n"
        },
        {
          "text": "```python\npaires = [(dist(p, x), c) for (x, c) in exemples]\npaires = sorted(paires, key=premier)\nreturn paires[0][1]\n```",
          "correct": false,
          "feedback": "Erreur : on renvoie la classe du seul plus\nproche voisin, comme si k valait toujours 1.\nLe paramètre `k` est ignoré et le vote\nmajoritaire disparaît.\n"
        },
        {
          "text": "```python\npaires = [(dist(p, x), c) for (x, c) in exemples]\npaires = sorted(paires, key=premier)\nvoisins = [paires[i][1] for i in range(k)]\nreturn majoritaire(voisins)\n```",
          "correct": true,
          "feedback": "Bonne réponse : on calcule les distances, on trie par distance\ncroissante grâce à la fonction clé `premier`, on garde les k plus\nproches, et `majoritaire` réalise le vote.\n"
        }
      ],
      "explanation": "Implémentation simple et lisible. La fonction `majoritaire` se\nprogramme avec un dictionnaire qui compte les occurrences de\nchaque classe."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "dictionnaires",
        "calcul-distance"
      ],
      "title": "Des exemples rangés en dictionnaires",
      "partie": "mise-en-oeuvre",
      "statement": "Chaque fruit est rangé dans un dictionnaire, par exemple\n`e = {\"masse\": 152, \"diametre\": 5, \"fruit\": \"pomme\"}`, et le\nfruit à classer est `p = {\"masse\": 150, \"diametre\": 7}`. Quelle\nexpression donne le carré de la distance euclidienne entre `e` et\n`p` ?",
      "options": [
        {
          "text": "`(e[\"masse\"] - e[\"diametre\"]) ** 2 + (p[\"masse\"] - p[\"diametre\"]) ** 2`",
          "correct": false,
          "feedback": "Erreur : cette expression soustrait le diamètre à la masse d'un\nmême fruit, ce qui n'a pas de sens. Il faut comparer la masse de\n`e` à celle de `p`, puis le diamètre de `e` à celui de `p` ; ici on\nobtiendrait 42 058 au lieu de 8.\n"
        },
        {
          "text": "`e[\"masse\"] - p[\"masse\"] + e[\"diametre\"] - p[\"diametre\"]`",
          "correct": false,
          "feedback": "Erreur : les écarts ne sont ni mis au carré ni pris en valeur\nabsolue, si bien qu'ils peuvent se compenser. Ici\n$2 + (-2) = 0$, comme si les deux fruits étaient identiques.\n"
        },
        {
          "text": "`(e[0] - p[0]) ** 2 + (e[1] - p[1]) ** 2`",
          "correct": false,
          "feedback": "Erreur : un dictionnaire ne s'indexe pas par position mais par\nclé. `e[0]` provoque `KeyError`, car `0` n'est pas une clé du\ndictionnaire.\n"
        },
        {
          "text": "`(e[\"masse\"] - p[\"masse\"]) ** 2 + (e[\"diametre\"] - p[\"diametre\"]) ** 2`",
          "correct": true,
          "feedback": "Bonne réponse : on accède à chaque attribut par sa clé et on\nadditionne les carrés des écarts, attribut par attribut :\n$2^2 + 2^2 = 8$ pour cet exemple. Le champ `\"fruit\"` n'entre pas\ndans le calcul.\n"
        }
      ],
      "explanation": "Représenter un exemple par un dictionnaire rend le code lisible : on\nécrit `e[\"masse\"]` plutôt qu'un indice. Le calcul de distance reste\nle même : somme des carrés des écarts, attribut par attribut."
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "coût"
      ],
      "title": "Coût de la prédiction",
      "partie": "limites",
      "statement": "Si l'on a N exemples d'entraînement, quel est\nle **coût** d'une prédiction k-NN naïve ?",
      "options": [
        {
          "text": "Logarithmique en N",
          "correct": false,
          "feedback": "Erreur : il faut bien examiner tous les\nexemples.\n"
        },
        {
          "text": "Linéaire en N (nombre d'exemples)",
          "correct": true,
          "feedback": "Bonne réponse : on calcule une distance par exemple, soit N\ndistances, comme dans un parcours séquentiel. Pour retenir les k\nplus proches, on peut chercher k fois le minimum, ce qui reste\nlinéaire en N pour k fixé.\n"
        },
        {
          "text": "Constant, indépendant de N",
          "correct": false,
          "feedback": "Erreur : il faut bien comparer le\nnouveau point à chaque exemple.\n"
        },
        {
          "text": "Quadratique en N (double boucle)",
          "correct": false,
          "feedback": "Erreur : on ne fait pas de double boucle.\n"
        }
      ],
      "explanation": "Conséquence : la prédiction est lente sur de gros jeux\nd'entraînement, car chaque nouveau point est comparé à tous les\nexemples."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "forces-faiblesses"
      ],
      "title": "Forces et limites",
      "partie": "limites",
      "statement": "Quelle est une **limite importante** de k-NN ?",
      "options": [
        {
          "text": "Il ne peut pas faire de classification",
          "correct": false,
          "feedback": "Erreur : c'est précisément son rôle.\n"
        },
        {
          "text": "Il ne fonctionne qu'en dimension 2",
          "correct": false,
          "feedback": "Erreur : k-NN se généralise à toute\ndimension.\n"
        },
        {
          "text": "Lent à la prédiction et sensible aux échelles",
          "correct": true,
          "feedback": "Bonne réponse : tous les calculs ont lieu à la prédiction. Pour un\nmillion d'exemples décrits par de nombreux attributs, k-NN peut\ndevenir impraticable.\n"
        },
        {
          "text": "Il invente de nouveaux exemples au cours de la prédiction",
          "correct": false,
          "feedback": "L'algorithme se contente\nde comparer le nouveau\npoint aux exemples déjà\nfournis. Il n'en invente\naucun.\n"
        }
      ],
      "explanation": "Forces : simple, intuitif, sans phase d'entraînement. Limites :\nlent à la prédiction, car chaque nouveau point est comparé à tous\nles exemples, et sensible aux échelles des attributs."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "vote-pondéré"
      ],
      "title": "Vote pondéré",
      "partie": "principe",
      "statement": "Une variante de k-NN consiste à **pondérer le vote** des voisins. Pourquoi ?",
      "options": [
        {
          "text": "Pour donner plus de poids aux voisins proches",
          "correct": true,
          "feedback": "Bonne réponse : un voisin très proche\ndevrait peser plus qu'un voisin éloigné.\nAméliore souvent la qualité des\nprédictions et résout les égalités.\n"
        },
        {
          "text": "Pour économiser la mémoire",
          "correct": false,
          "feedback": "La pondération n'a aucun\neffet sur la consommation\nmémoire.\n"
        },
        {
          "text": "Pour rendre l'algorithme aléatoire",
          "correct": false,
          "feedback": "Cette pondération reste\nparfaitement déterministe.\nElle ne fait pas\nintervenir le hasard.\n"
        },
        {
          "text": "Pour ralentir intentionnellement tout le programme",
          "correct": false,
          "feedback": "Aucun procédé de\npondération n'a pour but\nde ralentir le\nprogramme.\n"
        }
      ],
      "explanation": "Vote uniforme = chaque voisin compte 1.\nVote pondéré = poids dépendant de la\ndistance. Variante classique du k-NN."
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "trace-detaillee"
      ],
      "title": "Trace détaillée",
      "partie": "mise-en-oeuvre",
      "statement": "Avec k = 3, on classe un point P. Les 5\nexemples les plus proches sont (par distance\ncroissante) : (1.0, classe A), (1.5, B), (2.0,\nA), (3.0, A), (4.0, B). Quelle classe prédit\nk-NN ?",
      "options": [
        {
          "text": "A",
          "correct": true,
          "feedback": "Bonne réponse : on garde les k = 3 plus\nproches : (1.0, A), (1.5, B), (2.0, A).\nVote : A = 2, B = 1 → A gagne.\n"
        },
        {
          "text": "B",
          "correct": false,
          "feedback": "Erreur : B n'a qu'un seul vote.\n"
        },
        {
          "text": "Indéterminé",
          "correct": false,
          "feedback": "Erreur : k-NN est déterministe.\n"
        },
        {
          "text": "Égalité",
          "correct": false,
          "feedback": "Erreur : pas d'égalité.\n"
        }
      ],
      "explanation": "Méthode : trier par distance, garder les k\npremiers, voter à la majorité. Les exemples\nau-delà de k (4ᵉ et 5ᵉ ici) ne comptent pas."
    },
    {
      "id": "q22",
      "difficulty": 3,
      "skills": [
        "jeux-de-tests",
        "code-knn"
      ],
      "title": "Un test réussi qui ne prouve rien",
      "partie": "mise-en-oeuvre",
      "statement": "La fonction suivante doit prédire la classe de `p` par vote\nmajoritaire de ses k plus proches voisins (`dist(a, b)` renvoie la\ndistance euclidienne) :\n\n```python\ndef premier(couple):\n    return couple[0]\n\ndef predire(p, exemples, k):\n    couples = [(dist(p, x), c) for (x, c) in exemples]\n    couples = sorted(couples, key=premier)\n    return couples[0][1]\n```\n\nLe test suivant passe sans erreur :\n\n```python\nassert predire((0, 0), [((1, 0), \"A\"), ((5, 5), \"B\"), ((0, 2), \"A\")], 3) == \"A\"\n```\n\nQue peut-on en conclure ?",
      "options": [
        {
          "text": "La fonction est correcte, puisque l'assertion ne déclenche pas d'erreur",
          "correct": false,
          "feedback": "Erreur : le succès d'un test ne garantit pas la correction d'un\nprogramme. Ici la fonction ignore `k` et renvoie la classe du seul\nplus proche voisin ; ce test tombe simplement sur un cas où cela\ndonne le bon résultat.\n"
        },
        {
          "text": "Le paramètre `k` est bien utilisé, le résultat correspondant au vote des 3 voisins",
          "correct": false,
          "feedback": "Erreur : le résultat coïncide avec le vote des trois voisins\n(A, A, B), mais la fonction ne fait aucun vote : elle renvoie\n`couples[0][1]`, la classe du plus proche. Le paramètre `k`\nn'apparaît nulle part dans le corps.\n"
        },
        {
          "text": "Rien sur le vote : ce test donne A qu'on fasse voter 1 ou 3 voisins",
          "correct": true,
          "feedback": "Bonne réponse : la fonction renvoie la classe du seul plus proche\nvoisin et ignore `k`. Ce test ne le révèle pas, car le plus proche\nvoisin `(1, 0)` et la majorité des trois voisins désignent tous\ndeux A. Le test\n`predire((0, 0), [((1, 0), \"A\"), ((0, 2), \"B\"), ((2, 0), \"B\")], 3)`,\nqui doit donner B, ferait apparaître l'erreur : la fonction\nrenvoie A.\n"
        },
        {
          "text": "Le test est mal écrit : avec k = 3, la prédiction attendue serait B",
          "correct": false,
          "feedback": "Erreur : les trois voisins sont `(1, 0)` de classe A à distance 1,\n`(0, 2)` de classe A à distance 2 et `(5, 5)` de classe B, plus\nloin. Le vote donne A deux voix contre une, et le test attend bien\nA.\n"
        }
      ],
      "explanation": "Un bon jeu de tests contient des cas qui distinguent une fonction\ncorrecte d'une fonction fausse. Pour k-NN, il faut au moins un cas\noù la classe du plus proche voisin est minoritaire parmi les k\nvoisins."
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "selection-voisins",
        "trace"
      ],
      "title": "Toujours le même voisin",
      "partie": "distance",
      "statement": "Pour éviter de trier toute la liste, on cherche k fois le plus\nproche voisin dans une liste de couples `(distance, classe)` :\n\n```python\ndef k_plus_proches(distances, k):\n    retenus = []\n    for etape in range(k):\n        i_min = 0\n        for i in range(len(distances)):\n            if distances[i][0] < distances[i_min][0]:\n                i_min = i\n        retenus.append(distances[i_min])\n    return retenus\n\nprint(k_plus_proches([(3, \"A\"), (1, \"B\"), (2, \"A\"), (5, \"B\")], 3))\n```\n\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "`[(1, 'B'), (2, 'A'), (3, 'A')]`",
          "correct": false,
          "feedback": "Erreur : c'est le résultat voulu par l'auteur, mais la liste\n`distances` n'est jamais modifiée. Les trois recherches de minimum\nportent sur les mêmes couples et trouvent toutes `(1, 'B')`.\n"
        },
        {
          "text": "`[(1, 'B'), (1, 'B'), (1, 'B')]`",
          "correct": true,
          "feedback": "Bonne réponse : à chaque étape, la recherche du minimum parcourt\nla même liste, qui n'a pas changé, et retrouve le couple\n`(1, 'B')`. Il est donc ajouté trois fois. Il faudrait écarter les\ncouples déjà retenus, par exemple en mémorisant leurs indices.\n"
        },
        {
          "text": "`[(3, 'A'), (1, 'B'), (2, 'A')]`",
          "correct": false,
          "feedback": "Erreur : la fonction ne recopie pas les trois premiers couples de\nla liste, elle cherche à chaque étape celui de plus petite\ndistance, qui est toujours `(1, 'B')`.\n"
        },
        {
          "text": "`[(1, 'B')]`",
          "correct": false,
          "feedback": "Erreur : la boucle externe fait k = 3 tours et chacun ajoute un\ncouple à `retenus`. La liste finale contient donc trois couples,\ntous égaux à `(1, 'B')`.\n"
        }
      ],
      "explanation": "Chercher k fois un minimum est une bonne idée, à condition de ne pas\nretrouver toujours le même élément : il faut exclure à chaque étape\nles couples déjà retenus. L'erreur passe facilement inaperçue, car\nles k « voisins » ont alors tous la même classe et le vote semble\nfonctionner.",
      "variants": [
        {
          "statement": "Pour éviter de trier toute la liste, on cherche k fois le plus\nproche voisin dans une liste de couples `(distance, classe)` :\n\n```python\ndef k_plus_proches(distances, k):\n    retenus = []\n    for etape in range(k):\n        i_min = 0\n        for i in range(len(distances)):\n            if distances[i][0] < distances[i_min][0]:\n                i_min = i\n        retenus.append(distances[i_min])\n    return retenus\n\nprint(k_plus_proches([(3, \"A\"), (1, \"B\"), (2, \"A\"), (5, \"B\")], 3))\n```\n\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`[(1, 'B'), (2, 'A'), (3, 'A')]`",
              "correct": false,
              "feedback": "Erreur : c'est le résultat voulu par l'auteur, mais la liste\n`distances` n'est jamais modifiée. Les trois recherches de minimum\nportent sur les mêmes couples et trouvent toutes `(1, 'B')`.\n"
            },
            {
              "text": "`[(1, 'B'), (1, 'B'), (1, 'B')]`",
              "correct": true,
              "feedback": "Bonne réponse : à chaque étape, la recherche du minimum parcourt\nla même liste, qui n'a pas changé, et retrouve le couple\n`(1, 'B')`. Il est donc ajouté trois fois. Il faudrait écarter les\ncouples déjà retenus, par exemple en mémorisant leurs indices.\n"
            },
            {
              "text": "`[(3, 'A'), (1, 'B'), (2, 'A')]`",
              "correct": false,
              "feedback": "Erreur : la fonction ne recopie pas les trois premiers couples de\nla liste, elle cherche à chaque étape celui de plus petite\ndistance, qui est toujours `(1, 'B')`.\n"
            },
            {
              "text": "`[(1, 'B')]`",
              "correct": false,
              "feedback": "Erreur : la boucle externe fait k = 3 tours et chacun ajoute un\ncouple à `retenus`. La liste finale contient donc trois couples,\ntous égaux à `(1, 'B')`.\n"
            }
          ],
          "explanation": "Chercher k fois un minimum est une bonne idée, à condition de ne pas\nretrouver toujours le même élément : il faut exclure à chaque étape\nles couples déjà retenus. L'erreur passe facilement inaperçue, car\nles k « voisins » ont alors tous la même classe et le vote semble\nfonctionner."
        },
        {
          "statement": "Pour éviter de trier toute la liste, on cherche k fois le plus\nproche voisin dans une liste de couples `(distance, classe)` :\n\n```python\ndef k_plus_proches(distances, k):\n    retenus = []\n    for etape in range(k):\n        i_min = 0\n        for i in range(len(distances)):\n            if distances[i][0] < distances[i_min][0]:\n                i_min = i\n        retenus.append(distances[i_min])\n    return retenus\n\nprint(k_plus_proches([(4, \"B\"), (2, \"A\"), (6, \"B\"), (3, \"A\")], 3))\n```\n\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`[(2, 'A'), (3, 'A'), (4, 'B')]`",
              "correct": false,
              "feedback": "Erreur : c'est le résultat voulu par l'auteur, mais la liste\n`distances` n'est jamais modifiée. Les trois recherches de minimum\nportent sur les mêmes couples et trouvent toutes `(2, 'A')`.\n"
            },
            {
              "text": "`[(2, 'A'), (2, 'A'), (2, 'A')]`",
              "correct": true,
              "feedback": "Bonne réponse : à chaque étape, la recherche du minimum parcourt\nla même liste, qui n'a pas changé, et retrouve le couple\n`(2, 'A')`. Il est donc ajouté trois fois. Il faudrait écarter les\ncouples déjà retenus, par exemple en mémorisant leurs indices.\n"
            },
            {
              "text": "`[(4, 'B'), (2, 'A'), (6, 'B')]`",
              "correct": false,
              "feedback": "Erreur : la fonction ne recopie pas les trois premiers couples de\nla liste, elle cherche à chaque étape celui de plus petite\ndistance, qui est toujours `(2, 'A')`.\n"
            },
            {
              "text": "`[(2, 'A')]`",
              "correct": false,
              "feedback": "Erreur : la boucle externe fait k = 3 tours et chacun ajoute un\ncouple à `retenus`. La liste finale contient donc trois couples,\ntous égaux à `(2, 'A')`.\n"
            }
          ],
          "explanation": "Chercher k fois un minimum est une bonne idée, à condition de ne pas\nretrouver toujours le même élément : il faut exclure à chaque étape\nles couples déjà retenus. L'erreur passe facilement inaperçue, car\nles k « voisins » ont alors tous la même classe et le vote semble\nfonctionner."
        },
        {
          "statement": "Pour éviter de trier toute la liste, on cherche k fois le plus\nproche voisin dans une liste de couples `(distance, classe)` :\n\n```python\ndef k_plus_proches(distances, k):\n    retenus = []\n    for etape in range(k):\n        i_min = 0\n        for i in range(len(distances)):\n            if distances[i][0] < distances[i_min][0]:\n                i_min = i\n        retenus.append(distances[i_min])\n    return retenus\n\nprint(k_plus_proches([(7, \"A\"), (5, \"B\"), (1, \"A\"), (9, \"B\"), (4, \"A\")], 2))\n```\n\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`[(1, 'A'), (4, 'A')]`",
              "correct": false,
              "feedback": "Erreur : c'est le résultat voulu par l'auteur, mais la liste\n`distances` n'est jamais modifiée. Les deux recherches de minimum\nportent sur les mêmes couples et trouvent toutes `(1, 'A')`.\n"
            },
            {
              "text": "`[(1, 'A'), (1, 'A')]`",
              "correct": true,
              "feedback": "Bonne réponse : à chaque étape, la recherche du minimum parcourt\nla même liste, qui n'a pas changé, et retrouve le couple\n`(1, 'A')`. Il est donc ajouté deux fois. Il faudrait écarter les\ncouples déjà retenus, par exemple en mémorisant leurs indices.\n"
            },
            {
              "text": "`[(7, 'A'), (5, 'B')]`",
              "correct": false,
              "feedback": "Erreur : la fonction ne recopie pas les deux premiers couples de\nla liste, elle cherche à chaque étape celui de plus petite\ndistance, qui est toujours `(1, 'A')`.\n"
            },
            {
              "text": "`[(1, 'A')]`",
              "correct": false,
              "feedback": "Erreur : la boucle externe fait k = 2 tours et chacun ajoute un\ncouple à `retenus`. La liste finale contient donc deux couples,\ntous égaux à `(1, 'A')`.\n"
            }
          ],
          "explanation": "Chercher k fois un minimum est une bonne idée, à condition de ne pas\nretrouver toujours le même élément : il faut exclure à chaque étape\nles couples déjà retenus. L'erreur passe facilement inaperçue, car\nles k « voisins » ont alors tous la même classe et le vote semble\nfonctionner."
        }
      ]
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "variantes"
      ],
      "title": "k-NN pour la régression",
      "partie": "limites",
      "statement": "Peut-on adapter k-NN pour **prédire un nombre**\n(régression) au lieu d'une classe ?",
      "options": [
        {
          "text": "Oui, mais à condition de doubler la valeur de $k$",
          "correct": false,
          "feedback": "Le passage à la régression\nn'a aucun rapport avec un\ndoublement de $k$. C'est\nla nature de l'agrégation\nfinale (vote majoritaire\nou moyenne) qui change.\n"
        },
        {
          "text": "Non, l'algorithme est strictement limité à la classification",
          "correct": false,
          "feedback": "L'algorithme s'adapte au\ncontraire très naturellement\nau cas de la régression,\ncomme expliqué dans la\nbonne réponse.\n"
        },
        {
          "text": "Non, il faut nécessairement un autre algorithme",
          "correct": false,
          "feedback": "L'algorithme des $k$ plus\nproches voisins s'étend\nnaturellement à la\nrégression, sans qu'il\nsoit nécessaire de le\nremplacer.\n"
        },
        {
          "text": "Oui, en remplaçant le vote par la moyenne des k voisins",
          "correct": true,
          "feedback": "Bonne réponse : si la cible est\nnumérique (prix d'une maison, note\nattendue), la classe « majoritaire » est\nremplacée par la moyenne des cibles des\nk voisins. Idée générale identique.\n"
        }
      ],
      "explanation": "Algorithme **k-NN régresseur** : très utilisé\nen pratique pour des prédictions de prix,\ndurées, scores. Même principe que la\nclassification."
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Synthèse",
      "partie": "limites",
      "statement": "Parmi les affirmations suivantes sur k-NN,\nlaquelle est **fausse** ?",
      "options": [
        {
          "text": "La phase d'entraînement se résume essentiellement à mémoriser les exemples.",
          "correct": false,
          "feedback": "Vrai : algorithme paresseux.\n"
        },
        {
          "text": "La distance euclidienne est un choix courant pour la mesure de proximité.",
          "correct": false,
          "feedback": "Vrai : c'est la mesure standard, mais\nd'autres existent (Manhattan, cosinus).\n"
        },
        {
          "text": "Plus k est grand, plus l'algorithme est sensible au bruit local.",
          "correct": true,
          "feedback": "Faux (donc bonne réponse) : c'est\nl'**inverse**. Avec k grand, le vote\nintègre beaucoup de voisins, ce qui\n**lisse** la décision et la rend\n**moins** sensible aux exemples isolés\n(bruit). Avec k petit (k = 1), un seul\nexemple aberrant change la prédiction.\n"
        },
        {
          "text": "k-NN est un algorithme d'apprentissage supervisé.",
          "correct": false,
          "feedback": "Vrai : il apprend depuis des exemples\nétiquetés.\n"
        }
      ],
      "explanation": "Mnémonique : k petit = local, sensible au\nbruit ; k grand = global, lissé. Choisir k\nempiriquement par validation."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "choix-distance"
      ],
      "title": "Choix de la distance",
      "partie": "distance",
      "statement": "Outre la distance euclidienne, on rencontre\nsouvent la **distance de Manhattan**\n(|Δx| + |Δy|). Quel critère permet de choisir\nentre ces deux distances ?",
      "options": [
        {
          "text": "Aucun critère, on choisit au hasard",
          "correct": false,
          "feedback": "Erreur : le choix de la distance influence\nfortement les résultats de l'algorithme. Il\nfaut le justifier en fonction du problème,\npas le tirer au hasard.\n"
        },
        {
          "text": "La distance euclidienne est toujours préférable car elle est plus précise",
          "correct": false,
          "feedback": "Erreur : ces deux distances sont des choix\nvalides selon le contexte. Aucune n'est\nuniversellement meilleure. Le choix dépend\nde la nature des données et de la\nstructure de la proximité que l'on veut\nmodéliser.\n"
        },
        {
          "text": "Cela dépend du problème (espace continu ou grille)",
          "correct": true,
          "feedback": "Bonne réponse : la distance de Manhattan est moins sensible aux\nvaleurs aberrantes dans les coordonnées, car elle n'élève pas les\nécarts au carré. Elle est aussi plus naturelle quand on se déplace\nsur une grille, comme dans les rues d'une ville. La distance\neuclidienne, qui mesure la ligne droite, convient bien aux espaces\ngéométriques classiques.\n"
        },
        {
          "text": "La distance de Manhattan est plus rapide à calculer",
          "correct": false,
          "feedback": "Erreur : la différence est négligeable en pratique, les deux\ndistances demandant un calcul par coordonnée. La rapidité n'est\npas le critère qui motive le choix.\n"
        }
      ],
      "explanation": "Repère pratique : essayer plusieurs distances, mesurer la qualité\ndes prédictions sur des exemples dont on connaît la classe, et\nretenir la meilleure."
    },
    {
      "id": "q27",
      "difficulty": 3,
      "skills": [
        "classes-desequilibrees"
      ],
      "title": "Classes déséquilibrées",
      "partie": "limites",
      "statement": "On veut détecter une maladie rare avec k-NN.\nLe jeu d'entraînement contient 95 % d'exemples\n« sain » et 5 % d'exemples « malade ». Avec\nk = 7, que va prédire l'algorithme pour\npresque tous les nouveaux patients ?",
      "options": [
        {
          "text": "« malade » à 5 % des nouveaux patients, par symétrie",
          "correct": false,
          "feedback": "Erreur : ce serait le comportement attendu\nd'une distribution aléatoire. Or k-NN ne\ntire rien au hasard ; il prédit en\nfonction des voisins, et ceux-ci sont\nprincipalement « sain ».\n"
        },
        {
          "text": "Une classe choisie au hasard, l'algorithme étant non déterministe",
          "correct": false,
          "feedback": "Erreur : k-NN est strictement\ndéterministe. Pour une même entrée et un\nmême jeu d'entraînement, il prédit\ntoujours la même classe.\n"
        },
        {
          "text": "« sain » presque toujours, les voisins étant surtout sains",
          "correct": true,
          "feedback": "Bonne réponse : c'est le piège classique des classes déséquilibrées.\nParmi les 7 voisins, les exemples « malade » sont presque toujours\nminoritaires, et le vote donne « sain » même pour un patient malade.\nLe taux global de bonnes prédictions peut sembler élevé (95 %) alors\nque presque aucun malade n'est détecté.\n"
        },
        {
          "text": "« malade » dans la majorité des cas, par sécurité",
          "correct": false,
          "feedback": "Erreur : k-NN n'a aucune logique de prudence sanitaire. Il vote\nsimplement à la majorité des voisins, et ceux-ci sont presque\ntoujours « sain ».\n"
        }
      ],
      "explanation": "Quand une classe est très rare, le vote majoritaire la prédit\nrarement. Mesurer uniquement le taux global de bonnes prédictions\nest trompeur : il faut aussi vérifier combien d'exemples de la\nclasse rare sont bien reconnus."
    },
    {
      "id": "q28",
      "difficulty": 2,
      "skills": [
        "calcul-concret"
      ],
      "title": "Calcul concret",
      "partie": "distance",
      "statement": "On classe le point `P = (3, 4)` avec k = 3 et\nla distance euclidienne. Les exemples\nétiquetés sont :\n`(0, 0, A)`, `(6, 8, A)`, `(2, 1, B)`,\n`(5, 4, B)`, `(3, 0, A)`. Quelle classe\nprédit k-NN ?",
      "options": [
        {
          "text": "A",
          "correct": false,
          "feedback": "Erreur : ce serait correct si A obtenait\nla majorité parmi les 3 plus proches.\nRefaire le calcul des distances : `(0, 0)`\n→ 5 ; `(6, 8)` → 5 ; `(2, 1)` → √10 ≈ 3,16 ;\n`(5, 4)` → 2 ; `(3, 0)` → 4. Les 3 plus\nproches sont `(5, 4, B)` (2), `(2, 1, B)`\n(3,16) et `(3, 0, A)` (4).\n"
        },
        {
          "text": "Égalité entre A et B",
          "correct": false,
          "feedback": "Erreur : avec k = 3 (impair) et deux\nclasses, il ne peut pas y avoir égalité.\nLe calcul donne B = 2 voix, A = 1 voix.\n"
        },
        {
          "text": "Indéterminé sans plus d'informations",
          "correct": false,
          "feedback": "Erreur : toutes les informations\nnécessaires sont fournies (point, k,\ndistance, exemples étiquetés). Le résultat\nest parfaitement calculable.\n"
        },
        {
          "text": "B",
          "correct": true,
          "feedback": "Bonne réponse. Distances calculées :\n`(0, 0)` → 5 ; `(6, 8)` → 5 ; `(2, 1)` →\n√10 ≈ 3,16 ; `(5, 4)` → 2 ; `(3, 0)` → 4.\nLes 3 plus proches sont `(5, 4, B)`,\n`(2, 1, B)`, `(3, 0, A)`. Vote : B = 2,\nA = 1 → B gagne.\n"
        }
      ],
      "explanation": "Méthode systématique : (1) calculer la\ndistance de P à chaque exemple ; (2) trier par\ndistance croissante ; (3) garder les k\npremiers ; (4) voter à la majorité. Sur les\npetits jeux, refaire le calcul à la main est\nle meilleur exercice de compréhension."
    },
    {
      "id": "q29",
      "difficulty": 1,
      "skills": [
        "distance",
        "manhattan",
        "calcul"
      ],
      "title": "La distance de Manhattan",
      "partie": "distance",
      "statement": "Quelle est la distance de Manhattan, somme des écarts en valeur\nabsolue sur chaque coordonnée, entre les points $(1, 2)$ et\n$(4, 6)$ ?",
      "options": [
        {
          "text": "$7$",
          "correct": true,
          "feedback": "Bonne réponse : $|1 - 4| + |2 - 6| = 3 + 4 =\n7$.\n"
        },
        {
          "text": "$5$",
          "correct": false,
          "feedback": "Erreur : $5$ est la distance euclidienne, à vol d'oiseau ; la\ndistance de Manhattan additionne les écarts.\n"
        },
        {
          "text": "$12$",
          "correct": false,
          "feedback": "Erreur : les écarts s'additionnent, ils ne se multiplient pas.\n"
        },
        {
          "text": "$3$",
          "correct": false,
          "feedback": "Erreur : $3$ n'est que l'écart sur la première coordonnée.\n"
        }
      ],
      "explanation": "La distance de Manhattan compte les déplacements le long des axes,\ncomme dans une ville quadrillée ; elle se calcule sans racine\ncarrée.",
      "variants": [
        {
          "statement": "Quelle est la distance de Manhattan, somme des écarts en valeur\nabsolue sur chaque coordonnée, entre les points $(1, 2)$ et\n$(4, 6)$ ?",
          "options": [
            {
              "text": "$7$",
              "correct": true,
              "feedback": "Bonne réponse : $|1 - 4| + |2 - 6| = 3 + 4 =\n7$.\n"
            },
            {
              "text": "$5$",
              "correct": false,
              "feedback": "Erreur : $5$ est la distance euclidienne, à vol d'oiseau ; la\ndistance de Manhattan additionne les écarts.\n"
            },
            {
              "text": "$12$",
              "correct": false,
              "feedback": "Erreur : les écarts s'additionnent, ils ne se multiplient pas.\n"
            },
            {
              "text": "$3$",
              "correct": false,
              "feedback": "Erreur : $3$ n'est que l'écart sur la première coordonnée.\n"
            }
          ],
          "explanation": "La distance de Manhattan compte les déplacements le long des axes,\ncomme dans une ville quadrillée ; elle se calcule sans racine\ncarrée."
        },
        {
          "statement": "Quelle est la distance de Manhattan, somme des écarts en valeur\nabsolue sur chaque coordonnée, entre les points $(0, 5)$ et\n$(3, 1)$ ?",
          "options": [
            {
              "text": "$7$",
              "correct": true,
              "feedback": "Bonne réponse : $|0 - 3| + |5 - 1| = 3 + 4 =\n7$.\n"
            },
            {
              "text": "$5$",
              "correct": false,
              "feedback": "Erreur : $5$ est la distance euclidienne, à vol d'oiseau ; la\ndistance de Manhattan additionne les écarts.\n"
            },
            {
              "text": "$12$",
              "correct": false,
              "feedback": "Erreur : les écarts s'additionnent, ils ne se multiplient pas.\n"
            },
            {
              "text": "$3$",
              "correct": false,
              "feedback": "Erreur : $3$ n'est que l'écart sur la première coordonnée.\n"
            }
          ],
          "explanation": "La distance de Manhattan compte les déplacements le long des axes,\ncomme dans une ville quadrillée ; elle se calcule sans racine\ncarrée."
        },
        {
          "statement": "Quelle est la distance de Manhattan, somme des écarts en valeur\nabsolue sur chaque coordonnée, entre les points $(2, 2)$ et\n$(7, 9)$ ?",
          "options": [
            {
              "text": "$12$",
              "correct": true,
              "feedback": "Bonne réponse : $|2 - 7| + |2 - 9| = 5 + 7 =\n12$.\n"
            },
            {
              "text": "$8$",
              "correct": false,
              "feedback": "Erreur : $8$ est la distance euclidienne, à vol d'oiseau ; la\ndistance de Manhattan additionne les écarts.\n"
            },
            {
              "text": "$35$",
              "correct": false,
              "feedback": "Erreur : les écarts s'additionnent, ils ne se multiplient pas.\n"
            },
            {
              "text": "$5$",
              "correct": false,
              "feedback": "Erreur : $5$ n'est que l'écart sur la première coordonnée.\n"
            }
          ],
          "explanation": "La distance de Manhattan compte les déplacements le long des axes,\ncomme dans une ville quadrillée ; elle se calcule sans racine\ncarrée."
        }
      ]
    },
    {
      "id": "q30",
      "difficulty": 2,
      "skills": [
        "distance",
        "carre",
        "optimisation"
      ],
      "title": "Comparer des distances sans racine carrée",
      "partie": "distance",
      "statement": "Pour trouver les plus proches voisins de $(0, 0)$, un programme\ncompare les carrés des distances euclidiennes au lieu des\ndistances. Quelle valeur calcule-t-il pour le point $(3, 4)$,\net pourquoi ce choix ?",
      "options": [
        {
          "text": "$25$, la racine ne changeant pas l'ordre",
          "correct": true,
          "feedback": "Bonne réponse : $3^2 + 4^2 = 25$. Comparer les carrés donne\nle même classement que comparer les distances, en évitant un\ncalcul coûteux.\n"
        },
        {
          "text": "$5$, la distance elle-même, seule valeur exacte",
          "correct": false,
          "feedback": "Erreur : $5$ est la distance, mais le programme s'en passe ;\nson carré $25$ suffit pour comparer.\n"
        },
        {
          "text": "$7$, la somme des coordonnées, plus rapide",
          "correct": false,
          "feedback": "Erreur : la somme des coordonnées n'est pas une distance.\n"
        },
        {
          "text": "$25$, mais ce choix fausse l'ordre des voisins",
          "correct": false,
          "feedback": "Erreur : la valeur est juste, mais l'ordre est préservé, puisque\nla racine carrée est croissante.\n"
        }
      ],
      "explanation": "Pour classer des distances, le carré suffit : si $d_1 < d_2$, alors\n$d_1^2 < d_2^2$. On économise une racine carrée par exemple.",
      "variants": [
        {
          "statement": "Pour trouver les plus proches voisins de $(0, 0)$, un programme\ncompare les carrés des distances euclidiennes au lieu des\ndistances. Quelle valeur calcule-t-il pour le point $(3, 4)$,\net pourquoi ce choix ?",
          "options": [
            {
              "text": "$25$, la racine ne changeant pas l'ordre",
              "correct": true,
              "feedback": "Bonne réponse : $3^2 + 4^2 = 25$. Comparer les carrés donne\nle même classement que comparer les distances, en évitant un\ncalcul coûteux.\n"
            },
            {
              "text": "$5$, la distance elle-même, seule valeur exacte",
              "correct": false,
              "feedback": "Erreur : $5$ est la distance, mais le programme s'en passe ;\nson carré $25$ suffit pour comparer.\n"
            },
            {
              "text": "$7$, la somme des coordonnées, plus rapide",
              "correct": false,
              "feedback": "Erreur : la somme des coordonnées n'est pas une distance.\n"
            },
            {
              "text": "$25$, mais ce choix fausse l'ordre des voisins",
              "correct": false,
              "feedback": "Erreur : la valeur est juste, mais l'ordre est préservé, puisque\nla racine carrée est croissante.\n"
            }
          ],
          "explanation": "Pour classer des distances, le carré suffit : si $d_1 < d_2$, alors\n$d_1^2 < d_2^2$. On économise une racine carrée par exemple."
        },
        {
          "statement": "Pour trouver les plus proches voisins de $(0, 0)$, un programme\ncompare les carrés des distances euclidiennes au lieu des\ndistances. Quelle valeur calcule-t-il pour le point $(6, 8)$,\net pourquoi ce choix ?",
          "options": [
            {
              "text": "$100$, la racine ne changeant pas l'ordre",
              "correct": true,
              "feedback": "Bonne réponse : $6^2 + 8^2 = 100$. Comparer les carrés donne\nle même classement que comparer les distances, en évitant un\ncalcul coûteux.\n"
            },
            {
              "text": "$10$, la distance elle-même, seule valeur exacte",
              "correct": false,
              "feedback": "Erreur : $10$ est la distance, mais le programme s'en passe ;\nson carré $100$ suffit pour comparer.\n"
            },
            {
              "text": "$14$, la somme des coordonnées, plus rapide",
              "correct": false,
              "feedback": "Erreur : la somme des coordonnées n'est pas une distance.\n"
            },
            {
              "text": "$100$, mais ce choix fausse l'ordre des voisins",
              "correct": false,
              "feedback": "Erreur : la valeur est juste, mais l'ordre est préservé, puisque\nla racine carrée est croissante.\n"
            }
          ],
          "explanation": "Pour classer des distances, le carré suffit : si $d_1 < d_2$, alors\n$d_1^2 < d_2^2$. On économise une racine carrée par exemple."
        },
        {
          "statement": "Pour trouver les plus proches voisins de $(0, 0)$, un programme\ncompare les carrés des distances euclidiennes au lieu des\ndistances. Quelle valeur calcule-t-il pour le point $(5, 12)$,\net pourquoi ce choix ?",
          "options": [
            {
              "text": "$169$, la racine ne changeant pas l'ordre",
              "correct": true,
              "feedback": "Bonne réponse : $5^2 + 12^2 = 169$. Comparer les carrés donne\nle même classement que comparer les distances, en évitant un\ncalcul coûteux.\n"
            },
            {
              "text": "$13$, la distance elle-même, seule valeur exacte",
              "correct": false,
              "feedback": "Erreur : $13$ est la distance, mais le programme s'en passe ;\nson carré $169$ suffit pour comparer.\n"
            },
            {
              "text": "$17$, la somme des coordonnées, plus rapide",
              "correct": false,
              "feedback": "Erreur : la somme des coordonnées n'est pas une distance.\n"
            },
            {
              "text": "$169$, mais ce choix fausse l'ordre des voisins",
              "correct": false,
              "feedback": "Erreur : la valeur est juste, mais l'ordre est préservé, puisque\nla racine carrée est croissante.\n"
            }
          ],
          "explanation": "Pour classer des distances, le carré suffit : si $d_1 < d_2$, alors\n$d_1^2 < d_2^2$. On économise une racine carrée par exemple."
        }
      ]
    },
    {
      "id": "q31",
      "difficulty": 3,
      "skills": [
        "distance",
        "echelle",
        "normalisation"
      ],
      "title": "Des attributs d'échelles différentes",
      "partie": "distance",
      "statement": "Pour prédire si un client achète, on utilise son âge, entre $18$ et\n$80$, et son revenu annuel, entre $10\\,000$ et $100\\,000$. Quel\nproblème pose la distance euclidienne sur ces deux attributs tels\nquels ?",
      "options": [
        {
          "text": "Le revenu domine, l'âge n'ayant presque plus d'influence",
          "correct": true,
          "feedback": "Bonne réponse : un écart de $10\\,000$ sur le revenu écrase un\nécart de $20$ sur l'âge. Il faut ramener les attributs à une\nmême échelle avant de calculer les distances.\n"
        },
        {
          "text": "La distance ne peut pas se calculer avec deux attributs",
          "correct": false,
          "feedback": "Erreur : la distance euclidienne se calcule avec autant\nd'attributs que l'on veut.\n"
        },
        {
          "text": "L'âge domine la distance, étant le premier attribut",
          "correct": false,
          "feedback": "Erreur : l'ordre des attributs n'intervient pas ; c'est\nl'ampleur des écarts qui compte.\n"
        },
        {
          "text": "Aucun, les deux attributs étant des nombres positifs",
          "correct": false,
          "feedback": "Erreur : être des nombres ne suffit pas, les échelles doivent\nêtre comparables.\n"
        }
      ],
      "explanation": "Avant un k-NN, on normalise souvent chaque attribut, par exemple\nentre $0$ et $1$, pour qu'aucun ne domine la distance."
    },
    {
      "id": "q32",
      "difficulty": 1,
      "skills": [
        "principe",
        "k-egal-un"
      ],
      "title": "Le cas k égal à un",
      "partie": "principe",
      "statement": "Avec $k = 1$, quelle classe l'algorithme des k plus proches voisins\nattribue-t-il à un nouveau point ?",
      "options": [
        {
          "text": "La classe de l'exemple le plus proche",
          "correct": true,
          "feedback": "Bonne réponse : avec un seul voisin, le vote se réduit à sa\nclasse. C'est le cas le plus simple, et le plus sensible aux\nexemples mal étiquetés.\n"
        },
        {
          "text": "La classe la plus fréquente dans tout le jeu de données",
          "correct": false,
          "feedback": "Erreur : ce serait le cas où $k$ vaut le nombre total\nd'exemples.\n"
        },
        {
          "text": "Une classe tirée au hasard",
          "correct": false,
          "feedback": "Erreur : l'algorithme est déterministe, hors cas d'égalité des\ndistances.\n"
        },
        {
          "text": "La classe de l'exemple le plus éloigné",
          "correct": false,
          "feedback": "Erreur : seuls les voisins proches votent.\n"
        }
      ],
      "explanation": "Le plus proche voisin est l'algorithme le plus simple de la\nfamille : efficace sur des données propres, fragile sur des\ndonnées bruitées."
    },
    {
      "id": "q33",
      "difficulty": 3,
      "skills": [
        "principe",
        "vote",
        "egalite",
        "k-impair"
      ],
      "title": "Un vote sans majorité",
      "partie": "principe",
      "statement": "Avec $k = 4$, les quatre plus proches voisins d'un point ont pour\nclasses A, A, B et B. Que se passe-t-il, et comment l'éviter ?",
      "options": [
        {
          "text": "Le vote est indécis ; un $k$ impair évite cette égalité",
          "correct": true,
          "feedback": "Bonne réponse : deux voix contre deux ne désignent rien. Avec\ndeux classes, un $k$ impair garantit une majorité.\n"
        },
        {
          "text": "La classe A l'emporte, étant la première rencontrée",
          "correct": false,
          "feedback": "Erreur : l'ordre d'apparition n'a aucune valeur ; il faut une\nrègle explicite.\n"
        },
        {
          "text": "Le point est rejeté du jeu de données faute de majorité",
          "correct": false,
          "feedback": "Erreur : le point doit recevoir une classe ; c'est le paramètre\n$k$ qu'il faut ajuster.\n"
        },
        {
          "text": "L'algorithme recommence avec $k = 8$, le double",
          "correct": false,
          "feedback": "Erreur : doubler $k$ garde un nombre pair et le risque\nd'égalité.\n"
        }
      ],
      "explanation": "Choisir $k$ impair est une règle pratique pour deux classes ; avec\ndavantage de classes, une règle de départage reste nécessaire."
    },
    {
      "id": "q34",
      "difficulty": 2,
      "skills": [
        "limites",
        "k-grand"
      ],
      "title": "Quand k vaut le nombre d'exemples",
      "partie": "limites",
      "statement": "Le jeu de données compte $100$ exemples, $70$ de classe A et $30$ de\nclasse B. Que prédit l'algorithme pour tout nouveau point si $k$\nvaut $100$ ?",
      "options": [
        {
          "text": "Toujours A, la classe majoritaire du jeu entier",
          "correct": true,
          "feedback": "Bonne réponse : tous les exemples votent, quelle que soit la\nposition du point. Le résultat ne dépend plus du point, ce qui\nrend l'algorithme inutile.\n"
        },
        {
          "text": "Toujours B, la classe la plus rare du jeu",
          "correct": false,
          "feedback": "Erreur : le vote donne la majorité, A.\n"
        },
        {
          "text": "La classe du point le plus proche",
          "correct": false,
          "feedback": "Erreur : ce serait $k = 1$.\n"
        },
        {
          "text": "Une erreur, $k$ devant être inférieur à $10$",
          "correct": false,
          "feedback": "Erreur : aucune règle n'impose cette borne, mais un $k$ trop\ngrand noie les voisins proches.\n"
        }
      ],
      "explanation": "Un $k$ trop petit suit le bruit, un $k$ trop grand ignore la\nposition : le bon $k$ se trouve entre les deux, par essais."
    },
    {
      "id": "q35",
      "difficulty": 3,
      "skills": [
        "limites",
        "bruit",
        "etiquettes"
      ],
      "title": "Un exemple mal étiqueté",
      "partie": "limites",
      "statement": "Dans le jeu de données, un exemple de classe A a été étiqueté B\npar erreur. Quels nouveaux points cette erreur affecte-t-elle le\nplus, et pour quelle valeur de $k$ ?",
      "options": [
        {
          "text": "Les points proches de cet exemple, surtout avec $k$ petit",
          "correct": true,
          "feedback": "Bonne réponse : avec $k = 1$, l'exemple fautif impose sa classe à\ntout son voisinage ; avec un $k$ plus grand, les autres voisins\ncorrigent son vote.\n"
        },
        {
          "text": "Tous les points du jeu, quel que soit $k$",
          "correct": false,
          "feedback": "Erreur : l'exemple ne vote que pour les points dont il est\nvoisin.\n"
        },
        {
          "text": "Aucun point, une erreur isolée étant toujours ignorée",
          "correct": false,
          "feedback": "Erreur : avec $k = 1$, elle n'est pas ignorée du tout.\n"
        },
        {
          "text": "Les points les plus éloignés de cet exemple",
          "correct": false,
          "feedback": "Erreur : les points éloignés ne le comptent pas parmi leurs\nvoisins.\n"
        }
      ],
      "explanation": "Augmenter $k$ lisse les erreurs d'étiquetage, au prix d'une\nfrontière entre classes moins précise."
    },
    {
      "id": "q36",
      "difficulty": 2,
      "skills": [
        "mise-en-oeuvre",
        "evaluation",
        "jeu-de-test"
      ],
      "title": "Mesurer la qualité des prédictions",
      "partie": "mise-en-oeuvre",
      "statement": "Pour savoir si son k-NN prédit bien, Théo l'évalue sur les\nexemples qui ont servi à construire le jeu de données et obtient\n$100$ % de bonnes réponses. Pourquoi ce chiffre ne prouve-t-il\nrien ?",
      "options": [
        {
          "text": "Chaque exemple est son propre voisin, à distance nulle",
          "correct": true,
          "feedback": "Bonne réponse : l'algorithme retrouve la classe de l'exemple\nlui-même. Il faut évaluer sur des exemples mis de côté, qui\nn'ont pas servi à prédire.\n"
        },
        {
          "text": "Le pourcentage est mal calculé, les erreurs étant oubliées",
          "correct": false,
          "feedback": "Erreur : le calcul est juste, c'est le protocole qui est\nbiaisé.\n"
        },
        {
          "text": "$100$ % est impossible pour un algorithme d'apprentissage",
          "correct": false,
          "feedback": "Erreur : rien n'interdit un score parfait sur des données\nfaciles ; ici il est seulement trompeur.\n"
        },
        {
          "text": "Le k-NN ne s'évalue pas, faute de phase d'entraînement",
          "correct": false,
          "feedback": "Erreur : il s'évalue très bien, sur des données séparées.\n"
        }
      ],
      "explanation": "Séparer les données en un jeu pour prédire et un jeu pour évaluer\nest la règle pour mesurer honnêtement un algorithme\nd'apprentissage."
    }
  ]
}