correction tp et ajout tp de secours

This commit is contained in:
2026-03-30 21:21:43 +02:00
parent 944845b5bf
commit 06e73e7622
7 changed files with 359 additions and 6 deletions

View File

@@ -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)``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
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.

View File

@@ -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 :

View File

@@ -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.
---

View File

@@ -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``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``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):

View File

@@ -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 :

View File

@@ -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

View File

@@ -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
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.