Épreuve pratique · 2026 · Sujet 7

Bac NSI pratique 2026 sujet 7 : Simulation de coccinelles, hasard et conditions d’arrêt

Un producteur introduit des coccinelles dans une serre pour limiter les pucerons. Le modèle du sujet simule la chasse, la reproduction, le vieillissement et la croissance des proies. Une Coccinelle possède age, esperance_de_vie, sexe (male ou femelle) et niv_nutrition. random.randint(a, b) inclut ses deux bornes ; random.random() renvoie une valeur de[0,1[. Les résultats d’une exécution varient avec le hasard : il faut contrôler les règles et les invariants, pas mémoriser une population finale unique.

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

Simulation de coccinelles, hasard et conditions d’arrêt

Un producteur introduit des coccinelles dans une serre pour limiter les pucerons. Le modèle du sujet simule la chasse, la reproduction, le vieillissement et la croissance des proies. Une Coccinelle possède age, esperance_de_vie, sexe (male ou femelle) et niv_nutrition. random.randint(a, b) inclut ses deux bornes ; random.random() renvoie une valeur de[0,1[. Les résultats d’une exécution varient avec le hasard : il faut contrôler les règles et les invariants, pas mémoriser une population finale unique.

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 : coccinelles.py
import random


class Coccinelle:
    def __init__(self, sexe, age, niv_nutrition):
        self.age = age
        self.esperance_de_vie = random.randint(200, 350)
        self.sexe = sexe
        self.niv_nutrition = niv_nutrition

    def chasser(self, nb_proies, nb_coccinelles):
        if nb_coccinelles == 0:
            return nb_proies

        proies_par_cocci = nb_proies / nb_coccinelles

        if proies_par_cocci > 20:
            consomme = random.randint(12, 20)
        elif proies_par_cocci > 10:
            consomme = random.randint(8, 15)
        else:
            consomme = random.randint(3, 8)

        consomme = min(consomme, nb_proies)

        if consomme >= 10:
            self.niv_nutrition += 1
        else:
            self.niv_nutrition = max(0, self.niv_nutrition - 1)

        return nb_proies - consomme

    def reproduction(self):
        """
        Une femelle avec un niveau de nutrition >= 2 engendre exactement
        deux descendants : un mâle et une femelle.
        """
        descendants = []
        if self.sexe == "femelle" and self.niv_nutrition >= 2:
            descendants.append(Coccinelle("male", 0, 0))
            descendants.append(Coccinelle("femelle", 0, 0))
            self.niv_nutrition = 0

        return descendants

    def a_survecu(self):
        """
        Met à jour l'âge de la coccinelle et indique si elle est encore en vie.
        """
        self.age = self.age + 1
        return self.age < self.esperance_de_vie

    def __repr__(self):
        return f"Coccinelle {self.sexe}, âge: {self.age}/{self.esperance_de_vie}, niv_nutrition: {self.niv_nutrition}"


def evolution(population, nb_proies):
    """
    Simule une journée dans l'écosystème :
    - chasse des coccinelles
    - reproduction
    - vieillissement et mortalité
    - croissance des pucerons

    population est une liste d'instances de la classe Coccinelle
    nb_proies est un entier indiquant le nombre de proies

    Cette fonction renvoie un couple (population_suivante, nouveau_nb_proies) indiquant
    la nouvelle population à la fin de la journée et le nombre de proies.
    """
    population_suivante = []
    nouveau_nes = []
    nb_coccinelles = len(population)

    for coccinelle in population:
        nb_proies = coccinelle.chasser(nb_proies, nb_coccinelles)

        if coccinelle.a_survecu():
            population_suivante.append(coccinelle)

        nouveau_nes += coccinelle.reproduction()

    # Croissance naturelle des pucerons (augmentation de 20% par jour)
    nb_proies = int(nb_proies * 1.2)

    # Ajout des nouveau-nés en fin de journée
    population_suivante += nouveau_nes

    return population_suivante, nb_proies


#############################################################################
# Écrire ci-dessous le code pour les questions de l'énoncé                  #
#############################################################################

Question 1

#

Créer trois coccinelles de 10 jours, de nutrition 2 : deux femelles et un mâle. Avec 200 pucerons, simuler cinq jours par une boucle en appelant evolution. Afficher à chaque fin de journée le nombre de coccinelles et de pucerons.

Indice

La fonction renvoie un couple de valeurs mises à jour.

Comprendre la correction
population = [Coccinelle('femelle', 10, 2),
              Coccinelle('femelle', 10, 2), Coccinelle('male', 10, 2)]
nb_proies = 200
for jour in range(1, 6):
    population, nb_proies = evolution(population, nb_proies)
    print(jour, len(population), nb_proies)

Il faut réaffecter les deux valeurs renvoyées : evolution construit une nouvelle liste de population. Réutiliser sans changement la liste initiale ignorerait les naissances et les décès. Les compteurs sont affichés après l’appel, donc à la fin de la journée. Les objets sont mutables : recréer la population avant un autre essai.

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

Question 2

#

Écrire simulation_simple(population, nb_proies), avec 30 jours maximum, arrêt dès que les coccinelles ou les pucerons tombent à zéro. Renvoyer (nombre final de coccinelles, nombre final de pucerons, jours simulés). Tester avec la population initiale précédente et 1000 pucerons.

Comprendre la correction
def simulation_simple(population, nb_proies):
    jours = 0
    while jours < 30 and len(population) > 0 and nb_proies > 0:
        population, nb_proies = evolution(population, nb_proies)
        jours += 1
    return len(population), nb_proies, jours
population = [Coccinelle('femelle', 10, 2),
              Coccinelle('femelle', 10, 2), Coccinelle('male', 10, 2)]
print(simulation_simple(population, 1000))
assert simulation_simple([], 1000) == (0, 1000, 0)
assert simulation_simple([Coccinelle('male',10,2)], 0) == (1, 0, 0)

La condition vérifie les deux populations avant chaque journée. Une population déjà nulle conduit donc à zéro jour simulé. La variable jours compte les appels effectivement terminés et non une limite théorique. Pour rendre une comparaison reproductible, appeler random.seed avec la même valeur avant chaque création de population, car le constructeur tire aussi l’espérance de vie.

Le compteur de jours doit décrire ce que le programme a réellement simulé. Avec une population déjà vide, la condition initiale est fausse et aucune journée ne passe. Avec des populations vivantes, on appelle le moteur, on récupère son nouvel état, puis on incrémente le compteur. La condition est réévaluée sur cet état pour décider du jour suivant. Faire le test d’extinction seulement après une boucle fixe de 30 jours ne respecterait pas l’arrêt prématuré. Inversement, retourner toujours 30 masquerait une extinction survenue plus tôt.

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

Question 3

#

Écrire la documentation et les commentaires de chasser.

Comprendre la correction
def chasser(self, nb_proies, nb_coccinelles):
    """Simule la chasse d'une coccinelle et renvoie les proies restantes.
    nb_proies est un entier positif ou nul ; nb_coccinelles aussi.
    Modifie self.niv_nutrition sans jamais le rendre négatif.
    Si aucune coccinelle n'est comptée, les proies restent inchangées.
    """
    if nb_coccinelles == 0:
        return nb_proies  # éviter une division par zéro
    proies_par_cocci = nb_proies / nb_coccinelles
    if proies_par_cocci > 20:
        consomme = random.randint(12, 20)
    elif proies_par_cocci > 10:
        consomme = random.randint(8, 15)
    else:
        consomme = random.randint(3, 8)
    consomme = min(consomme, nb_proies)  # ne pas manger plus que le stock
    if consomme >= 10:
        self.niv_nutrition += 1
    else:
        self.niv_nutrition = max(0, self.niv_nutrition - 1)
    return nb_proies - consomme

Décrire l’effet de bord sur la nutrition est indispensable. Le code n’enregistre pas exactement un nombre de jours consécutifs bien nourris : il décrémente le niveau d’un jour quand la ration est insuffisante, au lieu de le remettre directement à zéro. La documentation doit refléter le code réellement fourni, même si la présentation initiale utilise cette expression.

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

Question 4

#

Modifier reproduction et a_survecu : reproduction seulement à partir de 20 jours ; une coccinelle de nutrition 0 a une chance sur 3 de mourir dans la journée. Tester à nouveau la simulation.

PythonProgrammer la maturité des coccinellesÉcrivez votre solution et mettez-la à l’épreuve

Modifiez reproduction : une femelle d’au moins 20 jours et de nutrition au moins 2 crée exactement deux Coccinelle, un mâle puis une femelle, tous deux d’âge et nutrition nuls. Après cette naissance, sa nutrition devient 0. Sinon renvoyez une liste vide sans changer son état. Cet entraînement isole la reproduction ; le vieillissement et le risque de famine sont traités séparément dans le corrigé.

class Coccinelle:
    def __init__(self, sexe, age, niv_nutrition):
        self.age = age
        self.esperance_de_vie = random.randint(200, 350)
        self.sexe = sexe
        self.niv_nutrition = niv_nutrition

    def reproduction(self):
        # Complétez cette méthode
        pass

Les cas de test proposés :

  • Le jour exact de maturité : 20 jours et nutrition 2 suffisent ; les descendants sont de vrais objets.
  • La veille est trop tôt : La nutrition ne remplace pas la condition d’âge.
  • Un mâle mature : La condition sur le sexe reste nécessaire.
  • Nutrition insuffisante : Un échec de reproduction ne doit pas consommer la nutrition.
  • Pas de seconde naissance immédiate : La remise à zéro empêche une nouvelle portée sans récupération.
Indice

Ajouter une condition de maturité sans supprimer les conditions déjà présentes.

Comprendre la correction
def reproduction(self):
    descendants = []
    if (self.sexe == 'femelle' and self.niv_nutrition >= 2
            and self.age >= 20):
        descendants.append(Coccinelle('male', 0, 0))
        descendants.append(Coccinelle('femelle', 0, 0))
        self.niv_nutrition = 0
    return descendants
def a_survecu(self):
    self.age += 1
    if self.age >= self.esperance_de_vie:
        return False
    if self.niv_nutrition == 0 and random.random() < 1 / 3:
        return False
    return True

L’âge 20 est inclus. Pour la famine, un tirage strictement inférieur à 1/3 couvre un tiers de[0,1[. Les autres coccinelles restent soumises à leur espérance de vie. L’ordre fourni appelle a_survecu avant reproduction : l’âge a donc déjà augmenté quand la maturité est testée. Il appelle aussi reproduction même après un décès ; ce défaut du moteur n’est pas demandé dans la question, mais une amélioration cohérente consiste à déplacer cet appel à l’intérieur du if de survie, et à expliquer ce changement supplémentaire.

# Amélioration cohérente dans evolution :
if coccinelle.a_survecu():
    population_suivante.append(coccinelle)
    nouveau_nes += coccinelle.reproduction()

Comparer plusieurs exécutions ou utiliser une graine fixe. Une seule simulation ne permet pas de mesurer une probabilité. Pour les tests unitaires, imposer des tirages de 0 et de 0,9 permet de vérifier les deux issues sans test aléatoire instable.

Pour vérifier la probabilité sans attendre des milliers d’expériences, testez les branches du code avec des tirages imposés : une coccinelle affamée doit mourir pour un tirage 0,2 et survivre au risque de famine pour 0,9. Une coccinelle nourrie ne doit pas mourir de cette cause, quelle que soit cette valeur. Vérifiez séparément le vieillissement à l’espérance de vie. Ces tests contrôlent les décisions exactes ; ils ne prétendent pas prouver que le générateur aléatoire produit la bonne distribution. La comparaison de simulations globales constitue un autre niveau de validation.

Prévoir une transitionUn 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. Femelle de 19 jours, nutrition 2 : appelée directement, reproduction renvoie…

    Une liste vide.. L’âge interdit encore la reproduction.

  2. Nutrition 0 et tirage 0,2 : que fait le test tirage&lt;1/3 ?

    Décès lié au manque de nourriture.. Le seuil vaut environ 0,3333.

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

Voir la question dans le sujet PDF, p. 3 (nouvel onglet)
Tester les règles sans subir le hasardUn atelier pour expérimenter

Réglez l’âge après vieillissement, la nutrition et le tirage. L’atelier isole les règles de maturité et de famine pour les rendre reproductibles ; l’espérance de vie est fixée à 250 jours.

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

La femelle survit et peut avoir 2 descendants.

Cette projection utilise le moteur amélioré : seules les survivantes se reproduisent. Le contrôle de maturité porte sur l’âge après vieillissement.

ConditionRésultat
Âge <250true
Nutrition0 et tirage<1/3false
Âge≥20 et nutrition≥2true

Un simulateur aléatoire devient testable quand on isole ses décisions et impose les tirages.

Revoir les notions de cet exercice

Du sujet à la méthode

Votre prochaine séance de révision

  • Recréer les objets pour chaque expérience.
  • Tester les probabilités par leurs seuils avant d’observer une simulation complète.

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_7 (PDF). Corrigé et explications pédagogiques proposés par Sofien.