From 06e73e7622916a1d1ec286382eb5edd940674b9b Mon Sep 17 00:00:00 2001 From: Florian Mathieu Date: Mon, 30 Mar 2026 21:21:43 +0200 Subject: [PATCH] correction tp et ajout tp de secours --- Programmation_Dynamique/Corrige_TP_Vaccin.md | 190 +++++++++++++++++++ Programmation_Dynamique/EXERCICES.md | 6 + Programmation_Dynamique/README.md | 2 +- Programmation_Dynamique/RENDU_MONNAIE.md | 2 +- Programmation_Dynamique/SAC_A_DOS.md | 2 +- Programmation_Dynamique/TP_Donjon.md | 4 +- Programmation_Dynamique/TP_Vaccin.md | 159 ++++++++++++++++ 7 files changed, 359 insertions(+), 6 deletions(-) create mode 100644 Programmation_Dynamique/Corrige_TP_Vaccin.md create mode 100644 Programmation_Dynamique/TP_Vaccin.md diff --git a/Programmation_Dynamique/Corrige_TP_Vaccin.md b/Programmation_Dynamique/Corrige_TP_Vaccin.md new file mode 100644 index 0000000..e1e7b4f --- /dev/null +++ b/Programmation_Dynamique/Corrige_TP_Vaccin.md @@ -0,0 +1,190 @@ +# Corrigé — TP L'Algorithme du Vaccin + +## Partie 1 — Comprendre le problème + +**Question 1.** Algorithme glouton (tri par ratio efficacité/temps décroissant) : + +Ratios : Gamma (15,0), Delta (14,3), Epsilon (14,2), Bêta (14,0), Alpha (13,3) + +- Prendre Gamma : 2 h utilisées, efficacité 30, reste 8 h +- Prendre Delta : 2+7=9 h utilisées, efficacité 130, reste 1 h +- Plus aucun anticorps ne tient en 1 h → arrêt + +**Résultat glouton : 130** (Gamma + Delta) + +**Question 2.** Oui : Alpha + Bêta + Gamma = 3+5+2 = 10 h, efficacité 40+70+30 = **140**. Le glouton a raté la solution optimale. + +**Question 3.** Pour 5 anticorps : 2⁵ = 32 combinaisons. Pour 30 anticorps : 2³⁰ ≈ 1 milliard. L'approche exhaustive devient impraticable très rapidement. + +--- + +## Partie 2 — Formulation récursive + +**Question 4.** Cas de base : +- `vaccin(0, t) = 0` (aucun anticorps disponible) +- `vaccin(i, 0) = 0` (temps épuisé) + +**Question 5.** Relation de récurrence : + +``` +vaccin(i, t) = vaccin(i-1, t) si duree_i > t +vaccin(i, t) = max(vaccin(i-1, t), sinon + eff_i + vaccin(i-1, t - duree_i)) +``` + +**Question 6.** Version naïve : + +```python +def vaccin_naif(anticorps, i, t): + if i == 0 or t == 0: + return 0 + duree, eff = anticorps[i - 1] + if duree > t: + return vaccin_naif(anticorps, i - 1, t) + else: + sans = vaccin_naif(anticorps, i - 1, t) + avec = eff + vaccin_naif(anticorps, i - 1, t - duree) + return max(sans, avec) + +anticorps = [(3, 40), (5, 70), (2, 30), (7, 100), (6, 85)] +n = len(anticorps) +print(vaccin_naif(anticorps, n, 10)) # 140 +``` + +--- + +## Partie 3 — Version Top-Down (mémoïsation) + +**Question 7.** Oui. Par exemple, `vaccin(2, 5)` (2 premiers anticorps, 5 h restantes) peut être appelé en ne prenant pas Alpha, mais aussi après avoir pris Alpha (si on avait commencé avec 8 h). Sans mémoïsation, ce sous-problème est recalculé à chaque fois. + +**Question 8.** Version mémoïsation : + +```python +def vaccin_memo(anticorps, temps_total): + memo = {} + + def vaccin(i, t): + if i == 0 or t == 0: + return 0 + if (i, t) in memo: + return memo[(i, t)] + duree, eff = anticorps[i - 1] + if duree > t: + resultat = vaccin(i - 1, t) + else: + sans = vaccin(i - 1, t) + avec = eff + vaccin(i - 1, t - duree) + resultat = max(sans, avec) + memo[(i, t)] = resultat + return resultat + + return vaccin(len(anticorps), temps_total) + +print(vaccin_memo(anticorps, 10)) # 140 +``` + +**Question 9.** Les deux versions donnent 140 ✓ + +--- + +## Partie 4 — Version Bottom-Up (tableau) + +**Question 10.** Tableau pour Alpha (3h,40), Bêta (5h,70), Gamma (2h,30), T=6 : + +| | t=0 | t=1 | t=2 | t=3 | t=4 | t=5 | t=6 | +|---|---|---|---|---|---|---|---| +| i=0 (aucun) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| i=1 (Alpha 3h, 40) | 0 | 0 | 0 | 40 | 40 | 40 | 40 | +| i=2 (+Bêta 5h, 70) | 0 | 0 | 0 | 40 | 40 | 70 | 70 | +| i=3 (+Gamma 2h, 30) | 0 | 0 | 30 | 40 | 40 | 70 | 70 | + +Avec 6 h, le meilleur est Bêta seul (70), ou Gamma+Alpha ne tient pas car 2+3=5 h → efficacité 70 aussi. + +**Question 11.** Implémentation : + +```python +def vaccin_bottom_up(anticorps, temps_total): + n = len(anticorps) + tableau = [[0] * (temps_total + 1) for _ in range(n + 1)] + + for i in range(1, n + 1): + duree, eff = anticorps[i - 1] + for t in range(temps_total + 1): + if duree > t: + tableau[i][t] = tableau[i - 1][t] + else: + tableau[i][t] = max( + tableau[i - 1][t], + eff + tableau[i - 1][t - duree] + ) + + return tableau[n][temps_total] + +print(vaccin_bottom_up(anticorps, 10)) # 140 +``` + +**Question 12.** Les trois versions donnent 140 ✓ + +--- + +## Partie 5 — Reconstruction de la solution + +**Question 13.** Reconstruction : + +```python +def vaccin_reconstruction(anticorps, temps_total): + n = len(anticorps) + tableau = [[0] * (temps_total + 1) for _ in range(n + 1)] + + for i in range(1, n + 1): + duree, eff = anticorps[i - 1] + for t in range(temps_total + 1): + if duree > t: + tableau[i][t] = tableau[i - 1][t] + else: + tableau[i][t] = max( + tableau[i - 1][t], + eff + tableau[i - 1][t - duree] + ) + + # Reconstruction : on remonte le tableau + choisis = [] + t = temps_total + for i in range(n, 0, -1): + if tableau[i][t] != tableau[i - 1][t]: + choisis.append(anticorps[i - 1]) + t -= anticorps[i - 1][0] + + return tableau[n][temps_total], choisis + + +efficacite, choix = vaccin_reconstruction(anticorps, 10) +print(f"Efficacité maximale : {efficacite}") # 140 +print(f"Anticorps choisis : {choix}") # [(3,40), (5,70), (2,30)] → Alpha + Bêta + Gamma +``` + +**Question 14.** Vérification : +- Alpha (3h) + Bêta (5h) + Gamma (2h) = **10 h** ≤ 10 h ✓ +- 40 + 70 + 30 = **140** ✓ + +**Question 15.** Complexité : +- **Temps : O(n × T)** — on remplit un tableau de n+1 lignes et T+1 colonnes +- **Espace : O(n × T)** — pour stocker le tableau + +Avec n = nombre d'anticorps et T = temps total disponible. + +--- + +## Partie 6 — Bonus : contraintes supplémentaires + +**Question 16.** Pour gérer l'incompatibilité Alpha/Gamma, on peut ajouter une dimension à l'état : au lieu de `vaccin(i, t)`, on utilise `vaccin(i, t, alpha_pris)` où `alpha_pris` vaut 1 si Alpha a déjà été sélectionné, 0 sinon. Si `alpha_pris == 1` et que l'anticorps courant est Gamma (ou vice-versa), on interdit sa sélection. + +Cette approche généralise naturellement : chaque contrainte d'incompatibilité entre deux objets peut être encodée comme une variable booléenne supplémentaire dans l'état. La complexité devient O(n × T × 2^k) où k est le nombre de contraintes d'incompatibilité. + +--- + +Auteur : Florian Mathieu + +Licence CC BY NC + +Licence Creative Commons
Ce cours est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International. diff --git a/Programmation_Dynamique/EXERCICES.md b/Programmation_Dynamique/EXERCICES.md index 5185bca..8a20aac 100644 --- a/Programmation_Dynamique/EXERCICES.md +++ b/Programmation_Dynamique/EXERCICES.md @@ -22,6 +22,8 @@ Vous souhaitez monter un escalier de `n` marches. À chaque étape, vous pouvez **6.** Vérifiez que vos deux fonctions donnent les mêmes résultats pour `n` allant de 1 à 10. +--- + ## Exercice 2 — Rendu de monnaie revisité On dispose de pièces de valeurs **[2, 5, 10]** (en centimes). @@ -37,6 +39,8 @@ On dispose de pièces de valeurs **[2, 5, 10]** (en centimes). **4.** Modifiez la fonction pour qu'elle retourne non seulement le nombre de pièces, mais aussi la **liste des pièces utilisées** (comme la reconstruction dans le sac à dos). +--- + ## Exercice 3 — Sac à dos : à la main et en code On dispose des objets suivants, avec un sac de capacité **6 kg** : @@ -56,6 +60,8 @@ On dispose des objets suivants, avec un sac de capacité **6 kg** : **4.** Vérifiez vos réponses en exécutant `sac_reconstruction(objets, 6)`. +--- + ## Exercice 4 — Triangle de Pascal ★ Le **triangle de Pascal** est construit selon la règle suivante : diff --git a/Programmation_Dynamique/README.md b/Programmation_Dynamique/README.md index 6d66497..8ce3291 100644 --- a/Programmation_Dynamique/README.md +++ b/Programmation_Dynamique/README.md @@ -6,7 +6,7 @@ ![bo.png](assets/bo.png) -> La programmation dynamique est une méthode algorithmique permettant de résoudre efficacement des problèmes d'optimisation en mémorisant les résultats des sous-problèmes déjà résolus. +> La programmation dynamique est une méthode algorithmique permettant de résoudre efficacement des problèmes complexes en mémorisant les résultats des sous-problèmes déjà résolus. --- diff --git a/Programmation_Dynamique/RENDU_MONNAIE.md b/Programmation_Dynamique/RENDU_MONNAIE.md index f9a813f..e1f09ee 100644 --- a/Programmation_Dynamique/RENDU_MONNAIE.md +++ b/Programmation_Dynamique/RENDU_MONNAIE.md @@ -99,7 +99,7 @@ Chaque valeur de `rendu(s)` n'est calculée qu'une seule fois et mémorisée dan ### Version Bottom-Up (tableau) -On remplit un tableau `t` où `t[s]` représente le nombre minimum de pièces pour rendre la somme `s`, en partant de `s = 0` jusqu'à `s = somme`. +On remplit un tableau `tableau` où `tableau[s]` représente le nombre minimum de pièces pour rendre la somme `s`, en partant de `s = 0` jusqu'à `s = somme`. ```python def rendu_bottom_up(pieces, somme): diff --git a/Programmation_Dynamique/SAC_A_DOS.md b/Programmation_Dynamique/SAC_A_DOS.md index abe4125..b635160 100644 --- a/Programmation_Dynamique/SAC_A_DOS.md +++ b/Programmation_Dynamique/SAC_A_DOS.md @@ -40,7 +40,7 @@ Optimal : Nourriture (3kg, val 7) + Trousse (2kg, val 6) = 5kg → **valeur = 13 Notons `sac(i, w)` la valeur maximale qu'on peut atteindre en choisissant parmi les `i` premiers objets avec une capacité restante de `w`. -- **Cas de base :** `sac(0, w) = 0` (aucun objet disponible) et `sac(i, 0) = 0` (sac plein) +- **Cas de base :** `sac(0, w) = 0` (aucun objet disponible) et `sac(i, 0) = 0` (capacité épuisée) - **Cas récursif :** Pour l'objet `i` de poids `p_i` et de valeur `v_i` : - Si `p_i > w` : on ne peut pas le prendre → `sac(i, w) = sac(i-1, w)` - Sinon, on choisit le meilleur entre le prendre et ne pas le prendre : diff --git a/Programmation_Dynamique/TP_Donjon.md b/Programmation_Dynamique/TP_Donjon.md index 2dc9af0..712f94b 100644 --- a/Programmation_Dynamique/TP_Donjon.md +++ b/Programmation_Dynamique/TP_Donjon.md @@ -36,7 +36,7 @@ Est-ce le meilleur chemin ? C'est ce que vous allez découvrir. ## Partie 2 — Formulation récursive -Notons `donjon(i, j)` l'or maximum qu'on peut ramasser en partant de `(i, j)` pour atteindre `(n-1, m-1)`. +Notons `donjon(i, j)` l'or maximum qu'on peut ramasser depuis `(0, 0)` jusqu'à `(i, j)`. **Question 4.** Quels sont les cas de base de cette fonction récursive ? *(Pensez aux bords de la grille.)* @@ -84,8 +84,6 @@ def donjon_memo(grille): **Question 10.** Remplissez à la main le tableau `t[i][j]` représentant l'or maximum qu'on peut ramasser **depuis `(0,0)` jusqu'à `(i,j)`** pour la grille de l'exemple. -*(Attention : ici on part de `(0,0)` et on avance, pas de `(i,j)` vers la sortie.)* - **Question 11.** Implémentez `donjon_bottom_up(grille)` : ```python diff --git a/Programmation_Dynamique/TP_Vaccin.md b/Programmation_Dynamique/TP_Vaccin.md new file mode 100644 index 0000000..f838324 --- /dev/null +++ b/Programmation_Dynamique/TP_Vaccin.md @@ -0,0 +1,159 @@ +# TP — L'Algorithme du Vaccin + +## Contexte + +Vous êtes ingénieur·e en bio-informatique dans un laboratoire pharmaceutique. Une nouvelle épidémie virale se propage, et votre équipe dispose d'un **bioréacteur** capable de produire des anticorps pendant un temps limité. + +Vous avez identifié **n variants du virus**, chacun nécessitant : +- un **temps de production** (en heures) pour fabriquer les anticorps correspondants +- une **efficacité estimée** (score de 1 à 100) contre la souche circulante + +Le bioréacteur ne peut fonctionner que pendant **T heures** au total. Vous devez choisir quels anticorps produire pour **maximiser l'efficacité totale** du vaccin, sans dépasser le temps disponible. + +> Ce problème est directement modélisé par le **problème du sac à dos 0/1** : chaque anticorps est produit entièrement ou pas du tout. + +**Exemple de données :** + +| Anticorps | Temps (h) | Efficacité | Ratio eff/temps | +|-----------|-----------|------------|-----------------| +| Alpha | 3 h | 40 | 13,3 | +| Bêta | 5 h | 70 | 14,0 | +| Gamma | 2 h | 30 | **15,0** ← meilleur | +| Delta | 7 h | 100 | 14,3 | +| Epsilon | 6 h | 85 | 14,2 | + +Temps disponible : **10 heures** + +--- + +## Partie 1 — Comprendre le problème + +**Question 1.** En utilisant l'algorithme glouton (trier par ratio efficacité/temps décroissant), quels anticorps choisissez-vous ? Quelle est l'efficacité totale obtenue ? + +**Question 2.** Essayez d'autres combinaisons à la main. Trouvez-vous une solution d'efficacité strictement supérieure à celle du glouton ? + +**Question 3.** Combien y a-t-il de combinaisons possibles en tout pour `n = 5` anticorps ? Pour `n = 30` ? Qu'en déduisez-vous sur une approche exhaustive ? + +--- + +## Partie 2 — Formulation récursive + +Notons `vaccin(i, t)` l'efficacité maximale qu'on peut atteindre en choisissant parmi les `i` premiers anticorps avec un temps restant de `t` heures. + +**Question 4.** Quels sont les cas de base de cette fonction récursive ? + +**Question 5.** Écrivez la relation de récurrence pour `vaccin(i, t)` sachant que l'anticorps `i` a un temps de production `duree_i` et une efficacité `eff_i`. + +**Question 6.** Implémentez la version naïve `vaccin_naif(anticorps, i, t)` : + +```python +def vaccin_naif(anticorps, i, t): + # anticorps : liste de tuples (duree, efficacite) + # À compléter + pass + +# Test +anticorps = [(3, 40), (5, 70), (2, 30), (7, 100), (6, 85)] +n = len(anticorps) +print(vaccin_naif(anticorps, n, 10)) # doit afficher 140 +``` + +--- + +## Partie 3 — Version Top-Down (mémoïsation) + +**Question 7.** Pourquoi la version naïve recalcule-t-elle des sous-problèmes ? Donnez un exemple de sous-problème qui serait recalculé plusieurs fois. + +**Question 8.** Implémentez `vaccin_memo(anticorps, temps_total)` avec mémoïsation : + +```python +def vaccin_memo(anticorps, temps_total): + memo = {} + + def vaccin(i, t): + # À compléter + pass + + return vaccin(len(anticorps), temps_total) +``` + +**Question 9.** Vérifiez que vous obtenez le même résultat qu'à la Question 6. + +--- + +## Partie 4 — Version Bottom-Up (tableau) + +**Question 10.** Remplissez à la main le tableau `tableau[i][t]` pour les **3 premiers anticorps** (Alpha, Bêta, Gamma) avec un temps total de **6 heures** : + +| | t=0 | t=1 | t=2 | t=3 | t=4 | t=5 | t=6 | +|---|---|---|---|---|---|---|---| +| i=0 (aucun) | | | | | | | | +| i=1 (Alpha 3h, 40) | | | | | | | | +| i=2 (+Bêta 5h, 70) | | | | | | | | +| i=3 (+Gamma 2h, 30) | | | | | | | | + +**Question 11.** Implémentez `vaccin_bottom_up(anticorps, temps_total)` : + +```python +def vaccin_bottom_up(anticorps, temps_total): + n = len(anticorps) + # tableau[i][t] = efficacité max avec les i premiers anticorps, temps t disponible + tableau = [[0] * (temps_total + 1) for _ in range(n + 1)] + + for i in range(1, n + 1): + duree, eff = anticorps[i - 1] + for t in range(temps_total + 1): + # À compléter + pass + + return tableau[n][temps_total] +``` + +**Question 12.** Vérifiez que vous obtenez 140 pour l'exemple complet (5 anticorps, 10 heures). + +--- + +## Partie 5 — Reconstruction de la solution + +**Question 13.** Modifiez `vaccin_bottom_up` pour qu'elle retourne non seulement l'efficacité maximale, mais aussi la **liste des anticorps sélectionnés** : + +```python +def vaccin_reconstruction(anticorps, temps_total): + """ + Retourne (efficacite_max, liste_anticorps_choisis) + """ + n = len(anticorps) + tableau = [[0] * (temps_total + 1) for _ in range(n + 1)] + + # Remplissage du tableau (identique à vaccin_bottom_up) + # À compléter + + # Reconstruction : on remonte le tableau + choisis = [] + t = temps_total + for i in range(n, 0, -1): + # À compléter + pass + + return tableau[n][temps_total], choisis +``` + +**Question 14.** Testez votre fonction. Vérifiez que les anticorps sélectionnés ont bien une durée totale ≤ 10 heures et une efficacité totale de 140. + +**Question 15.** Quelle est la complexité en temps et en espace de la version bottom-up ? Exprimez-la en fonction de `n` (nombre d'anticorps) et `T` (temps total disponible). + +--- + +## Partie 6 — Bonus : contraintes supplémentaires + +**Question 16.** *(Bonus)* En réalité, certains anticorps sont **incompatibles** entre eux (ils réagissent chimiquement). Supposez que les anticorps Alpha et Gamma ne peuvent pas être produits ensemble. + +Comment modifieriez-vous l'algorithme pour prendre en compte cette contrainte ? Proposez une approche (pas nécessairement de code). + +--- + +Auteur : Florian Mathieu + +Licence CC BY NC + +Licence Creative Commons
Ce cours est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International.