Comment utiliser cette page

Cette page reprend un sujet d'entraînement complet, avec deux exercices : les polynômes (représentés avec des listes chaînées) et la nutrition (représentée avec des objets et des classes). Pour chaque exercice, l'énoncé est d'abord reformulé de façon très explicite, puis chaque question est corrigée en détail, avec le raisonnement complet, une trace sur un exemple concret, et le code Python.

Utilisation des démonstrations
Une démonstration avance uniquement quand vous cliquez sur « Suivant » ou « Précédent » : rien ne bouge tout seul. Un bouton « Lecture automatique » est disponible si vous préférez laisser les étapes défiler seules, à une vitesse que vous choisissez.
📄 Le code de cette page, en fichier téléchargeable
Le fichier src/entrainement_bac_polynomes_nutrition.py, dans le sous-dossier src à côté de cette page, contient tout le code des deux exercices, avec un test pour chaque question. Après l'avoir téléchargé, on l'exécute dans un terminal, dans le dossier où il a été enregistré, avec python3 entrainement_bac_polynomes_nutrition.py (Python doit être installé sur l'ordinateur). Le fichier affiche « OK » pour chaque question s'il n'y a pas d'erreur.
Une précision importante avant de commencer
Le corrigé officiel de ce sujet contient deux erreurs de calcul : dans l'exemple de l'énoncé de l'exercice 1 (question 4), et dans l'algorithme du corrigé officiel de cette même question. Cette page ne recopie pas ces erreurs : chaque résultat donné ci-dessous a été recalculé et vérifié en exécutant réellement le code Python (visible dans le fichier téléchargeable ci-dessus). Vous pouvez donc faire confiance à tous les résultats de cette page.

Exercice 1 — Les polynômes : énoncé

Contexte

On veut représenter, en mémoire, des polynômes à une variable réelle x. Un polynôme est représenté par une liste chaînée de monômes. Un monôme est représenté par un couple (coefficient, exposant).

Exemple : le polynôme p1(x) = 2x³ − x + 2 est représenté par la liste chaînée :

(2, 3)
→
(-1, 1)
→
(2, 0)
→
∅

Chaque monôme est rangé par exposant décroissant : d'abord x³, puis x¹, puis x⁰. Le symbole ∅ marque la fin de la liste (la liste vide).

Opérations primitives disponibles

On dispose d'une structure de données abstraite de liste, avec ces opérations déjà toutes prêtes :

OpérationRôle
creer_liste()renvoie la liste vide
est_vide(L)renvoie True si la liste L est vide, False sinon
ajouter_en_tete(x, L)ajoute l'élément x en tête de la liste L
supprimer_en_tete(L)supprime l'élément de tête de la liste L
tete(L)renvoie l'élément de tête de L
queue(L)renvoie la liste L privée de son élément de tête
Les 4 questions de l'exercice
  1. À l'aide de ces opérations primitives, écrire les instructions qui créent une liste nommée p1, représentant le polynôme p1 ci-dessus.
  2. Que renvoie l'instruction tete(p1) ? Que renvoie l'instruction queue(p1) ?
  3. L'image de 2 par le polynôme p1 est p1(2) = 2×2³ − 2 + 2 = 16. À l'aide des opérations primitives, écrire un algorithme valuation qui prend en argument une liste représentant un polynôme et un nombre x, et qui renvoie l'image de x par ce polynôme.
  4. Écrire un algorithme qui, étant données deux listes représentant deux polynômes, renvoie la liste représentant leur somme. On l'appliquera à p1 et à p2(x) = 5x⁴ + 3x² + 1.

1. Questions 1 à 3 — corrigé détaillé

1.1 — Question 1 : construire la liste p1

Principe
On part de la liste vide, et on ajoute les monômes en tête, en partant du dernier (celui de plus petit exposant). Ainsi, le dernier monôme ajouté — celui de plus grand exposant — se retrouve en tête, ce qui donne bien l'ordre décroissant attendu.
p1 = creer_liste()
p1 = ajouter_en_tete((2, 0), p1)     # on ajoute d'abord le terme constant
p1 = ajouter_en_tete((-1, 1), p1)    # puis le terme en x
p1 = ajouter_en_tete((2, 3), p1)     # puis le terme en x³, qui devient la tête

Trace détaillée de la construction, étape par étape :

InstructionValeur de p1 après l'instruction
p1 = creer_liste()∅
p1 = ajouter_en_tete((2, 0), p1)(2, 0) → ∅
p1 = ajouter_en_tete((-1, 1), p1)(-1, 1) → (2, 0) → ∅
p1 = ajouter_en_tete((2, 3), p1)(2, 3) → (-1, 1) → (2, 0) → ∅
tête de la liste monôme qui vient d'être ajouté
Étape 1 / 1

1.2 — Question 2 : tete(p1) et queue(p1)

Réponse

tete(p1) renvoie l'élément de tête de la liste, c'est-à-dire (2, 3) (le premier monôme).

queue(p1) renvoie la liste privée de sa tête, c'est-à-dire la liste (-1, 1) → (2, 0) → ∅ (tous les monômes sauf le premier).

1.3 — Question 3 : l'algorithme valuation

Principe, en une phrase
Pour calculer l'image d'un nombre x par un polynôme, on additionne la contribution de chaque monôme (coefficient × xexposant), un par un, en parcourant la liste avec queue jusqu'à la liste vide.
  1. Cas 1 — la liste est vide : il ne reste plus aucun monôme à ajouter, donc on renvoie 0.
  2. Cas 2 — la liste n'est pas vide : on lit le monôme de tête (son coefficient et son exposant), on calcule sa contribution coefficient × xexposant, et on y ajoute le résultat de valuation appliqué au reste de la liste (queue).
def valuation(liste, x):
    if est_vide(liste):
        return 0
    temp = tete(liste)
    return temp[0] * x ** temp[1] + valuation(queue(liste), x)

Trace détaillée de valuation(p1, 2) :

Appeltemp = tete(liste)Contribution de tempRésultat de l'appel
valuation((2,3)→(-1,1)→(2,0)→∅, 2)(2, 3)2 × 2³ = 1616 + valuation((-1,1)→(2,0)→∅, 2)
valuation((-1,1)→(2,0)→∅, 2)(-1, 1)-1 × 2¹ = -2-2 + valuation((2,0)→∅, 2)
valuation((2,0)→∅, 2)(2, 0)2 × 2⁰ = 22 + valuation(∅, 2)
valuation(∅, 2)— (liste vide)—0 (cas de base)

En remontant les appels : 2 + 0 = 2, puis -2 + 2 = 0, puis 16 + 0 = 16. On retrouve bien valuation(p1, 2) = 16, comme annoncé dans l'énoncé.

2. Question 4 — additionner deux polynômes

Principe général, en une phrase
Pour additionner deux polynômes, on ajoute les monômes du premier polynôme, un par un, dans le second polynôme : à chaque fois, on insère un seul monôme dans une liste déjà triée, en respectant l'ordre décroissant des exposants.

On découpe donc le problème en deux algorithmes : addition_monome_polynome, qui insère un seul monôme dans un polynôme, et addition_polynome, qui utilise le premier pour insérer tous les monômes d'un polynôme dans un autre, un par un.

2.1 — Insérer un monôme dans un polynôme : les 3 cas possibles

Pour insérer un monôme element = (coefficient, exposant) en tête d'une liste non vide dont la tête s'appelle premier, il faut comparer l'exposant de element à celui de premier. Il y a exactement 3 cas :

  1. Cas 1 — l'exposant de element est plus grand que celui de premier : element n'a pas d'équivalent dans le polynôme à cet endroit, donc on l'insère directement, avant premier.
  2. Cas 2 — les deux exposants sont égaux : les deux monômes portent sur la même puissance de x, donc on additionne leurs coefficients.
    • Si la somme des coefficients est différente de 0 : on remplace premier par le nouveau monôme, avec ce nouveau coefficient (même exposant).
    • Si la somme des coefficients vaut 0 : le monôme disparaît complètement du polynôme (un coefficient nul ne s'écrit pas).
  3. Cas 3 — l'exposant de element est plus petit que celui de premier : ce n'est pas encore le bon endroit pour insérer element ; on garde premier en tête, et on recommence la même recherche dans le reste de la liste (queue).
Cas particulier : la liste est vide
Si la liste dans laquelle on insère est vide (par exemple, à force d'avancer avec queue dans le cas 3, on finit par atteindre la fin de la liste), il n'y a plus rien à comparer : on ajoute simplement element dans une liste vide.
def addition_monome_polynome(element, liste):    if est_vide(liste):        L = creer_liste()        L = ajouter_en_tete(element, L)        return L    premier = tete(liste)    if element[1] > premier[1]:        return ajouter_en_tete(element, liste)    elif element[1] == premier[1]:        nouveau_coeff = premier[0] + element[0]        reste = queue(liste)        if nouveau_coeff == 0:            return reste        else:            return ajouter_en_tete((nouveau_coeff, element[1]), reste)    else:        L = addition_monome_polynome(element, queue(liste))        return ajouter_en_tete(premier, L)
Pourquoi element[1] et pas element.exposant ?
Un monôme est représenté par un couple Python (coefficient, exposant), donc element[0] est son coefficient et element[1] est son exposant (comme pour n'importe quel tuple : [0] désigne le premier élément, [1] le second).

2.2 — Additionner deux polynômes complets

Principe
On insère, un par un, tous les monômes du premier polynôme (liste1) dans le second (liste2), avec addition_monome_polynome. S'il ne reste plus de monôme dans liste1 à insérer, le résultat est simplement liste2 (avec tous les monômes déjà insérés).
def addition_polynome(liste1, liste2):
    if est_vide(liste1):
        return liste2
    L = addition_polynome(queue(liste1), liste2)
    return addition_monome_polynome(tete(liste1), L)

Cet algorithme insère d'abord les monômes les plus profonds de liste1 (ceux de plus petit exposant), puis remonte vers la tête : le tout dernier monôme inséré est donc tete(liste1), celui de plus grand exposant.

2.3 — Trace détaillée sur l'exemple : p1 + p2

On applique addition_polynome(p1, p2) avec p1 = (2,3) → (-1,1) → (2,0) → ∅ et p2 = (5,4) → (3,2) → (1,0) → ∅. D'après le principe ci-dessus, cela revient à insérer, un par un dans p2, les monômes de p1 en commençant par le dernier : d'abord (2,0), puis (-1,1), puis (2,3).

monôme comparé en ce moment monôme inséré ou modifié
Étape 1 / 1
Chaîne affichée : le polynôme résultat, en cours de construction (au départ, c'est une copie de p2).
Monôme de p1 inséréRésultat après insertion
(départ : copie de p2)(5,4) → (3,2) → (1,0) → ∅
(2, 0)(5,4) → (3,2) → (3,0) → ∅ — fusion avec (1,0) : 2+1=3
(-1, 1)(5,4) → (3,2) → (-1,1) → (3,0) → ∅ — aucun terme en x¹ dans la liste, insertion
(2, 3)(5,4) → (2,3) → (3,2) → (-1,1) → (3,0) → ∅ — aucun terme en x³, insertion

p1 + p2 = 5x⁴ + 2x³ + 3x² − x + 3

Vérification par le calcul direct

On peut vérifier ce résultat sans passer par les listes chaînées, en additionnant terme à terme :

p1(x) + p2(x) = (2x³ − x + 2) + (5x⁴ + 3x² + 1) = 5x⁴ + 2x³ + 3x² + (−x) + (2 + 1) = 5x⁴ + 2x³ + 3x² − x + 3

C'est exactement ce que renvoie addition_polynome(p1, p2), et c'est ce que confirme le fichier téléchargeable de cette page, qui vérifie ce résultat de deux façons différentes (en construisant la liste, et en calculant valuation sur plusieurs valeurs de x).

📄 Télécharger ce code (exercice 1)
src/entrainement_bac_polynomes_nutrition.py — contient les opérations sur les listes chaînées, valuation, addition_monome_polynome, addition_polynome, et les tests des 4 questions.

Exercice 2 — Nutrition : énoncé

Contexte

Sur les aliments vendus en Europe, les informations nutritionnelles pour 100 g doivent être affichées. Voici celles du riz blanc utilisé dans cet exercice :

InformationValeur pour 100 g de riz
Énergie1532 kJ
Lipides0,6 g
Glucides81 g
Fibres1,3 g
Protéines7,2 g
Sel0,01 g
Point important à retenir pour tout l'exercice
Toutes ces valeurs sont données pour 100 g de riz, jamais pour la quantité réellement utilisée dans un plat. C'est ce détail qu'il faudra prendre en compte à chaque calcul de quantité.
La classe aliment
class aliment:
    def __init__(self, nom, energie, lipide, glucide, fibre, proteine, sel):
        self.nom = nom
        self.energie = energie
        self.lipide = lipide
        self.glucide = glucide
        self.fibre = fibre
        self.proteine = proteine
        self.sel = sel

Chaque attribut correspond à une valeur du tableau ci-dessus, pour 100 g de l'aliment.

Les questions de l'exercice
  1. Quelle instruction permet d'instancier l'objet riz (« riz blanc »), conformément au tableau des informations nutritionnelles ?
  2. Écrire la méthode __repr__ de la classe aliment, qui affiche les informations nutritionnelles d'un objet, comme dans l'exemple ci-dessous :
    >>> riz Intitulé : riz blanc Énergie : 1532 kJ Lipides : 0.6 g Glucides : 81 g Fibres : 1.3 g Proteines : 7.2 g Sel : 0.01g
  3. Quelle instruction permet d'obtenir la quantité de glucides contenue dans n grammes de l'objet riz ?
  4. On veut représenter des plats composés de plusieurs aliments, chacun avec sa quantité en grammes. Une salade de riz est composée de 500 g de riz, 200 g de thon au naturel, 200 g de tomates, 110 g d'œufs durs et 300 g de maïs. On représente ce plat par la liste Python :
    salade_de_riz = [(riz, 500), (thon, 200), (tomate, 200), (oeuf_dur, 110), (mais, 300)]
    1. Écrire une fonction masse_plat qui prend en argument un « plat » (comme ci-dessus) et renvoie sa masse totale, en grammes.
    2. Écrire une fonction energie_plat qui prend en argument un « plat » et renvoie le nombre de kJ pour 100 g de ce plat. On admet qu'on dispose de fonctions semblables pour les lipides, les glucides, les fibres, les protéines et le sel.
    3. Écrire une fonction fiche_info_nutrition qui renvoie un dictionnaire contenant les informations nutritionnelles d'un plat, dans le même esprit que le tableau du riz.

3. Questions 1 à 3 — corrigé détaillé

3.1 — Question 1 : instancier riz

riz = aliment('riz blanc', 1532, 0.6, 81, 1.3, 7.2, 0.01)

Les 7 valeurs sont données exactement dans l'ordre des paramètres du constructeur __init__ : nom, energie, lipide, glucide, fibre, proteine, sel.

3.2 — Question 2 : la méthode __repr__

Principe
__repr__ doit renvoyer une chaîne de caractères (pas un affichage direct avec print à l'intérieur). Pour obtenir un retour à la ligne entre chaque information, on insère le caractère spécial \n dans la chaîne.
def __repr__(self):
    return (f"Intitulé : {self.nom}\n"
            f"Énergie : {self.energie} kJ\n"
            f"Lipides : {self.lipide} g\n"
            f"Glucides : {self.glucide} g\n"
            f"Fibres : {self.fibre} g\n"
            f"Proteines : {self.proteine} g\n"
            f"Sel : {self.sel}g")

Trace de l'affichage dans une console Python :

riz : aliment
nom'riz blanc'
energie1532
lipide0.6
glucide81
fibre1.3
proteine7.2
sel0.01
 
Étape 1 / 1

3.3 — Question 3 : quantité de glucides dans n grammes

Principe
riz.glucide donne la quantité de glucides pour 100 g de riz, pas pour n grammes. Pour obtenir la quantité dans n grammes, il faut donc d'abord ramener riz.glucide à 1 gramme (en divisant par 100), puis multiplier par n.
n * riz.glucide / 100

Trace avec n = 75 :

ÉtapeCalculRésultat
Glucides pour 1 g de riz81 / 1000,81 g
Glucides pour 75 g de riz75 × 0,8160,75 g

En une seule instruction Python : 75 * riz.glucide / 100 renvoie bien 60.75.

4. Question 4 — composer et analyser un plat

4.1 — Question 4a : masse_plat

Principe
Un « plat » est une liste Python de couples (aliment, masse). Pour obtenir la masse totale, on parcourt cette liste avec une boucle for, et on additionne chaque masse dans une variable accumulateur.
def masse_plat(plat):
    masse_totale = 0
    for un_aliment, masse in plat:
        masse_totale += masse
    return masse_totale

Trace détaillée sur salade_de_riz :

Couple lumassemasse_totale après addition
(riz, 500)5000 + 500 = 500
(thon, 200)200500 + 200 = 700
(tomate, 200)200700 + 200 = 900
(oeuf_dur, 110)110900 + 110 = 1010
(mais, 300)3001010 + 300 = 1310

masse_plat(salade_de_riz) = 1310 g, exactement la valeur donnée par le sujet.

4.2 — Question 4b : energie_plat (et les fonctions semblables)

Principe, en une phrase
energie_plat doit renvoyer l'énergie pour 100 g du plat entier — pas la simple somme des énergies affichées sur chaque aliment, qui sont elles-mêmes données pour 100 g de chaque aliment séparément, et pas pour la quantité réellement utilisée.

Il faut donc, pour chaque aliment du plat, calculer sa contribution réelle (en fonction de sa masse dans le plat), les additionner, puis ramener ce total à une base de 100 g du plat entier :

  1. Pour un aliment de masse masse dans le plat, sa contribution en énergie est masse * aliment.energie / 100 (même principe qu'à la question 3 : on ramène la valeur « pour 100 g » à la masse réellement utilisée).
  2. On additionne ces contributions pour tous les aliments du plat : on obtient l'énergie totale du plat entier.
  3. On ramène ce total à 100 g du plat, avec la règle de trois énergie_totale / masse_totale × 100.
def energie_plat(plat):
    masse_totale = masse_plat(plat)
    energie_totale = 0
    for un_aliment, masse in plat:
        energie_totale += masse * un_aliment.energie / 100
    return energie_totale / masse_totale * 100

Les fonctions lipide_plat, glucide_plat, fibre_plat, proteine_plat et sel_plat se construisent exactement sur le même modèle, en remplaçant energie par l'attribut correspondant.

Vérification : un plat avec un seul aliment

Une façon simple de vérifier que la formule est correcte : si un plat ne contient qu'un seul aliment, alors, quelle que soit sa masse dans le plat, energie_plat doit redonner exactement la valeur pour 100 g de cet aliment (puisque le plat entier, ramené à 100 g, c'est justement 100 g de cet aliment).

Platmasse_totaleenergie_totale (avant division)energie_plat (résultat)
[(riz, 250)]250250 × 1532 / 100 = 38303830 / 250 × 100 = 1532
[(riz, 40)]4040 × 1532 / 100 = 612,8612,8 / 40 × 100 = 1532

Dans les deux cas, on retrouve bien 1532, la valeur du tableau du riz : la formule ne dépend pas de la masse choisie, comme attendu.

Vérification : le poids de chaque aliment compte bien

Avec deux aliments fictifs A (1000 kJ pour 100 g) et B (2000 kJ pour 100 g) : si le plat contient autant de A que de B (100 g chacun), le résultat doit être leur moyenne simple, 1500. Si le plat contient 3 fois plus de B que de A (50 g de A, 150 g de B), le résultat doit se rapprocher de B.

Platenergie_plat (résultat)
[(A, 100), (B, 100)]1500 (moyenne simple, quantités égales)
[(A, 50), (B, 150)]1750 (plus proche de B, qui pèse plus dans le plat)

Ces deux vérifications, ainsi que celle sur masse_plat, sont exécutées automatiquement dans le fichier téléchargeable de cette page.

4.3 — Question 4c : fiche_info_nutrition

Principe
Il suffit d'appeler chacune des fonctions déjà écrites, et de ranger leurs résultats dans un dictionnaire, avec les mêmes clés que le tableau nutritionnel du riz.
def fiche_info_nutrition(plat):
    return {'Energie': energie_plat(plat),
            'Lipides': lipide_plat(plat),
            'Glucides': glucide_plat(plat),
            'Fibres': fibre_plat(plat),
            'Proteines': proteine_plat(plat),
            'Sel': sel_plat(plat)}

Sur le plat fictif [(A, 100), (B, 100)] de la vérification précédente (A : 1000 kJ, 10 g de lipides, 20 g de glucides, 1 g de fibres, 5 g de protéines, 0,1 g de sel pour 100 g ; B : le double de chaque valeur), fiche_info_nutrition renvoie :

{'Energie': 1500.0, 'Lipides': 15.0, 'Glucides': 30.0, 'Fibres': 1.5, 'Proteines': 7.5, 'Sel': 0.15}

Ce sont bien les moyennes simples de A et B pour chaque information, cohérentes avec un plat composé pour moitié de A et pour moitié de B.

📄 Télécharger ce code (exercice 2)
src/entrainement_bac_polynomes_nutrition.py — contient la classe aliment, masse_plat, energie_plat et les fonctions semblables, fiche_info_nutrition, et les tests des 4 questions.

5. Points clés à retenir

Revoir les notions utilisées ici ? → Fiche « Interfaces et POO » · Retour à l'accueil