Skip to content
Snippets Groups Projects
Commit 0b7aca1e authored by Romain Vuillemot's avatar Romain Vuillemot
Browse files

Update INF-TC1-td07.ipynb

parent 16ca032e
No related branches found
No related tags found
No related merge requests found
%% Cell type:markdown id:095a75fb tags: %% Cell type:markdown id:52685e4b tags:
NAME: NAME:
%% Cell type:markdown id:57c2dc5b-0ce7-47e4-bfeb-445c2f90cb6e tags: %% Cell type:markdown id:57c2dc5b-0ce7-47e4-bfeb-445c2f90cb6e tags:
# INF TC1 - TD7 (2h) - Rendu de monnaie # INF TC1 - TD7 (2h) - Rendu de monnaie
%% Cell type:markdown id:2a40aa1b-c3fe-4cd2-9236-3c7aeddedf55 tags: %% Cell type:markdown id:2a40aa1b-c3fe-4cd2-9236-3c7aeddedf55 tags:
--- ---
%% Cell type:markdown id:25995ac6-8673-46f2-9c77-37b48a889b73 tags: %% Cell type:markdown id:25995ac6-8673-46f2-9c77-37b48a889b73 tags:
## Objectif du TD ## Objectif du TD
Dans ce TD nous allons aborder le problème de rendu de monnaie selon plusieurs méthodes algorithmiques : Dans ce TD nous allons aborder le problème de rendu de monnaie selon plusieurs méthodes algorithmiques :
- technique dite Gloutonne - technique dite Gloutonne
- chemin minimal dans un arbre de recherche - chemin minimal dans un arbre de recherche
- Programmation Dynamique (devoir à rendre) - Programmation Dynamique (devoir à rendre)
Vous serez amené également à créer une structure de données de graphe et une méthode de parcours de celui-ci. Vous serez amené également à créer une structure de données de graphe et une méthode de parcours de celui-ci.
**Les réponses de la partie sur la programmation dynamique feront l'objet d'un autre sujet de TD et d'un rendu sous forme de rapport à rendre sur Moodle.** **Les réponses de la partie sur la programmation dynamique feront l'objet d'un autre sujet de TD et d'un rendu sous forme de rapport à rendre sur Moodle.**
%% Cell type:markdown id:c38c9de3-f716-4907-8387-7cf6f4f5e926 tags: %% Cell type:markdown id:c38c9de3-f716-4907-8387-7cf6f4f5e926 tags:
## Le problème de "rendu de monnaie" ## Le problème de "rendu de monnaie"
Le problème de rendu de monnaie est très fréquent dans la vie de tous les jours et peut être défini comme suit : étant donné un montant, une machine capable de rendre la monnaie doit rendre ce montant au client à partir de pièces (1c à 2€) et de billets. On suppose pour simplifier qu'il n'y a que des pièces en centimes; un billet de 5€ sera représenté comme une pièce de 500 centimes. On supposera dans un premier temps qu'il existe un nombre suffisant (autant que nécessaire) de chaque pièce, mais dans un second temps nous introduirons des contraintes de disponibilité des pièces. Le problème de rendu de monnaie est très fréquent dans la vie de tous les jours et peut être défini comme suit : étant donné un montant, une machine capable de rendre la monnaie doit rendre ce montant au client à partir de pièces (1c à 2€) et de billets. On suppose pour simplifier qu'il n'y a que des pièces en centimes; un billet de 5€ sera représenté comme une pièce de 500 centimes. On supposera dans un premier temps qu'il existe un nombre suffisant (autant que nécessaire) de chaque pièce, mais dans un second temps nous introduirons des contraintes de disponibilité des pièces.
%% Cell type:markdown id:5a29e913-8954-4e88-b477-234c4ee0fd9b tags: %% Cell type:markdown id:5a29e913-8954-4e88-b477-234c4ee0fd9b tags:
| | **la table S** | **la table D** | | | **la table S** | **la table D** |
|---------------|------------------------|----------------------------------| |---------------|------------------------|----------------------------------|
| **indice $i$**| **Valeur ($v_i$)** | **Disponibilité ($d_i$)** | | **indice $i$**| **Valeur ($v_i$)** | **Disponibilité ($d_i$)** |
| 1 | 1c | nombre de pièces de 1c disponibles | | 1 | 1c | nombre de pièces de 1c disponibles |
| 2 | 2c | nombre de pièces de 2c disponibles | | 2 | 2c | nombre de pièces de 2c disponibles |
| 3 | 5c | ... | | 3 | 5c | ... |
| 4 | 10c | ... | | 4 | 10c | ... |
| 5 | 20c | ... | | 5 | 20c | ... |
| 6 | 50c | ... | | 6 | 50c | ... |
| 7 | 100 (1€) | pièces de 1€ | | 7 | 100 (1€) | pièces de 1€ |
| 8 | 200 (2€) | pièces de 2€ | | 8 | 200 (2€) | pièces de 2€ |
| 9 | 500 (5€) | billets de 5€ | | 9 | 500 (5€) | billets de 5€ |
| 10 | 1000 | billets de 10€ | | 10 | 1000 | billets de 10€ |
| 11 | 2000 | billets de 20€ | | 11 | 2000 | billets de 20€ |
| 12 | 5000 | billets de 50€ | | 12 | 5000 | billets de 50€ |
| 13 | 10000 | billets de 100€ | | 13 | 10000 | billets de 100€ |
%% Cell type:markdown id:7f845f7b-8bd0-4544-8110-fa5ec1a7a960 tags: %% Cell type:markdown id:7f845f7b-8bd0-4544-8110-fa5ec1a7a960 tags:
De manière plus formelle, un stock de pièces est un tuple $S=(v_1, v_2, ..., v_n)$ où l'entier $v_i > 0$ est la valeur de la $i^{ème}$ pièce. Pour refléter le fait qu'on a des pièces de 1, 2 et 5c, $S$ contiendra $v_1=1$ (1 centime), $v_2=2, v_3=5$. Le problème de monnaie est un problème d'optimisation combinatoire $(S,M)$ permettant de trouver le tuple $T=(x_1, x_2, ..., x_n)$ avec $x_i \geq 0$ qui minimise $ \sum_{i=1}^n x_i$ sous la contrainte $\sum_{i=1}^n x_i.v_i=M$. Autrement dit, nous souhaitons aussi bien obtenir le montant exact, que minimiser le nombre total de pièces $x_i$ de valeur $v_i$ utilisées. Appelons $Q(S,M) = \sum_{i=1}^n x_i$ la quantité de pièces à rendre pour le montant *M* étant donné le système *S* décrit dans la Table~1. Une solution optimale $Q_{opt}$ à ce problème est telle que *Q(S,M)* soit minimale : De manière plus formelle, un stock de pièces est un tuple $S=(v_1, v_2, ..., v_n)$ où l'entier $v_i > 0$ est la valeur de la $i^{ème}$ pièce. Pour refléter le fait qu'on a des pièces de 1, 2 et 5c, $S$ contiendra $v_1=1$ (1 centime), $v_2=2, v_3=5$. Le problème de monnaie est un problème d'optimisation combinatoire $(S,M)$ permettant de trouver le tuple $T=(x_1, x_2, ..., x_n)$ avec $x_i \geq 0$ qui minimise $ \sum_{i=1}^n x_i$ sous la contrainte $\sum_{i=1}^n x_i.v_i=M$. Autrement dit, nous souhaitons aussi bien obtenir le montant exact, que minimiser le nombre total de pièces $x_i$ de valeur $v_i$ utilisées. Appelons $Q(S,M) = \sum_{i=1}^n x_i$ la quantité de pièces à rendre pour le montant *M* étant donné le système *S* décrit dans la Table~1. Une solution optimale $Q_{opt}$ à ce problème est telle que *Q(S,M)* soit minimale :
$Q_{opt}(S,M) = min \ \sum_{i=1}^n x_i$. $Q_{opt}(S,M) = min \ \sum_{i=1}^n x_i$.
Dans certaines situations il faudra gérer le nombre de pièces/billets disponibles (la table *D*). Nous noterons *d[i]=k* pour dire : il y a *k* pièces/billets du montant *$v_i$* disponibles (pièces ou billets du montant *v[i]*) à l'indice *i* dans la table *S*. On supposera cependant dans un premier temps qu'il y a un nombre suffisant de chaque pièce/billet dans le tableau S. On supposera également que *S* est ordonné dans un ordre croissant. Dans certaines situations il faudra gérer le nombre de pièces/billets disponibles (la table *D*). Nous noterons *d[i]=k* pour dire : il y a *k* pièces/billets du montant *$v_i$* disponibles (pièces ou billets du montant *v[i]*) à l'indice *i* dans la table *S*. On supposera cependant dans un premier temps qu'il y a un nombre suffisant de chaque pièce/billet dans le tableau S. On supposera également que *S* est ordonné dans un ordre croissant.
%% Cell type:markdown id:077ca702-bc18-4f2d-95e6-86fd4d53ebe7 tags: %% Cell type:markdown id:077ca702-bc18-4f2d-95e6-86fd4d53ebe7 tags:
## Exemples ## Exemples
**M = 9€:** étant donné S dans la Table ci-dessous, la solution qui minimise le nombre total de pièces rendues à 3 est *T=(0,0,0,0,0,0,0,2,1,0,0,0,0)*. Donc, $Q_{opt}(S,9) = \min \ Q(S,9) = 3$. Détails (avec des pièces $\geq$ 1€) : **M = 9€:** étant donné S dans la Table ci-dessous, la solution qui minimise le nombre total de pièces rendues à 3 est *T=(0,0,0,0,0,0,0,2,1,0,0,0,0)*. Donc, $Q_{opt}(S,9) = \min \ Q(S,9) = 3$. Détails (avec des pièces $\geq$ 1€) :
| Description | T | Rendu | | Description | T | Rendu |
|--------------------------------------------------------|----------------------------------------|---------------------------------------------| |--------------------------------------------------------|----------------------------------------|---------------------------------------------|
| 9 pièces de 1€ et 0 pour toutes les autres | T=(0,0,0,0,0,0,0,9,0,0,0...0) | $\rightarrow$ 9 pièces | $Q(S,9) =9$ | | 9 pièces de 1€ et 0 pour toutes les autres | T=(0,0,0,0,0,0,0,9,0,0,0...0) | $\rightarrow$ 9 pièces | $Q(S,9) =9$ |
| $\circ$ 5 $\times$ 1€ + 2 $\times$ 2€, 0 pour les autres | T=(0,0,0,0,0,0,0,5,2,0,0...0) | $\rightarrow$ 7 pièces | $Q(S,9) =7$ | | $\circ$ 5 $\times$ 1€ + 2 $\times$ 2€, 0 pour les autres | T=(0,0,0,0,0,0,0,5,2,0,0...0) | $\rightarrow$ 7 pièces | $Q(S,9) =7$ |
| $\circ$ 1 $\times$ 1€ + 4 $\times$ 2€, 0 pour les autres | T=(0,0,0,0,0,0,0,1,4,0,0...0) | $\rightarrow$ 5 pièces | $Q(S,9) =5$ | | $\circ$ 1 $\times$ 1€ + 4 $\times$ 2€, 0 pour les autres | T=(0,0,0,0,0,0,0,1,4,0,0...0) | $\rightarrow$ 5 pièces | $Q(S,9) =5$ |
| $\circ$ 2 $\times$ 2€ + 1 $\times$ 5€, 0 pour les autres | T=(0,0,0,0,0,0,0,0,2,1,0...0) | $\rightarrow$ 3 pièces | **$Q(S,9) =3$** | | $\circ$ 2 $\times$ 2€ + 1 $\times$ 5€, 0 pour les autres | T=(0,0,0,0,0,0,0,0,2,1,0...0) | $\rightarrow$ 3 pièces | **$Q(S,9) =3$** |
| $\circ$ 3 $\times$ 1 + 3 $\times$ 2, 0 pour les autres | T=(0,0,0,0,0,0,0,3,3,0,0...0) | $\rightarrow$ 6 pièces | | | $\circ$ 3 $\times$ 1 + 3 $\times$ 2, 0 pour les autres | T=(0,0,0,0,0,0,0,3,3,0,0...0) | $\rightarrow$ 6 pièces | |
| $\circ$ 4 $\times$ 1 + 1 $\times$ 5, 0 pour les autres | T=(0,0,0,0,0,0,0,4,0,1,0...0) | $\rightarrow$ 5 pièces | | | $\circ$ 4 $\times$ 1 + 1 $\times$ 5, 0 pour les autres | T=(0,0,0,0,0,0,0,4,0,1,0...0) | $\rightarrow$ 5 pièces | |
| $\circ$ etc. sans parler des solutions avec des centimes ! | | | | | $\circ$ etc. sans parler des solutions avec des centimes ! | | | |
<center> <center>
<i>Table 1</i> <i>Table 1</i>
</center> </center>
**M = 1989€:** pour rendre la somme de 1989€ pièces (sans les centimes), on aura : $1989 = 500 \times 3 + 488 = 500 \times 3 + 200 \times 2 + 50 \times 1 + 20 \times 1 + 10 \times 1 + 5 \times 1 + 2 \times 2$, soit $3+2+1+1+1 = 8$ grosses pièces (billets) et $1+2 = 3$ pièces. **M = 1989€:** pour rendre la somme de 1989€ pièces (sans les centimes), on aura : $1989 = 500 \times 3 + 488 = 500 \times 3 + 200 \times 2 + 50 \times 1 + 20 \times 1 + 10 \times 1 + 5 \times 1 + 2 \times 2$, soit $3+2+1+1+1 = 8$ grosses pièces (billets) et $1+2 = 3$ pièces.
%% Cell type:markdown id:254f7b95-aaeb-4f96-b8ec-47df74a3c3a3 tags: %% Cell type:markdown id:254f7b95-aaeb-4f96-b8ec-47df74a3c3a3 tags:
## Résolution du problème ## Résolution du problème
L'exemple de la Table 1 est un système *canonique*, c'est à dire qu'en choisissant systématiquement les pièces de plus grande valeur (algorithme glouton) on obtient toujours la solution optimale. Il existe des systèmes pour lesquels c'est moins simple, par exemple $S=(1,7,23)$. Pour **M = 28**, en choisissant en priorité les pièces de plus grande valeur on trouvera $T=(5,0,1)$ (6 pièces), alors que la solution optimale est $T=(0,4,0)$ (4 pièces). L'exemple de la Table 1 est un système *canonique*, c'est à dire qu'en choisissant systématiquement les pièces de plus grande valeur (algorithme glouton) on obtient toujours la solution optimale. Il existe des systèmes pour lesquels c'est moins simple, par exemple $S=(1,7,23)$. Pour **M = 28**, en choisissant en priorité les pièces de plus grande valeur on trouvera $T=(5,0,1)$ (6 pièces), alors que la solution optimale est $T=(0,4,0)$ (4 pièces).
Dans le cas général, ce problème est démontré NP-difficile, c'est-à-dire qu'on ne connaît pas d'algorithme qui puisse le résoudre en complexité polynomiale par rapport à la taille de $S$. Dans le cas général, ce problème est démontré NP-difficile, c'est-à-dire qu'on ne connaît pas d'algorithme qui puisse le résoudre en complexité polynomiale par rapport à la taille de $S$.
Il existe plusieurs approches : Il existe plusieurs approches :
1. Approche gloutonne (pas toujours optimale) 1. Approche gloutonne (pas toujours optimale)
2. Recherche de chemin de longueur minimale avec (ou sans) un arbre de recherche, ... 2. Recherche de chemin de longueur minimale avec (ou sans) un arbre de recherche, ...
3. Programmation Dynamique, ... et autres méthodes algorithmiques (cf. quasi-Dijkstra) 3. Programmation Dynamique, ... et autres méthodes algorithmiques (cf. quasi-Dijkstra)
Il existe d'autres approches de résolutions algorithmiques ou non-algorithmiques comme par exemple un système d'équations à optimiser. Nous allons dans ce TD aborder les différentes approches algorithmiques mentionnées ci-dessus. Il existe d'autres approches de résolutions algorithmiques ou non-algorithmiques comme par exemple un système d'équations à optimiser. Nous allons dans ce TD aborder les différentes approches algorithmiques mentionnées ci-dessus.
%% Cell type:markdown id:c8b2e4fe-cbfa-4301-89af-d02ea55473f0 tags: %% Cell type:markdown id:c8b2e4fe-cbfa-4301-89af-d02ea55473f0 tags:
## Algorithme Glouton ## Algorithme Glouton
Les techniques de programmation gloutonnes ont la particularité de faire des choix locaux à chaque étape de calcul. Cette méthode ne donne pas forcément un nombre minimal de pièces mais elle est simple à comprendre et à implémenter. Son application au problème de rendu de monnaie est simple : on trouve la pièce la plus grande inférieure ou égale à $M$. Soit $v_i$ cette pièce. On utilise ($x_i = M \ div \ v_i$) fois la pièce $v_i$; le reste à traiter sera $M'=(M \mod v_i)$. Ensuite, on recommence suivant le même principe pour satisfaire $M'$. Ce qui donne le pseudo-code d'algorithme suivant (qui fait l'hypothèse d'un nombre illimité de pièces, on ne tient pas compte ici de $D$ : $d_i=\infty$) : Les techniques de programmation gloutonnes ont la particularité de faire des choix locaux à chaque étape de calcul. Cette méthode ne donne pas forcément un nombre minimal de pièces mais elle est simple à comprendre et à implémenter. Son application au problème de rendu de monnaie est simple : on trouve la pièce la plus grande inférieure ou égale à $M$. Soit $v_i$ cette pièce. On utilise ($x_i = M \ div \ v_i$) fois la pièce $v_i$; le reste à traiter sera $M'=(M \mod v_i)$. Ensuite, on recommence suivant le même principe pour satisfaire $M'$. Ce qui donne le pseudo-code d'algorithme suivant (qui fait l'hypothèse d'un nombre illimité de pièces, on ne tient pas compte ici de $D$ : $d_i=\infty$) :
``` ```
Fonction Monnaie_Gloutonne Fonction Monnaie_Gloutonne
Entrées : la somme S, M Entrées : la somme S, M
Sorties : le vecteur T = Q(S,M) : le nombre de pièces nécessaires Sorties : le vecteur T = Q(S,M) : le nombre de pièces nécessaires
M'=M M'=M
Répéter Répéter
Chercher dans S l'indice i tel que Vi =< M' Chercher dans S l'indice i tel que Vi =< M'
Ti = M' div Vi Ti = M' div Vi
M' = M' mod Si M' = M' mod Si
Jusqu'à M' = 0 Jusqu'à M' = 0
Constituer T avec les Ti utilisés Constituer T avec les Ti utilisés
$Q(S,M) = somme de i=1 a i=n de T_i$ Q(S,M) = somme de i=1 a i=n de T_i
T est la valeur de sortie de l'algorithme T est la valeur de sortie de l'algorithme
Fin Monnaie_Gloutonne Fin Monnaie_Gloutonne
``` ```
Pour **M = 236,65€** cet algorithme se déroule de la manière suivante : Pour **M = 236,65€** cet algorithme se déroule de la manière suivante :
$23665 \geq 10000$ $23665 \geq 10000$
- $T_{13}=23665 \ div \ 10000 = 2$ ($T_{13}$ correspond à 100€) - $T_{13}=23665 \ div \ 10000 = 2$ ($T_{13}$ correspond à 100€)
- $M'=23665 \ mod \ 10000 = 3665$ - $M'=23665 \ mod \ 10000 = 3665$
$3665 \geq 2000$ $3665 \geq 2000$
- $T_{11}=3665 \ div \ 2000 = 1$ - $T_{11}=3665 \ div \ 2000 = 1$
- $M'=3665 \ mod \ 2000 = 1665$ - $M'=3665 \ mod \ 2000 = 1665$
... ...
$\rightarrow$ On obtient $T_{1..13}=(0,0,1,1,0,1,1,0,1,1,1,0,2)$ et $Q(S,M) = 9$ $\rightarrow$ On obtient $T_{1..13}=(0,0,1,1,0,1,1,0,1,1,1,0,2)$ et $Q(S,M) = 9$
A noter qu'en Python les indices commencent à zéro ! Faire -1 sur les indices ci-dessus. A noter qu'en Python les indices commencent à zéro ! Faire -1 sur les indices ci-dessus.
%% Cell type:markdown id:425fe58a-b0bc-41eb-acea-0d88e1daf18f tags: %% Cell type:markdown id:425fe58a-b0bc-41eb-acea-0d88e1daf18f tags:
**Question 1.1 -** Implémenter l'algorithme glouton ci-dessus. **Question 1.1 -** Implémenter l'algorithme glouton ci-dessus.
%% Cell type:code id:e3880c0c-4c2c-48c4-96b1-0c95c712b489 tags: %% Cell type:code id:e3880c0c-4c2c-48c4-96b1-0c95c712b489 tags:
``` python ``` python
def gloutonne(S: list, M: int) -> list: def gloutonne(S: list, M: int) -> tuple:
""" """
Algorithme glouton Algorithme glouton
Args: Args:
- S (list): Une liste d'entier représentant les pièces - S (list): Une liste d'entier représentant les pièces
- M (int): La somme à atteindre - M (int): La somme à atteindre
Returns: Returns:
- list: (True, Q(S,M)) si succès OU (False, None) si échec. - tuple: (True, Q(S,M)) si succès OU (False, None) si échec.
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:markdown id:2a39cbfb-d35d-4da7-bbaa-d273bc900c2d tags: %% Cell type:markdown id:2a39cbfb-d35d-4da7-bbaa-d273bc900c2d tags:
Les tests suivants doivent être validés : Les tests suivants doivent être validés :
%% Cell type:code id:90cd609d-71d9-41b0-ba28-c66fd94a2524 tags: %% Cell type:code id:90cd609d-71d9-41b0-ba28-c66fd94a2524 tags:
``` python ``` python
Rep= gloutonne([1,7,23], 28) Rep= gloutonne(sorted([1, 7, 23]), 28)
print(Rep, ': ', sum(Rep),' pieces') if Rep else print('Echec') print(Rep[1], ': ', len(Rep[1]),' pièces') if Rep[0] else print('Echec')
# [5, 0, 1] : 6 pieces # [5, 0, 1] : 3 pièces Un epièce de 23 et 5 pièces de 1
Rep= gloutonne([7,23], 5) Rep= gloutonne(sorted([7, 23]), 5)
print(Rep, ': ', sum(Rep),' pieces') if Rep else print('Echec') print(Rep[1], ': ', len(Rep[1]),' pièces') if Rep[0] else print('Echec')
# Echec car M < la + petite pièce # Echec car M < la + petite pièce
Rep= gloutonne([7,23], 8) Rep= gloutonne(sorted([7, 23]), 8)
print(Rep, ': ', sum(Rep),' pieces') if Rep else print('Echec') print(Rep[1], ': ', len(Rep[1]),' pièces') if Rep[0] else print('Echec')
# Echec car on ne peut jamais faire la monnaie avec cette S # Echec car on ne peut jamais faire la monnaie avec cette S
Rep= gloutonne([1,5,10,25], 63)
print(Rep, ': ', sum(Rep),' pieces') if Rep else print('Echec')
# [3, 0, 1, 2] : 6 pieces
``` ```
%% Cell type:markdown id:c5d61f32-13cc-4176-8f47-b0ca861a74ba tags: %% Cell type:markdown id:c5d61f32-13cc-4176-8f47-b0ca861a74ba tags:
**Question 1.2 -** Proposez une modification du pseudo-code ci-dessus pour prendre en compte un nombre **limité** de pièces (en prenant en compte la table $D$). Implémenter cet algorithme modifié (vous fournirez vos propres valeurs de disponibilité dans $D$). **Question 1.2 -** Proposez une modification du pseudo-code ci-dessus pour prendre en compte un nombre **limité** de pièces (en prenant en compte la table $D$). Implémenter cet algorithme modifié (vous fournirez vos propres valeurs de disponibilité dans $D$).
%% Cell type:code id:91951f93-bf34-4ea9-ad08-76c73ff3ce44 tags: %% Cell type:code id:91951f93-bf34-4ea9-ad08-76c73ff3ce44 tags:
``` python ``` python
def gloutonne_avec_D(S: list, M: int, D: int) -> tuple: def gloutonne_avec_D(S: list, M: int, D: int) -> tuple:
""" """
Algorithme glouton avec nombre limité de pièces Algorithme glouton avec nombre limité de pièces
Args: Args:
- S (list): Une liste d'entier représentant les pièces - S (list): Une liste d'entier représentant les pièces
- M (int): La somme ) à atteindre - M (int): La somme ) à atteindre
- D (int): La disponibilité des pièces - D (int): La disponibilité des pièces
Returns: Returns:
- list: (True, Q(S,M)) si succès OU (False, None) si échec. - tuple: (True, Q(S,M)) si succès OU (False, None) si échec.
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:markdown id:9786ca50-6bfe-491d-8971-89be91ccfd8b tags: %% Cell type:markdown id:9786ca50-6bfe-491d-8971-89be91ccfd8b tags:
Les tests suivants doivent être validés : Les tests suivants doivent être validés :
%% Cell type:code id:b5af3022-65fe-4877-99ce-23de3843a5ec tags: %% Cell type:code id:a09ba678-bff6-4728-b301-6579f8a62e40 tags:
``` python ``` python
Rep= gloutonne_avec_D([1,7,23], 28,[10,10,10]) Rep= gloutonne_avec_D(sorted([1,7,23]), 28, [10,10,0])
print(Rep, ': ', sum(Rep),' pieces') if Rep else print('Echec') print(Rep[1], ': ', len(Rep[1]),' pièces') if Rep[0] else print('Echec')
# [5, 0, 1] : 6 pieces # [0, 4, 0] : 3 pièces # On utilise 7 pièces
Rep= gloutonne_avec_D([1,7,23], 28,[10,10,0])
print(Rep, ': ', sum(Rep),' pieces') if Rep else print('Echec')
# [0, 4, 0] : 4 pieces
Rep= gloutonne_avec_D([1,5,10,25], 63, [1,0,3,2])
print(Rep, ': ', sum(Rep),' pieces') if Rep else print('Echec')
# Echec
``` ```
%% Cell type:markdown id:70534a4f-cc60-4c29-b5e6-1392126fc9f5 tags: %% Cell type:markdown id:70534a4f-cc60-4c29-b5e6-1392126fc9f5 tags:
Vous noterez que la méthode Gloutonne ne garantit pas que *Q(S, M)* soit minimal comme les tests l'ont montré pour M=28 et S=(1, 7, 23). La méthode Gloutonne utilise un optimum local (choix de la plus grande pièce) qui ne débouche pas forcément sur un optimum global. Cependant, elle est très simple à calculer. Vous noterez que la méthode Gloutonne ne garantit pas que *Q(S, M)* soit minimal comme les tests l'ont montré pour M=28 et S=(1, 7, 23). La méthode Gloutonne utilise un optimum local (choix de la plus grande pièce) qui ne débouche pas forcément sur un optimum global. Cependant, elle est très simple à calculer.
%% Cell type:markdown id:321b284b-a735-47b5-a889-ef52006c967e tags: %% Cell type:markdown id:321b284b-a735-47b5-a889-ef52006c967e tags:
## Chemin minimal dans un arbre ## Chemin minimal dans un arbre
Une deuxième méthode consiste à construire toutes les solutions possibles sous forme d'arbre, et ensuite de choisir la solution de manière globale. Cette méthode est plus complexe à mettre en oeuvre mais donne une solution optimale. Une deuxième méthode consiste à construire toutes les solutions possibles sous forme d'arbre, et ensuite de choisir la solution de manière globale. Cette méthode est plus complexe à mettre en oeuvre mais donne une solution optimale.
%% Cell type:markdown id:431ee4d7-1fa8-4bfd-bf9c-ec0725d08e5b tags: %% Cell type:markdown id:431ee4d7-1fa8-4bfd-bf9c-ec0725d08e5b tags:
<center> <center>
<img src="monnaie-graph.png" alt="Image originale" style="height:10cm;"> <img src="monnaie-graph.png" alt="Image originale" style="height:10cm;">
</center> </center>
Exemple d'arbre de recherche résultant *M=28* et *S=(1,7,23)*. C'est à dire, on a seulement des pièces de 1, 7 et 23 (€ ou cents) en nombre suffisant. Dès qu'on atteint 0, on remonte de ce 0 à la racine pour obtenir la solution minimale donnant le nombre minimal d'arcs entre ce 0 et la racine. On remarque qu'on utilisera 4 pièces de 7c pour avoir 28c. Par ailleurs, on remarque que le choix initial de 23 (à gauche de l'arbre) laisse le montant *M'=5c* à satisfaire lequel impose le choix de 5 pièces de 1c. En bleu les autres chemins choisissant une pièce de 23. Exemple d'arbre de recherche résultant *M=28* et *S=(1,7,23)*. C'est à dire, on a seulement des pièces de 1, 7 et 23 (€ ou cents) en nombre suffisant. Dès qu'on atteint 0, on remonte de ce 0 à la racine pour obtenir la solution minimale donnant le nombre minimal d'arcs entre ce 0 et la racine. On remarque qu'on utilisera 4 pièces de 7c pour avoir 28c. Par ailleurs, on remarque que le choix initial de 23 (à gauche de l'arbre) laisse le montant *M'=5c* à satisfaire lequel impose le choix de 5 pièces de 1c. En bleu les autres chemins choisissant une pièce de 23.
%% Cell type:markdown id:36bc9ae5-81f2-4f12-9b94-03d90db050c0 tags: %% Cell type:markdown id:36bc9ae5-81f2-4f12-9b94-03d90db050c0 tags:
On construit un arbre de recherche (arbre des possibilités) dont la racine est *M*. Chaque noeud de l'arbre représente un montant : les noeuds autres que la racine initiale représentent $M' < M$ une fois qu'on aura utilisé une (seule) des pièces de $S$ (parmi 1, 7 ou 23€). La pièce utilisée pour aller de $M$ à $M'$ sera la valeur de l'arc reliant ces deux montants. Cet arbre est donc développé par niveau (en largeur). On arrête de développer un niveau supplémentaire dès qu'un noeud a atteint 0 auquel cas une solution sera le vecteur des valeurs (des arcs) allant de la racine à ce noeud. On construit un arbre de recherche (arbre des possibilités) dont la racine est *M*. Chaque noeud de l'arbre représente un montant : les noeuds autres que la racine initiale représentent $M' < M$ une fois qu'on aura utilisé une (seule) des pièces de $S$ (parmi 1, 7 ou 23€). La pièce utilisée pour aller de $M$ à $M'$ sera la valeur de l'arc reliant ces deux montants. Cet arbre est donc développé par niveau (en largeur). On arrête de développer un niveau supplémentaire dès qu'un noeud a atteint 0 auquel cas une solution sera le vecteur des valeurs (des arcs) allant de la racine à ce noeud.
Le principe de l'algorithme correspondant à un parcours en largeur dont la version itérative utilise une *File d'attente* est : Le principe de l'algorithme correspondant à un parcours en largeur dont la version itérative utilise une *File d'attente* est :
``` ```
Fonction Monnaie_graphe Fonction Monnaie_graphe
% on suppose qu'il y a un nombre suffisant de chaque pièce/billet dans la tableau S % on suppose qu'il y a un nombre suffisant de chaque pièce/billet dans la tableau S
Entrées : la somme M Entrées : la somme M
Sorties : le vecteur T et Qopt(S,M) le nombre de pièces nécessaires Sorties : le vecteur T et Qopt(S,M) le nombre de pièces nécessaires
File F = vide ; File F = vide ;
Arbre A contenant un noeud racine dont la valeur = M Arbre A contenant un noeud racine dont la valeur = M
Enfiler(M) % la file F contient initialement M Enfiler(M) % la file F contient initialement M
Répéter Répéter
M'= défiler() M'= défiler()
Pour chaque pièce vi =< M' disponible dans S : Pour chaque pièce vi =< M' disponible dans S :
S'il existe dans l'arbre A un noeud dont la valeur est M' - vi S'il existe dans l'arbre A un noeud dont la valeur est M' - vi
Alors établir un arc étiqueté par vi allant de M' à ce noeud Alors établir un arc étiqueté par vi allant de M' à ce noeud
Sinon Sinon
Créer un nouveau noeud de valeur M' - vi dans A et lier ce noeud Créer un nouveau noeud de valeur M' - vi dans A et lier ce noeud
à M' par un arc étiqueté par vi à M' par un arc étiqueté par vi
Enfiler ce nouveau noeud Enfiler ce nouveau noeud
Fin Si Fin Si
Jusqu'à (M' - vi = 0) ou (F = vide) Jusqu'à (M' - vi = 0) ou (F = vide)
Si (F est vide Et M' - vi /= 0) Si (F est vide Et M' - vi /= 0)
Alors il y a un problème dans les calculs ! STOP. Alors il y a un problème dans les calculs ! STOP.
Sinon Le dernier noeud créer porte la valeur 0 Sinon Le dernier noeud créer porte la valeur 0
On remonte de ce noeud à la racine et on comptabilise dans T où On remonte de ce noeud à la racine et on comptabilise dans T où
Ti = le nombre d'occurrences des arcs étiquetés vi de la racine Ti = le nombre d'occurrences des arcs étiquetés vi de la racine
au noeud v de valeur 0 au noeud v de valeur 0
$Q(S,M) = somme de i=1 a i=n de T_i$ $Q(S,M) = somme de i=1 a i=n de T_i$
Fin si Fin si
Fin Monnaie_graphe Fin Monnaie_graphe
``` ```
%% Cell type:markdown id:d15c2fca-64f1-4d6d-b6bf-5edf9b095828 tags: %% Cell type:markdown id:d15c2fca-64f1-4d6d-b6bf-5edf9b095828 tags:
**Question 2.1 -** Ecrire un algorithme de construction de l'arbre. **Question 2.1 -** Ecrire un algorithme de construction de l'arbre.
%% Cell type:code id:2d2556ed-8be9-416f-a976-8964ccca8061 tags: %% Cell type:code id:2d2556ed-8be9-416f-a976-8964ccca8061 tags:
``` python ``` python
def Monnaie_graphe(S: list, M: int) -> dict: def Monnaie_graphe(S: list, M: int) -> dict:
""" """
Génère un graphe représentant les combinaisons de pièces possibles pour atteindre un montant spécifié Génère un graphe représentant les combinaisons de pièces possibles pour atteindre un montant spécifié
en utilisant un ensemble donné de valeurs de pièces. en utilisant un ensemble donné de valeurs de pièces.
Args: Args:
- S (list): Une liste d'entiers représentant les valeurs disponibles des pièces. - S (list): Une liste d'entiers représentant les valeurs disponibles des pièces.
- M (int): Un entier spécifiant le montant cible à atteindre. - M (int): Un entier spécifiant le montant cible à atteindre.
Returns: Returns:
- dict: Un graphe (représenté sous forme de dictionnaire) - dict: Un graphe (représenté sous forme de dictionnaire)
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:c2709c24-bbe0-4c6a-aaf8-f2e3712131f5 tags: %% Cell type:code id:c2709c24-bbe0-4c6a-aaf8-f2e3712131f5 tags:
``` python ``` python
A = Monnaie_graphe([1, 2, 5], 7) A = Monnaie_graphe([1, 2, 5], 7)
print("Arbre créé", A) print("Arbre créé", A)
``` ```
%% Cell type:markdown id:91d7262d-9a9c-4f22-b60e-6d43267a59bb tags: %% Cell type:markdown id:91d7262d-9a9c-4f22-b60e-6d43267a59bb tags:
Vous devriez pouvoir visualiser votre arbre avec la méthode `plot_graph` (chargez d'abord les dernières cellules du notebook afin d'initialiser cette fonction) : Vous devriez pouvoir visualiser votre arbre avec la méthode `plot_graph` (chargez d'abord les dernières cellules du notebook afin d'initialiser cette fonction) :
%% Cell type:code id:701a7359-145f-4077-a8ee-e056eb3447f1 tags: %% Cell type:code id:701a7359-145f-4077-a8ee-e056eb3447f1 tags:
``` python ``` python
plot_graph(A) plot_graph(A)
``` ```
%% Cell type:markdown id:9de0a303-b587-4901-b0ac-68a7cd04bfc7 tags: %% Cell type:markdown id:9de0a303-b587-4901-b0ac-68a7cd04bfc7 tags:
**Question 2.2 -** Ecrire un algorithme de recherche du chemin le plus court dans l'arbre. **Question 2.2 -** Ecrire un algorithme de recherche du chemin le plus court dans l'arbre.
%% Cell type:code id:f5f033d8-1d49-49c7-9af7-e1cd59905d96 tags: %% Cell type:code id:f5f033d8-1d49-49c7-9af7-e1cd59905d96 tags:
``` python ``` python
def plus_court_chemin(graph: dict, start: str): def plus_court_chemin(graph: dict, start: str):
""" """
Calcule les distances les plus courtes depuis un nœud de départ dans un graphe. Calcule les distances les plus courtes depuis un nœud de départ dans un graphe.
Args: Args:
- graph (dict): Un dictionnaire représentant le graphe - graph (dict): Un dictionnaire représentant le graphe
- start (str): Le nœud de départ à partir duquel calculer les distances. - start (str): Le nœud de départ à partir duquel calculer les distances.
Returns: Returns:
- int: la taille du chemin - int: la taille du chemin
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:7aca69d8-79ed-4f06-bb25-5c42b116cbc8 tags: %% Cell type:code id:7aca69d8-79ed-4f06-bb25-5c42b116cbc8 tags:
``` python ``` python
assert plus_court_chemin(A, 3)[0] == 2 assert plus_court_chemin(A, 3)[0] == 2
``` ```
%% Cell type:markdown id:72832556-e0d7-4959-8d93-2b55145e7c25 tags: %% Cell type:markdown id:72832556-e0d7-4959-8d93-2b55145e7c25 tags:
**Question 2.3 -** Compléter l'algorithme précédent avec un mécanisme de backtracking permettant de retourner le plus court chemin utilisé. **Question 2.3 -** Compléter l'algorithme précédent avec un mécanisme de backtracking permettant de retourner le plus court chemin utilisé.
%% Cell type:code id:636ed054-38e5-46d1-b44f-c2d861bf1cc0 tags: %% Cell type:code id:636ed054-38e5-46d1-b44f-c2d861bf1cc0 tags:
``` python ``` python
def plus_court_chemin_backtracking(graph: dict, start: str): def plus_court_chemin_backtracking(graph: dict, start: str):
""" """
Calcule les distances les plus courtes depuis un nœud de départ dans un graphe. Calcule les distances les plus courtes depuis un nœud de départ dans un graphe.
Args: Args:
- graph (dict): Un dictionnaire représentant le graphe - graph (dict): Un dictionnaire représentant le graphe
- start (str): Le nœud de départ à partir duquel calculer les distances. - start (str): Le nœud de départ à partir duquel calculer les distances.
Returns: Returns:
- tuple: le chemin utilisé et les montants intermédiaires - tuple: le chemin utilisé et les montants intermédiaires
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:b0253f7c-0f07-436c-b72c-2411fb757e47 tags: %% Cell type:code id:b0253f7c-0f07-436c-b72c-2411fb757e47 tags:
``` python ``` python
assert plus_court_chemin_backtracking(A, 7) == [7, 5, 0] assert plus_court_chemin_backtracking(A, 7) == [7, 5, 0]
``` ```
%% Cell type:markdown id:f358da32-9534-493c-80b1-60d387f89229 tags: %% Cell type:markdown id:f358da32-9534-493c-80b1-60d387f89229 tags:
A noter que vous pouvez combiner la construction et la recherche du plus court chemin (en vous arrêtant au premier 0 rencontré). Cependant, cette methode a pour inconvénient d'explorer un très large espace de recherche (qui devient vite très grand). Un autre inconvénient est le calcul de solutions dont on sait qu'elles ne seront pas optimales. A noter que vous pouvez combiner la construction et la recherche du plus court chemin (en vous arrêtant au premier 0 rencontré). Cependant, cette methode a pour inconvénient d'explorer un très large espace de recherche (qui devient vite très grand). Un autre inconvénient est le calcul de solutions dont on sait qu'elles ne seront pas optimales.
%% Cell type:markdown id:91af94f8-c687-4739-b4fb-ef58cb7e6081 tags: %% Cell type:markdown id:91af94f8-c687-4739-b4fb-ef58cb7e6081 tags:
## Utils ## Utils
%% Cell type:code id:7cbf74a0-525c-4b6f-b078-5f0eea6fe09d tags: %% Cell type:code id:7cbf74a0-525c-4b6f-b078-5f0eea6fe09d tags:
``` python ``` python
from graphviz import Digraph from graphviz import Digraph
from IPython.display import display, Image from IPython.display import display, Image
import graphviz import graphviz
``` ```
%% Cell type:code id:83f489e1-8fb7-45ec-a906-4d2699c63a46 tags: %% Cell type:code id:83f489e1-8fb7-45ec-a906-4d2699c63a46 tags:
``` python ``` python
def plot_graph(graph): def plot_graph(graph):
dot = graphviz.Digraph() dot = graphviz.Digraph()
for node in graph: for node in graph:
dot.node(str(node)) dot.node(str(node))
for node, neighbors in graph.items(): for node, neighbors in graph.items():
for neighbor, weight in neighbors.items(): for neighbor, weight in neighbors.items():
dot.edge(str(node), str(neighbor), label=str(weight)) dot.edge(str(node), str(neighbor), label=str(weight))
display(dot) display(dot)
``` ```
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment