Épreuve pratique · 2026 · Sujet 5

Bac NSI pratique 2026 sujet 5 : Empreinte carbone et parcours récursif de dictionnaires

Ada a exporté une estimation annuelle d’empreinte carbone en kilogrammes deCO₂. Le fichier agrégé associe un total à chaque catégorie ; le fichier détaillé contient des dictionnaires imbriqués. Le module json charge ces données et est_dictionnaire distingue une branche d’une valeur numérique. Il faut additionner toutes les feuilles puis détecter une feuille dépassant un seuil, sans confondre une catégorie entière et un poste élémentaire.

Dans ce sujet

Un corrigé pédagogique pour comprendre et justifier vos réponses. Les conseils de rédaction ne constituent pas un barème officiel détaillé.

Application

Empreinte carbone et parcours récursif de dictionnaires

Ada a exporté une estimation annuelle d’empreinte carbone en kilogrammes deCO₂. Le fichier agrégé associe un total à chaque catégorie ; le fichier détaillé contient des dictionnaires imbriqués. Le module json charge ces données et est_dictionnaire distingue une branche d’une valeur numérique. Il faut additionner toutes les feuilles puis détecter une feuille dépassant un seuil, sans confondre une catégorie entière et un poste élémentaire.

Catégorie agrégéekg deCO₂
Logement2660
Alimentation1500
Transport708
Consommation893
Services sociétaux1491
{
  "Alimentation": {
    "Repas": {
      "Viande": {
        "Blanche": 296,
        "Rouge": 814
      },
      "Autre": 97
    },
    "Déchets": 152,
    "Boisson": 141
  },
  "Logement": {
    "Energie": {
      "Electricité": 206,
      "Cuisson": 105,
      "Chauffage individuel": 1500
    },
    "Construction": 650,
    "Location": 37,
    "Ameublement": 162
  },
  "Transport": {
    "Voiture": 563,
    "Avion": 92,
    "En commun": 29,
    "Vélo": 10,
    "Train": 14
  },
  "Consommation": {
    "Textile": 418,
    "Loisirs": 168,
    "Produits manufacturés neufs": 121,
    "Electronique": {
      "Electroménager": 70,
      "Numérique": 90
    },
    "Consommables": 26
  },
  "Services sociétaux": {
    "Public": 1300,
    "Marchand": 191
  }
}

Le dossier comporte le sujet et les fichiers listés dans les téléchargements. Les codes de départ sont reproduits ci-dessous. Travaillez sur une copie. Les appels professeur signalés dans les questions servent à présenter votre démarche ; vous pouvez aussi demander de l’aide en cas de difficulté.

Code fourni : empreinte.py
import json

########### Fonctions données ###########


def chargement_json(nom_fichier):
    """Charge le contenu d'un fichier JSON dans un dictionnaire Python renvoyé"""
    with open(nom_fichier, "r", encoding="utf8") as curseur:
        return json.load(curseur)


def est_dictionnaire(objet):
    """Teste si un objet est de type dictionnaire"""
    return isinstance(objet, dict)


##########################################


# Première fonction à implémenter après avoir découvert le fichier JSON agrégé
# Cf fichier `empreinte_ada_agr.json`
def total_simple(empreinte):
    """Fonction qui renvoie l'empreinte carbone totale d'un dictionnaire associant
    une empreinte carbone à des noms de catégories"""
    pass


# Deuxième fonction : il faut la récursivité pour le cas des sous-catégories
# Cf fichier `empreinte_ada.json`
def total_rec(empreinte):
    """Fonction récursive qui renvoie l'empreinte carbone totale représentée
    par un dictionnaire dont les valeurs peuvent aussi être des dictionnaires"""
    pass


def test_total_rec():
    test_dico1 = {"a": 1, "d": 2}
    assert total_rec(test_dico1) == 3
    test_dico2 = {"a": {"b": 1, "c": 2}, "d": {"e": 3}}
    assert total_rec(test_dico2) == 6

# ==========================================
# Fonction à analyser et corriger (Question 3)
# ==========================================


def alerte_valeur_aberrante(empreinte, limite):
    """
    Fonction censée déterminer si au moins une valeur du dictionnaire
    dépasse strictement la limite donnée.
    """
    for categorie, valeur in empreinte.items():
        if est_dictionnaire(valeur):
            return alerte_valeur_aberrante(valeur, limite)
        else:
            if valeur > limite:
                return True
    return False

Question 1

#

Compléter total_simple. Ajouter un test affichant l’empreinte carbone totale d’Ada à partir de empreinte_ada_agr.json, avec les fonctions d’accès fournies.

Indice

La somme porte sur les valeurs associées aux catégories.

Comprendre la correction
def total_simple(empreinte):
    total = 0
    for valeur in empreinte.values():
        total += valeur
    return total
empreinte = chargement_json('empreinte_ada_agr.json')
print(total_simple(empreinte))
assert total_simple(empreinte) == 7252
assert total_simple({}) == 0

Le résultat est 7252 kg deCO₂ dans les données fournies. Le dictionnaire est parcouru par ses valeurs : additionner les clés ferait une opération sur des chaînes. Le total nul correspond naturellement à un dictionnaire vide.

Voir la question dans le sujet PDF, p. 2 (nouvel onglet)

Question 2

#

Écrire la fonction récursive total_rec qui additionne les valeurs numériques de dictionnaires imbriqués en utilisant est_dictionnaire. Compléter les tests fournis avec un cas proche de empreinte_ada.json.

PythonExplorer toutes les branches de l’empreinte carboneÉcrivez votre solution et mettez-la à l’épreuve

Écrivez total_rec(empreinte), qui additionne les feuilles numériques d’un dictionnaire éventuellement imbriqué. Utilisez la récursion et la fonction est_dictionnaire fournie. Les structures sont finies et sans cycle. Les unités des valeurs restent les kg de CO₂.

def total_rec(empreinte):
    # À vous de jouer
    pass

Les cas de test proposés :

  • Un niveau : Additionner les valeurs terminales, sans utiliser les noms des catégories.
  • Logement détaillé : Le sous-total Énergie vaut 1811, puis les autres postes portent le total à 2660.
  • Deux branches profondes : Revenir d’un appel récursif ne doit pas abandonner les branches suivantes.
  • Dictionnaires vides imbriqués : Une catégorie vide contribue zéro, sans arrêter le parcours.
  • Aucune émission : Le total initial nul traite les deux situations.
Indice

Le résultat récursif doit être ajouté au total courant.

Comprendre la correction
def total_rec(empreinte):
    total = 0
    for valeur in empreinte.values():
        if est_dictionnaire(valeur):
            total += total_rec(valeur)
        else:
            total += valeur
    return total

Un sous-dictionnaire fournit son propre sous-total. Une valeur numérique est un cas terminal. Chaque appel traite un dictionnaire strictement plus profond et plus petit : le fichier est une structure finie sans cycle. Le total reste 7252, ce qui permet aussi de vérifier l’accord des deux exports.

test_total_rec()
assert total_rec({'Logement': {'Energie': {'Electricité': 206,
    'Cuisson': 105, 'Chauffage individuel': 1500},
    'Construction': 650, 'Location': 37, 'Ameublement': 162}}) == 2660
assert total_rec(chargement_json('empreinte_ada.json')) == 7252

On peut voir le dictionnaire comme un arbre : les catégories sont des nœuds, les nombres sont des feuilles. Le total d’un nœud est la somme des résultats de ses enfants. Pour Logement, le sous-total Énergie vaut 206 + 105 + 1500 = 1811 ; le niveau supérieur ajoute 650 + 37 + 162 et obtient 2660. Chaque feuille n’est visitée qu’une fois, et aucune catégorie n’est ajoutée en plus de ses feuilles : cela évite de compter deux fois la même émission.

Voir la question dans le sujet PDF, p. 3 (nouvel onglet)

Question 3

#

Avec limite 1000, alerte_valeur_aberrante ne détecte rien dans le dictionnaire complet alors que Chauffage individuel vaut 1500. Expliquer précisément le défaut et proposer une version corrigée.

PythonTrouver une alerte cachée dans la seconde brancheÉcrivez votre solution et mettez-la à l’épreuve

Corrigez alerte_valeur_aberrante(empreinte, limite). Elle renvoie True si au moins une feuille numérique dépasse strictement limite. Le total d’une catégorie n’est pas une feuille. Utilisez est_dictionnaire, fourni, pour parcourir les catégories imbriquées.

def alerte_valeur_aberrante(empreinte, limite):
    # À vous de jouer
    pass

Les cas de test proposés :

  • Première branche calme, seconde en alerte : Un False local ne permet pas de conclure que toutes les branches sont normales.
  • Première branche vide : Une catégorie vide ne doit pas arrêter la recherche.
  • Seuil exactement atteint : La comparaison est strictement supérieure.
  • Somme élevée, feuilles normales : 1600 est le total, mais aucune feuille ne dépasse 1000.
  • Alerte profonde : True doit remonter plusieurs niveaux récursifs.
  • Aucune feuille : Aucun témoin ne satisfait le critère.
Indice

Une recherche infructueuse dans une branche prouve-t-elle l’absence dans tout l’arbre ?

Comprendre la correction

Le return placé devant le premier appel récursif termine immédiatement le parcours, même lorsque ce premier sous-arbre ne contient aucune alerte. Dans le fichier réel, Alimentation vient avant Logement : le programme s’arrête dans la première branche, sans atteindre le chauffage. Une réponse True peut être propagée aussitôt ; une réponse False impose de continuer les autres branches.

def alerte_valeur_aberrante(empreinte, limite):
    for valeur in empreinte.values():
        if est_dictionnaire(valeur):
            if alerte_valeur_aberrante(valeur, limite):
                return True
        elif valeur > limite:
            return True
    return False

Le False final signifie que toutes les branches ont échoué à trouver une valeur strictement supérieure au seuil. Déplacer ce False dans la boucle reproduirait le même défaut sous une autre forme. La détection porte sur une feuille, pas sur le total du sous-dictionnaire.

La recherche d’une alerte correspond à un « ou » entre branches. Pour prouver l’existence d’une valeur trop grande, un seul témoin suffit ; pour prouver son absence, il faut avoir exploré toutes les branches. Cette asymétrie explique la place des retours. L’appel récursif qui trouve False ne permet pas de conclure globalement, tandis qu’un True remonte immédiatement jusqu’à la racine. Tester une première branche vide puis une seconde branche contenant 1500 cible précisément cette différence logique.

Voir la question dans le sujet PDF, p. 3 (nouvel onglet)

Question 4

#

Proposer un jeu de tests robuste pour alerte_valeur_aberrante : préciser pour chaque cas la structure d’entrée, le résultat attendu et la propriété algorithmique vérifiée.

Comprendre la correction
assert not alerte_valeur_aberrante({}, 1000)
assert not alerte_valeur_aberrante({'a': 1000}, 1000)
assert alerte_valeur_aberrante({'a': 1001}, 1000)
assert alerte_valeur_aberrante({'a': {'b': {'c': 1500}}}, 1000)
assert alerte_valeur_aberrante({'a': {'b': 10}, 'c': 1500}, 1000)
assert alerte_valeur_aberrante({'a': {}, 'b': {'c': 1500}}, 1000)
assert not alerte_valeur_aberrante({'a': {'b': 800}, 'c': 800}, 1000)
TestPropriété
Dictionnaire videAucune alerte ; boucle non exécutée
Valeur exactement 1000Seuil strict, pas supérieur ou égal
Valeur 1001Détection directe
Trois niveaux imbriquésPropagation de True
Première branche faible, seconde forteAbsence de retour False prématuré
Branche vide puis forteNe pas abandonner après un dictionnaire vide
Deux feuilles 800Ne pas comparer le total 1600 au seuil

Les tests ciblent la structure et les frontières, pas seulement des nombres différents dans un même cas. Ajoutez l’appel sur le fichier réel, qui doit maintenant renvoyer True pour 1000 et False pour 1500, car la comparaison est stricte et la plus grande feuille vaut 1500.

Quand a-t-on le droit de retourner ?Un atelier pour expérimenter

Choisissez une réponse puis confrontez votre intuition à l’explication. Vous pouvez modifier vos choix.

Lire les réponses et leurs explications
  1. Une première branche renvoie False. Que faire ?

    Continuer les branches restantes.. Une autre branche peut contenir une alerte.

  2. Deux feuilles 800, limite 1000 : alerte ?

    Non, aucune feuille ne dépasse 1000.. Chaque valeur est comparée séparément.

Appuyez votre explication sur les valeurs et les conditions exactes du programme.

Voir la question dans le sujet PDF, p. 3 (nouvel onglet)
Voir le retour prématuré dans un arbreUn atelier pour expérimenter

La première branche contient 300, la seconde une valeur réglable. Choisissez le programme fourni ou corrigé et observez les feuilles effectivement parcourues.

Lire le résultat de l’expérience initiale

Alerte détectée.

Seul True permet un arrêt anticipé ; sinon on continue.

CheminValeurComparaison
Première branche300Sous le seuil
Seconde branche1500Alerte

Le résultat d’une branche n’est pas forcément celui de toute la structure.

Revoir les notions de cet exercice

Du sujet à la méthode

Votre prochaine séance de révision

  • Pour tester la récursivité, varier la profondeur et l’ordre des branches.
  • Distinguer une somme récursive d’une recherche existentielle.

Retrouver ces notions dans d’autres sujets

Toutes les annales de NSI · Le guide pour préparer le bac NSI 2027

Énoncé : sujet 26_BCG_NSI_5 (PDF). Corrigé et explications pédagogiques proposés par Sofien.