correction et avancement sur le rapport

This commit is contained in:
2026-10-14 14:55:25 +02:00
parent be7b09fdb7
commit d102852bcf
11 changed files with 343 additions and 70 deletions
+120 -12
View File
@@ -1,20 +1,128 @@
import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
data = np.loadtxt("rabbits.csv", delimiter=",", skiprows=1, usecols=(0, 1))
month, total = data[:, 0], data[:, 1]
OUT = os.path.join("repport", "images")
os.makedirs(OUT, exist_ok=True)
# Supprime l'ancienne figure règle 3 si elle existe encore
STALE = os.path.join(OUT, "regle3_vieillissement.png")
if os.path.exists(STALE):
os.remove(STALE)
print(f"Ancien fichier supprimé : {STALE}")
def save_fig(fig, name):
path = os.path.join(OUT, f"{name}.png")
fig.savefig(path, dpi=300, bbox_inches="tight")
plt.close(fig)
print(f"OK -> {path}")
# --- Chargement ---
df = pd.read_csv("rabbits.csv", skipinitialspace=True)
df.columns = df.columns.str.strip()
df = df.loc[:, ~df.columns.str.startswith("Unnamed")]
cols = df.columns.drop("Month")
df[cols] = df[cols].astype("float64") # effectifs > int64 max
m = df["Month"].values.astype(int)
litters = df["Litters"].values
births = (df["Kitten Male (age 1)"] + df["Kitten Female (age 1)"]).values
adult_1 = (df["Male (age 1)"] + df["Female (age 1)"]).values
adult_f = df.filter(regex=r"^Female \(age").sum(axis=1).values
km = df[[f"Kitten Male (age {i})" for i in range(1, 9)]].values
kf = df[[f"Kitten Female (age {i})" for i in range(1, 9)]].values
kit = km + kf # (N, 8), âges en mois
# Buckets adultes etanches (age 1 exclu : il recoit la maturation), ages 2..16 ans
ages = list(range(2, 17))
ad = df[[f"Male (age {i})" for i in ages] +
[f"Female (age {i})" for i in ages]].values # (N, 30)
# --- Controle d'integrite : saut du ratio naissances/portees ? ---
ratio_g = births[1:] / np.maximum(litters[:-1], 1)
jumps = np.where(np.abs(np.diff(ratio_g)) > 0.75)[0]
if jumps.size:
print(f"⚠️ CSV incoherent : le ratio naissances/portees saute autour du mois "
f"{m[jumps[0] + 1]} — verifie le regime de gestation !")
# === Regle 1 : gestation a 50 %/mois ===
fig, ax = plt.subplots(figsize=(9, 5))
ax.plot(month, total, lw=2, color="tab:blue")
ax.plot(m, litters / np.maximum(adult_f, 1), lw=1, color="tab:blue")
ax.axhline(0.5, color="red", ls="--", label="attendu : 0.50")
ax.set_ylim(0, 1)
ax.set_xlabel("Mois"); ax.set_ylabel("Litters / femelles adultes")
ax.set_title("Règle 1 — Taux de gestation"); ax.legend(); ax.grid(alpha=0.3)
fig.tight_layout(); save_fig(fig, "regle1_gestation")
# === Regle 2 : 3 a 6 bebes par gestation ===
fig, ax = plt.subplots(figsize=(9, 5))
ax.plot(m[1:], ratio_g, lw=1, color="tab:purple")
ax.axhspan(3, 6, color="green", alpha=0.15, label="zone [3, 6]")
ax.axhline(4.5, color="red", ls="--", label="moyenne uniforme : 4.5")
ax.set_ylim(0, 8)
ax.set_xlabel("Mois"); ax.set_ylabel("Nouveaux-nés / portée")
ax.set_title("Règle 2 — Bébés par gestation"); ax.legend(); ax.grid(alpha=0.3)
fig.tight_layout(); save_fig(fig, "regle2_portees")
# === Regle 2bis : distribution des portees ===
fig, ax = plt.subplots(figsize=(9, 5))
valid = ratio_g[(ratio_g > 1) & (ratio_g < 12)]
ax.hist(valid, bins=60, color="tab:purple", alpha=0.8)
ax.axvline(4.5, color="red", ls="--", label="4.5 attendu")
ax.set_xlabel("Bébés / portée"); ax.set_ylabel("Occurrences")
ax.set_title("Règle 2 — Distribution des portées"); ax.legend(); ax.grid(alpha=0.3)
fig.tight_layout(); save_fig(fig, "regle2_distribution")
# === Regle 3 : maturation 5-8 mois (mesure seule, sans prediction) ===
fig, ax = plt.subplots(figsize=(9, 5))
ok3 = (m % 12 != 1) & (m >= 14) & (adult_1 > 100)
ax.plot(m[ok3], adult_1[ok3], lw=1.5, color="tab:green", label="Adultes âge 1 (mesuré)")
ax.set_yscale("log")
ax.set_xlim(0, month.max())
ax.set_ylim(10, total.max() * 3)
ax.set_xticks(np.arange(0, month.max() + 1, 24))
ax.grid(True, which="both", axis="y", alpha=0.3)
ax.set_xlabel("Month")
ax.set_ylabel("Total rabbits (log)")
ax.set_title("Simulation Rabbit — population totale")
ax.set_xlabel("Mois"); ax.set_ylabel("Effectifs (log)")
ax.set_title("Règle 3 — Maturation entre 5 et 8 mois"); ax.legend(); ax.grid(alpha=0.3)
fig.tight_layout(); save_fig(fig, "regle3_maturation")
fig.tight_layout()
plt.show()
# === Regle 5 : survie des kittens ~95 %/mois ===
fig, ax = plt.subplots(figsize=(9, 5))
surv = kit[1:, 1:] / np.maximum(kit[:-1, :-1], 1) # (N-1, 7)
mask = kit[:-1, :-1] > 100 # (N-1, 7)
xs = np.repeat(m[1:, None], 7, axis=1) # (N-1, 7)
ax.plot(xs[mask], surv[mask], ".", ms=2, alpha=0.2, color="tab:cyan")
ax.axhline(0.95, color="red", ls="--", label="attendu : 0.95")
ax.set_ylim(0.8, 1.05)
ax.set_xlabel("Mois"); ax.set_ylabel("Taux de survie mensuel")
ax.set_title("Règle 5 — Survie des kittens (5 %/mois)"); ax.legend(); ax.grid(alpha=0.3)
fig.tight_layout(); save_fig(fig, "regle5_survie_kittens")
# === Regle 4 : mortalite adultes — 40 %/an, +10 pts/an apres 10 ans ===
trans1 = (m[1:] % 12 == 1) # (N-1,) : mois cible = bascule
s_reg = ad[1:] / np.maximum(ad[:-1], 1) # (N-1, 30) mois reguliers
s_diag = np.full_like(s_reg, np.nan)
s_diag[trans1, 1:] = ad[1:][trans1, 1:] / np.maximum(ad[:-1][trans1, :-1], 1)
s_meas = np.where(trans1[:, None], s_diag, s_reg) # (N-1, 30)
d_annuel = np.array([0.40 + 0.10 * max(0, a - 10) for a in ages])
s_attendu = 1 - d_annuel / 12
fig, ax = plt.subplots(figsize=(10, 5.5))
for j, a in enumerate(ages):
ok = (ad[:-1, j] > 100) & ~np.isnan(s_meas[:, j]) & \
(s_meas[:, j] > 0.5) & (s_meas[:, j] < 1.02)
ax.plot(m[1:][ok], s_meas[ok, j], ".", ms=2.5, alpha=0.45, label=f"âge {a} ans")
for a, s in zip(ages, s_attendu):
ax.axhline(s, color="black", ls=":", lw=0.7, alpha=0.6)
ax.text(1.01, s, f"{s:.4f}", transform=ax.get_yaxis_transform(),
va="center", fontsize=7, color="black")
ax.set_ylim(0.88, 1.0)
ax.set_xlabel("Mois"); ax.set_ylabel("Taux de survie mensuel par bucket d'âge")
ax.set_title("Règle 4 — 40 %/an jusqu'à 10 ans, +10 pts/an ensuite (appliqué /12 par mois)")
ax.legend(ncol=2, fontsize=8, loc="lower left"); ax.grid(alpha=0.3)
fig.tight_layout(); save_fig(fig, "regle4_mortalite_adultes")
print("\nTaux de survie mensuels attendus (règle 4) :")
for a, d, s in zip(ages, d_annuel, s_attendu):
print(f" âge {a:2d} ans : {100*d:5.1f} %/an -> survie mensuelle {s:.5f}")
print("\nToutes les images sont dans", OUT)
+19 -24
View File
@@ -166,33 +166,28 @@ double ul_mu_litter, ul_sigma_litter;
void update_litter(rabbit_hole_t *rabbits, __uint128_t *new_kitten_male, __uint128_t *new_kitten_female) {
(*new_kitten_female) = 0;
(*new_kitten_male) = 0;
// sigma par femelle carre = ((b-a+1)caree- 1)/12 = 4
// sigma par femelle = 2
//sigma total = sqrt(sigma par femelle carre * N)
//mu = ((9+3)/2 ) * N = 6 * N
if (rabbits->female_litter < 100) {
for (i = 0; i < rabbits->female_litter; i++) {
taille1 = 3 + (int)(4.0 * genrand_real2()); //TODO
taille1 = 3 + (int)(4.0 * genrand_real2()); //TODO
for (int k = 0; k < taille1; k++) {
if (genrand_real1() > 0.5) (*new_kitten_male)++;
else (*new_kitten_female)++;
}
if (rabbits->female_litter < 100) {
for (i = 0; i < rabbits->female_litter; i++) {
taille1 = 3 + (int)(4.0 * genrand_real2());
for (int k = 0; k < taille1; k++) {
if (genrand_real1() > 0.5) (*new_kitten_male)++;
else (*new_kitten_female)++;
}
}else {
ul_mu_k = 6.0*(double)rabbits->female_litter;
ul_sigma_k = sqrt(4.0 * (double)rabbits->female_litter);
gauss(x_gauss);
x_gauss[0] = CLAMP_BTW(x_gauss[0] * ul_sigma_k+ ul_mu_k, 0.0 , rabbits->female_litter * 9.0);
ul_mu_sex = x_gauss[0] * 0.5;
ul_sigma_sex = sqrt(ul_mu_sex * 0.5);
x_gauss[1] = CLAMP_BTW(x_gauss[1] * ul_sigma_sex + ul_mu_sex , 0.0, rabbits->female_litter * 9.0);
(*new_kitten_female) = (__uint128_t)roundl(x_gauss[1]) ;
(*new_kitten_male) = (__uint128_t)roundl(x_gauss[0] - x_gauss[1]);
}
}else {
ul_mu_k = 4.5*(double)rabbits->female_litter; // 4.5 = (b+a)/2
ul_sigma_k = sqrt(1.118 * (double)rabbits->female_litter); //(k²−1)/12 = 1.118
gauss(x_gauss);
x_gauss[0] = CLAMP_BTW(x_gauss[0] * ul_sigma_k+ ul_mu_k, 0.0 , rabbits->female_litter * 9.0);
ul_mu_sex = x_gauss[0] * 0.5;
ul_sigma_sex = sqrt(ul_mu_sex * 0.5);
x_gauss[1] = CLAMP_BTW(x_gauss[1] * ul_sigma_sex + ul_mu_sex , 0.0, rabbits->female_litter * 9.0);
(*new_kitten_female) = (__uint128_t)roundl(x_gauss[1]) ;
(*new_kitten_male) = (__uint128_t)roundl(x_gauss[0] - x_gauss[1]);
}
nb_females = 0;
for (i = 0; i < N_MATURE; i++) {
+180 -34
View File
@@ -3,68 +3,214 @@
% Préambule : packages utiles
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[french]{babel}
\usepackage{graphicx}
\usepackage[french]{babel}
\usepackage{graphicx}
\usepackage{amsmath}
\usepackage{hyperref}
\graphicspath{ {./images/} }
\usepackage[a4paper, margin=2cm]{geometry}
\usepackage[table]{xcolor}
\usepackage{listings}
\usepackage{hyperref} % hyperref se charge en dernier
\title{Simulation de population de lapin \\ (Rabbit's hole)}
\author{Aubin DORIVAL F2 Promo 28}
\graphicspath{ {./images/} }
\definecolor{codebg}{HTML}{F6F8FA}
\definecolor{codeframe}{HTML}{D0D7DE}
\definecolor{kw}{HTML}{CF222E} % mots-clés C
\definecolor{tp}{HTML}{0550AE} % types
\definecolor{str}{HTML}{0A3069} % chaînes
\definecolor{cmt}{HTML}{6E7781} % commentaires
\definecolor{prep}{HTML}{8250DF} % préprocesseur
\lstset{
language=C,
basicstyle=\ttfamily\small,
keywordstyle=\color{kw}\bfseries,
commentstyle=\color{cmt}\itshape,
stringstyle=\color{str},
backgroundcolor=\color{codebg},
frame=single,
rulecolor=\color{codeframe},
numbers=left,
numberstyle=\tiny\color{cmt},
numbersep=8pt,
breaklines=true,
showstringspaces=false,
tabsize=4,
captionpos=b,
% types et fonctions courantes du C
morekeywords={size_t, uint8_t, uint32_t, uint64_t, NULL,
malloc, free, printf, rand, srand,
sqrt, log, cos, sin, M_PI},
% directives du préprocesseur (le # reste gris, le mot est coloré)
emph={include, define, ifndef, ifdef, endif, pragma},
emphstyle=\color{prep}\bfseries
}
\title{Simulation de population de lapins \\ « Rabbit Hole »}
\author{Aubin DORIVAL \\ F2 -- Promo 28}
\date{\today}
\begin{document}
\maketitle
\begin{center}
\url{https://git.dukantic.fr/dukantic/Rabbit-s-Hole}
\includegraphics[width=0.7\textwidth]{draw_rabbit.jpg}
\url{https://git.dukantic.fr/dukantic/Rabbit-s-Hole}\\[0.5cm]
\includegraphics[width=0.7\textwidth]{draw_rabbit.jpg}
\end{center}
\section{Introduction}
Le projet `Rabbit Hole` est une simulation de population de lapin. Elle
essaye de simuler la croissance de la population de lapin en essayant de
suivre certaine règles et en faisant abstraction d'autre choses. Dans
tout le projet j'ai globalement prit les conditions et les probabilités
qui sont dans le sujet de base.
Le projet « Rabbit Hole » est une simulation de population de lapins.
Elle vise à simuler la croissance de cette population en suivant certaines
règles, tout en faisant abstraction d'autres paramètres. Tout au long du
projet, j'ai globalement conservé les conditions et les probabilités
fournies dans le sujet.
\section{Règles}
Tout d'abord il faut définir les règles à suivre.
Tout d'abord, il faut définir les règles à suivre.
\begin{itemize}
\item Taux de gestation à $50\%$ par mois.
\item $3$ à $6$ bébés par gestation de manière uniforme.
\item Les bébés deviennent adulte entre le $5\up{eme}$ et $8\up{eme}$ mois
\item Les adultes meurent avec un taux de $40\%/ans + 10\%/ans$ à partir de 10 ans.
\item Les bébés meurent avec un taux de $65\%/ans \Rightarrow 5\%/mois$ sur 7 mois en moyenne.
\item taux de gestation de $50\%$ par mois ;
\item $3$ à $6$ bébés par gestation, répartis de manière uniforme ;
\item les bébés deviennent adultes entre le 5\ieme{} et le 8\ieme{} mois ;
\item les adultes meurent avec un taux de $40\%$ par an, auquel s'ajoute
$10\%$ par an à partir de 10 ans ;
\item les bébés meurent avec un taux de $65\%$ par an, soit environ
$5\%$ par mois pendant 7 mois en moyenne.
\end{itemize}
\begin{center}
\includegraphics[width=\textwidth]{rabbit's_rules.png}
\includegraphics[width=\textwidth]{rabbit's_rules.png}
\end{center}
\section{Structure de donnée}
Pour stocker les lapins j'ai choisit de les classées par âge. En effet
toute les règles sont basé sur l'âge alors je peux les compter. Ma
structure de données est donc basé sur cela. J'ai un tableau par sexe
et l'indice du tableau correspond a l'âge. J'ai aussi le nombre de lapine
en gestation. Pour les bébés j'ai exactement la même chose sauf que
l indice représente le mois plutôt que l'année. Pour les nombres j'ai
choisi un entier non-signé de 128 bits (ce qui est pas disponible pour
toute les plateformes) pour évité un maximum les dépassement d'entier.
\section{Structure de données}
Pour stocker les lapins, j'ai choisi de les classer par âge. En effet,
toutes les règles sont basées sur l'âge : il suffit donc de compter les
lapins par classe d'âge. Ma structure de données repose sur ce principe :
un tableau par sexe, dont l'indice correspond à l'âge, ainsi que le nombre
de lapines en gestation. Pour les bébés, j'ai exactement la même chose,
si ce n'est que l'indice représente le mois plutôt que l'année. Pour les
nombres, j'ai choisi un entier non signé de 128 bits (non disponible sur
toutes les plateformes) afin de limiter au maximum les dépassements
d'entier.
\begin{verbatim}
\begin{figure}[htbp]
\begin{lstlisting}[caption={Structure de données pour les lapins}, label={lst:struct}]
#define N_MATURE (16)
#define N_KITTEN (8)
typedef struct rabbit_hole_s {
typedef struct rabbit_hole_s {
__uint128_t male_mature[N_MATURE];
__uint128_t female_mature[N_MATURE];
__uint128_t female_litter;
__uint128_t kitten_male[N_KITTEN];
__uint128_t kitten_female[N_KITTEN];
} rabbit_hole_t;
\end{verbatim}
\end{lstlisting}
\end{figure}
\section{Implémentation}
Pour la génération de nombres aléatoires, j'utilise le générateur
Mersenne Twister, associé à la méthode de Box-Muller. Dans un premier
temps, j'effectuais un tirage aléatoire pour chaque probabilité et pour
chaque lapin. Par exemple, pour la mort : pour chaque lapin, je tire un
nombre aléatoire et je décide s'il meurt ou s'il survit. Cette approche
était beaucoup trop lente : au bout de 9 à 10 ans simulés, les calculs
prenaient déjà trop de temps. Je me suis alors dit que toutes mes
probabilités suivaient des lois uniformes et que je pouvais donc les
approximer par des gaussiennes grâce à la méthode de Box-Muller.
Je n'utilise cette approximation que lorsque le nombre de lapins à
traiter dépasse 100.
\begin{figure}[htbp]
\centering
\begin{align*}
R_{n,1},\, R_{n,2} &\gets \text{nombres aléatoires} \\
X_1 &= \sqrt{-2 \ln R_{n,1}} \cdot \cos(2\pi R_{n,2}) \\
X_2 &= \sqrt{-2 \ln R_{n,1}} \cdot \sin(2\pi R_{n,2})
\end{align*}
\caption{Méthode de Box-Muller}
\label{fig:box-muller}
\end{figure}
La gaussienne générée par la méthode de Box-Muller est centrée en 0 et
réduite (écart type 1). Dans mon cas, j'ai besoin de la centrer sur le
nombre de lapins attendu. Pour cela, je calcule $\mu$, la moyenne de la
valeur attendue, ainsi que $\sigma$, l'écart type de la gaussienne.
\begin{figure}[htbp]
\centering
\begin{align*}
\text{Loi binomiale (survies, sexes) : } \mathcal{B}(n, p) \\
\mu_{\text{bin}} &= n \cdot p \\
\sigma_{\text{bin}} &= \sqrt{n \cdot p \cdot (1-p)} \\[4pt]
\text{Somme de $n$ lois uniformes (portées) : } \sum_{i=1}^{n} \mathcal{U}([a,b]) \\
\mu_{\text{unif}} &= n \cdot \frac{a+b}{2} \\
\sigma_{\text{unif}} &= \sqrt{\frac{n \cdot (b-a)^2}{12}} \\[4pt]
\text{Tirage Box-Muller : } X_0 \sim \mathcal{N}(0,1) \\
X_{\text{utilisable}} &= \mu + \sigma \cdot X_0
\;\sim\; \mathcal{N}(\mu, \sigma^2)
\end{align*}
\caption{Décalage de la gaussienne par Box-Muller}
\label{eq:decalage-gaussienne}
\end{figure}
\begin{figure}[htbp]
\begin{lstlisting}[caption={Code des naissances}, label={lst:naissance}]
if (rabbits->female_litter < 100) {
for (i = 0; i < rabbits->female_litter; i++) {
taille1 = 3 + (int)(4.0 * genrand_real2());
for (int k = 0; k < taille1; k++) {
if (genrand_real1() > 0.5) (*new_kitten_male)++;
else (*new_kitten_female)++;
}
}
}else {
ul_mu_k = 4.5*(double)rabbits->female_litter; // 4.5 = (b+a)/2
ul_sigma_k = sqrt(1.118 * (double)rabbits->female_litter); //(k*k-1)/12 = 1.118
gauss(x_gauss);
x_gauss[0] = CLAMP_BTW(x_gauss[0] * ul_sigma_k+ ul_mu_k, 0.0 , rabbits->female_litter * 9.0);
ul_mu_sex = x_gauss[0] * 0.5;
ul_sigma_sex = sqrt(ul_mu_sex * 0.5);
x_gauss[1] = CLAMP_BTW(x_gauss[1] * ul_sigma_sex + ul_mu_sex , 0.0, rabbits->female_litter * 9.0);
(*new_kitten_female) = (__uint128_t)roundl(x_gauss[1]) ;
(*new_kitten_male) = (__uint128_t)roundl(x_gauss[0] - x_gauss[1]);
}
\end{lstlisting}
\end{figure}
\section{Statistiques}
Pour collecter les statistiques, j'écris sur la sortie d'erreur (stderr)
au format CSV, puis je redirige cette sortie vers un fichier afin de
l'exploiter. J'inscris une nouvelle ligne chaque mois, contenant
l'ensemble des nombres de ma structure. Pour afficher les nombres en
\verb|__uint128_t|, il n'existe pas de format d'affichage par défaut,
contrairement à \verb|uint64_t| par exemple (\verb|%llu|) : il a donc
fallu réaliser un formatage manuel. Les graphiques obtenus permettent de
vérifier que les règles posées au départ sont bien respectées.
\begin{figure}[htbp]
\centering
\includegraphics[width=0.45\textwidth]{regle1_gestation}
\includegraphics[width=0.45\textwidth]{regle2_distribution}
\includegraphics[width=0.45\textwidth]{regle2_portees}
\includegraphics[width=0.45\textwidth]{regle3_maturation}
\includegraphics[width=0.45\textwidth]{regle4_mortalite_adultes}
\includegraphics[width=0.45\textwidth]{regle5_survie_kittens}
\caption{Vérification graphique des règles}
\label{fig:regles}
\end{figure}
\section{Utilisation de l'IA}
Pendant ce TP, l'IA a été utilisée pour :
\begin{itemize}
\item réaliser les graphiques (Python, avec NumPy et Matplotlib) ;
\item vérifier la cohérence des valeurs retournées par la simulation ;
\item m'aider à rédiger ce rapport (LaTeX, correction d'erreurs et de
formulations en français).
\end{itemize}
Pour le reste du TP, aucune utilisation n'a été faite.
\end{document}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.