Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • rvuillem/inf-tc1
  • hwei/inf-tc1
  • nmbengue/inf-tc1
  • fernanda/inf-tc1
  • mleger/inf-tc1
  • lmeng/inf-tc1
  • gferryla/inf-tc1
  • jconso/inf-tc1
  • smaghsou/inf-tc1
  • emarquet/inf-tc1
  • ecluzel/inf-tc1
  • aaudeoud/inf-tc1
  • tsegond/inf-tc1
  • aetienne/inf-tc1
  • djoly/inf-tc1
  • bcampeas/inf-tc1
  • dnovarez/inf-tc1
  • ruetm/inf-tc1
  • cchenu/inf-tc1
  • cguiotdu/inf-tc1
  • mclouard/inf-tc1
  • gwachowi/inf-tc1
  • qbaalaou/inf-tc1
  • sbrocas/inf-tc1
  • ppupion/inf-tc1
  • kinty/inf-tc1
  • hadomo/inf-tc1
  • tgicquel/inf-tc1
  • rhahn/inf-tc1
  • cguyau/inf-tc1
  • mpairaul/inf-tc1
  • rmuller/inf-tc1
  • rlecharp/inf-tc1
  • asebasty/inf-tc1
  • qmaler/inf-tc1
  • aoussaid/inf-tc1
  • kcherigu/inf-tc1
  • sgu/inf-tc1
  • malcalat/inf-tc1
  • afalourd/inf-tc1
  • phugues/inf-tc1
  • lsteunou/inf-tc1
  • llauschk/inf-tc1
  • langloia/inf-tc1
  • aboucard/inf-tc1
  • wmellali/inf-tc1
  • ifaraidi/inf-tc1
  • lir/inf-tc1
  • ynedjar/inf-tc1
  • schneidl/inf-tc1
  • zprandi/inf-tc1
  • acoradid/inf-tc1
  • amarcq/inf-tc1
  • dcombet/inf-tc1
  • gplaud/inf-tc1
  • mkernoaj/inf-tc1
  • gbichot/inf-tc1
  • tdutille/inf-tc1
58 results
Select Git revision
Show changes
Showing
with 1719 additions and 0 deletions
%% Cell type:markdown id:7f210b68 tags:
NAME:
%% Cell type:markdown id:57c2dc5b-0ce7-47e4-bfeb-445c2f90cb6e tags:
# INF TC1 - TD7 (2h) - Rendu de monnaie
%% Cell type:markdown id:2a40aa1b-c3fe-4cd2-9236-3c7aeddedf55 tags:
---
%% Cell type:markdown id:25995ac6-8673-46f2-9c77-37b48a889b73 tags:
## Objectif du TD
Dans ce TD nous allons aborder le problème dit de de _rendu de monnaie_ selon plusieurs méthodes algorithmiques :
- technique de programmation Gloutonne
- chemin minimal dans un arbre de recherche
- Programmation Dynamique (cela fera l'objet du **prochain TD7bis et du devoir à rendre**)
Vous serez amené dans ce TD (et les suivant) à 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.**
%% Cell type:markdown id:c38c9de3-f716-4907-8387-7cf6f4f5e926 tags:
## 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€, etc.) et de billets (10€, 50€, etc.). 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 suppose également (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:
| | **la table S** | **la table D** |
|---------------|------------------------|----------------------------------|
| **indice $i$**| **Valeur ($v_i$)** | **Disponibilité ($d_i$)** |
| 1 | 1c | nombre de pièces de 1c disponibles |
| 2 | 2c | nombre de pièces de 2c disponibles |
| 3 | 5c | ... |
| 4 | 10c | ... |
| 5 | 20c | ... |
| 6 | 50c | ... |
| 7 | 100 (1€) | pièces de 1€ |
| 8 | 200 (2€) | pièces de 2€ |
| 9 | 500 (5€) | billets de 5€ |
| 10 | 1000 | billets de 10€ |
| 11 | 2000 | billets de 20€ |
| 12 | 5000 | billets de 50€ |
| 13 | 10000 | billets de 100€ |
%% 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 :
$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.
%% Cell type:markdown id:077ca702-bc18-4f2d-95e6-86fd4d53ebe7 tags:
## 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€) :
| 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$ |
| $\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$ 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$ 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 ! | | | |
<center>
<i>Table 1</i>
</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.
%% Cell type:markdown id:254f7b95-aaeb-4f96-b8ec-47df74a3c3a3 tags:
## 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).
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 :
1. Approche gloutonne (pas toujours optimale)
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)
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:
## 1. 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$) :
```
Fonction Monnaie_Gloutonne
Entrées : la somme S, M
Sorties : le vecteur T = Q(S,M) : le nombre de pièces nécessaires
M'=M
Répéter
Chercher dans S l'indice i tel que Vi =< M'
Ti = M' div Vi
M' = M' mod Si
Jusqu'à M' = 0
Constituer T avec les Ti utilisés
Q(S,M) = somme de i=1 a i=n de T_i
T est la valeur de sortie de l'algorithme
Fin Monnaie_Gloutonne
```
Pour **M = 236,65€** cet algorithme se déroule de la manière suivante :
$23665 \geq 10000$
- $T_{13}=23665 \ div \ 10000 = 2$ ($T_{13}$ correspond à 100€)
- $M'=23665 \ mod \ 10000 = 3665$
$3665 \geq 2000$
- $T_{11}=3665 \ div \ 2000 = 1$
- $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$
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:
**Question 1.1 -** Implémenter l'algorithme glouton ci-dessus.
%% Cell type:code id:e3880c0c-4c2c-48c4-96b1-0c95c712b489 tags:
``` python
def gloutonne(S: list, M: int) -> tuple:
"""
Algorithme glouton sans nombre limité de pièces
Args:
- S (list): Une liste d'entier représentant les pièces
- M (int): La somme à atteindre
Returns:
- tuple: (True, Q(S,M)) si succès OU (False, None) si échec
"""
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:2a39cbfb-d35d-4da7-bbaa-d273bc900c2d tags:
Les tests suivants doivent être validés :
%% Cell type:code id:90cd609d-71d9-41b0-ba28-c66fd94a2524 tags:
``` python
Rep = gloutonne(sorted([1, 7, 23], reverse=True), 28)
print(Rep[1], ': ', sum(Rep[1]),' pièces') if Rep[0] else print('Echec')
# [28, 0, 0] : 28 pièces
Rep = gloutonne(sorted([7, 23], reverse=True), 5)
print(Rep[1], ': ', sum(Rep[1]),' pièces') if Rep[0] else print('Echec')
# Echec car M < la + petite pièce
Rep = gloutonne(sorted([7, 23], reverse=True), 8)
print(Rep[1], ': ', sum(Rep[1]),' pièces') if Rep[0] else print('Echec')
# Echec car on ne peut jamais faire la monnaie avec cette S
```
%% 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$).
%% Cell type:code id:91951f93-bf34-4ea9-ad08-76c73ff3ce44 tags:
``` python
def gloutonne_avec_D(S: list, M: int, D: list) -> tuple:
"""
Algorithme glouton avec nombre limité de pièces
Args:
- S (list): Une liste d'entier représentant les pièces
- M (int): La somme à atteindre
- D (list): La disponibilité des pièces
Returns:
- tuple: (True, Q(S,M)) si succès OU (False, None) si échec
"""
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:9786ca50-6bfe-491d-8971-89be91ccfd8b tags:
Les tests suivants doivent être validés :
%% Cell type:code id:a09ba678-bff6-4728-b301-6579f8a62e40 tags:
``` python
Rep= gloutonne_avec_D([23, 7, 1], 28, [0, 10, 10]) # On ne fait pas de tri car il faudrait trier D aussi
print(Rep[1], ': ', sum(Rep[1]),' pièces utilisées') if Rep[0] else print('Echec')
# Echec
Rep= gloutonne_avec_D([23, 7, 1], 28, [0, 1, 21])
print(Rep[1], ': ', sum(Rep[1]),' pièces utilisées') if Rep[0] else print('Echec')
# [21, 1, 0] : 22 pièces utilisées
```
%% 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.
%% Cell type:markdown id:321b284b-a735-47b5-a889-ef52006c967e tags:
## 2. 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.
%% Cell type:markdown id:431ee4d7-1fa8-4bfd-bf9c-ec0725d08e5b tags:
<center>
<img src="monnaie-graph.png" alt="Image originale" style="height:10cm;">
</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.
%% 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.
Le principe de l'algorithme correspondant à un parcours en largeur dont la version itérative utilise une *File d'attente* est :
```
Fonction Monnaie_graphe
% on suppose qu'il y a un nombre suffisant de chaque pièce/billet dans la tableau S
Entrées : la somme M
Sorties : le vecteur T et Qopt(S,M) le nombre de pièces nécessaires
File F = vide ;
Arbre A contenant un noeud racine dont la valeur = M
Enfiler(M) % la file F contient initialement M
Répéter
M'= défiler()
Pour chaque pièce vi =< M' disponible dans S :
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
Sinon
Créer un nouveau noeud de valeur M' - vi dans A et lier ce noeud
à M' par un arc étiqueté par vi
Enfiler ce nouveau noeud
Fin Si
Jusqu'à (M' - vi = 0) ou (F = vide)
Si (F est vide Et M' - vi /= 0)
Alors il y a un problème dans les calculs ! STOP.
Sinon Le dernier noeud créer porte la valeur 0
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
au noeud v de valeur 0
$Q(S,M) = somme de i=1 a i=n de T_i$
Fin si
Fin Monnaie_graphe
```
%% Cell type:markdown id:d15c2fca-64f1-4d6d-b6bf-5edf9b095828 tags:
**Question 2.1 -** Ecrire un algorithme de construction de l'arbre.
%% Cell type:code id:2d2556ed-8be9-416f-a976-8964ccca8061 tags:
``` python
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é
en utilisant un ensemble donné de valeurs de pièces.
Args:
- S (list): Une liste d'entiers représentant les valeurs disponibles des pièces.
- M (int): Un entier spécifiant le montant cible à atteindre.
Returns:
- dict: Un graphe (représenté sous forme de dictionnaire)
"""
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:c2709c24-bbe0-4c6a-aaf8-f2e3712131f5 tags:
``` python
A = Monnaie_graphe([1, 2, 5], 7)
print("Arbre créé", A)
```
%% 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) :
%% Cell type:code id:701a7359-145f-4077-a8ee-e056eb3447f1 tags:
``` python
plot_graph(A)
```
%% 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.
%% Cell type:code id:f5f033d8-1d49-49c7-9af7-e1cd59905d96 tags:
``` python
def plus_court_chemin(graph: dict, start: str) -> int:
"""
Calcule les distances les plus courtes depuis un nœud de départ dans un graphe.
Args:
- graph (dict): Un dictionnaire représentant le graphe
- start (str): Le nœud de départ à partir duquel calculer les distances.
Returns:
- int: la taille du chemin
"""
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:7aca69d8-79ed-4f06-bb25-5c42b116cbc8 tags:
``` python
assert plus_court_chemin(A, 3)[0] == 2
```
%% 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é.
%% Cell type:code id:636ed054-38e5-46d1-b44f-c2d861bf1cc0 tags:
``` python
def plus_court_chemin_backtracking(graph: dict, start: str) -> list:
"""
Calcule les distances les plus courtes depuis un nœud de départ dans un graphe.
Args:
- graph (dict): Un dictionnaire représentant le graphe
- start (str): Le nœud de départ à partir duquel calculer les distances.
Returns:
- list: le chemin utilisé et les montants intermédiaires
"""
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:b0253f7c-0f07-436c-b72c-2411fb757e47 tags:
``` python
assert plus_court_chemin_backtracking(A, 7) == [7, 5, 0]
```
%% 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.
%% Cell type:markdown id:91af94f8-c687-4739-b4fb-ef58cb7e6081 tags:
## Utils
%% Cell type:code id:7cbf74a0-525c-4b6f-b078-5f0eea6fe09d tags:
``` python
from graphviz import Digraph
from IPython.display import display, Image
import graphviz
```
%% Cell type:code id:83f489e1-8fb7-45ec-a906-4d2699c63a46 tags:
``` python
def plot_graph(graph):
dot = graphviz.Digraph()
for node in graph:
dot.node(str(node))
for node, neighbors in graph.items():
for neighbor, weight in neighbors.items():
dot.edge(str(node), str(neighbor), label=str(weight))
display(dot)
```
TD07/monnaie-graph.png

69.5 KiB

TD07/monnaie-progdyn.png

67.4 KiB

File deleted
%% Cell type:markdown id:2c7e0986 tags:
NAME:
%% Cell type:markdown id:57c2dc5b-0ce7-47e4-bfeb-445c2f90cb6e tags:
# INF TC1 - TD7bis (2h + 2h AUTO) - Rendu du Rendu de monnaie
%% Cell type:markdown id:2a40aa1b-c3fe-4cd2-9236-3c7aeddedf55 tags:
---
%% Cell type:markdown id:25995ac6-8673-46f2-9c77-37b48a889b73 tags:
## Objectif du TD
Dans ce rendu de TD est d'aborder le problème de rendu de monnaie selon la méthode de **Programmation Dynamique**.
**Vos réponses à ce TD fera l'objet d'un rendu à [déposer sur Moodle](https://pedagogie1.ec-lyon.fr/course/view.php?id=1865).**
## Modalités de rendu du TD
Le rendu sera à déposer sur Moodle une (1) semaine après le dernier TD d'autonomie. Il pourra être réalisé seul ou en binôme. Ce rendu devra comporter :
- Les réponses aux questions de la partie 3 sous forme de notebook ou autre document.
- Un code fonctionnel et les tests appropriés (avec `assert` par exemple) sous forme de notebook ou de fichier Pythons classiques.
- De **nombreux** tests avec plusieurs systèmes de monnaies (canoniques et non canoniques) démontrant l'efficacité de votre approche. **Pensez également à inclure des tests démontrant les limites des solutions que vous proposez.** Pensez à rajouter les types de variables et commentaires dans les fonctions.
- La description de parties de code difficiles (dans le code ou dans des cellules supplémentaires).
- Tout soucis ou point bloquant dans votre code.
- Les diagrammes, exemples et illustration nécessaires (dans ce cas vous devrez créer une archive comprenant les fichiers de code, notebook et les fichiers d'illustration).
- Tout élément et exemple supplémentaire que vous jugerez nécessaires.
%% Cell type:markdown id:1fcb1349-4cae-4f76-b3b6-d9a810225afe tags:
NOM(s) et PRENOM(s) :
Groupe de TD :
%% Cell type:markdown id:c38c9de3-f716-4907-8387-7cf6f4f5e926 tags:
## Rappel: 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.
%% Cell type:markdown id:5a29e913-8954-4e88-b477-234c4ee0fd9b tags:
| | **la table S** | **la table D** |
|---------------|------------------------|----------------------------------|
| **indice $i$**| **Valeur ($v_i$)** | **Disponibilité ($d_i$)** |
| 1 | 1c | nombre de pièces de 1c disponibles |
| 2 | 2c | nombre de pièces de 2c disponibles |
| 3 | 5c | ... |
| 4 | 10c | ... |
| 5 | 20c | ... |
| 6 | 50c | ... |
| 7 | 100 (1€) | pièces de 1€ |
| 8 | 200 (2€) | pièces de 2€ |
| 9 | 500 (5€) | billets de 5€ |
| 10 | 1000 | billets de 10€ |
| 11 | 2000 | billets de 20€ |
| 12 | 5000 | billets de 50€ |
| 13 | 10000 | billets de 100€ |
%% 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 :
$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.
%% Cell type:markdown id:077ca702-bc18-4f2d-95e6-86fd4d53ebe7 tags:
## 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€) :
| 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$ |
| $\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$ 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$ 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 ! | | | |
<center>
<i>Table 1</i>
</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.
%% Cell type:markdown id:6c6aef6c-c6b7-4376-a0dd-06346df476df tags:
## 3. Algorithme de Programmation Dynamique (Rendu du TD)
Nous introduisons une troisième et dernière méthode de résolution qui se base sur la programmation dynamique dont les principes sont :
1) identifier une formule récursive pour résoudre le problème de façon incrémentale,
2) résoudre le problème pour des conditions aux bords,
3) itérer pour résoudre le problème complet.
Supposons que l'on puisse, pour le montant *M*, savoir calculer une solution optimale pour tout montant $M' < M$. Pour satisfaire *M*, il faudra alors prendre une (seule) pièce $v_i$ supplémentaire parmi les *n* pièces disponibles. Une fois cette pièce choisie, le reste $M' = M-v_i$ est forcément inférieur à *M* et on sait qu'on peut calculer un nombre optimal de pièces pour *M'*. Par conséquent :
%% Cell type:markdown id:53272a7f-ff93-4143-83cd-f33e1750b0b2 tags:
$Q_{opt}(i,m) = min
\begin{cases}
1 + Q_{opt}(i, m - v_i) \quad si \ (m - v_i) \geq 0 \qquad \text{ on utilise une pièce de type i de
valeur $v_i$}\\
Q_{opt}(i-1, m) \qquad \quad si \ i \geq 1 \qquad \qquad \quad \text{ on n'utilise pas la pièce de type i,
essayons i-1}
\end{cases}$
L'inconvénient de cette méthode est que chaque appel à $Q_{opt}$ fait deux appels à lui-même, donc le nombre d'opérations nécessaires est exponentiel à la taille de *M*. Pour éviter cela ferons appel au principe de *mémoïsation* de la programmation dynamique, en stockant les résultats intermédiaires dans une matrice $mat[|S|][M]$ (Figure 2).
<center>
<img src="monnaie-progdyn.png" alt="Image originale" style="height:5cm;">
</center>
<center>
<i>Figure 2</i>
</center>
Illustration de la résolution par programmation dynamique. L'ordre de remplissage des cellules (de haut en bas, de gauche à droite) est représenté en vert.
Les colonnes de la matrice sont les valeurs de M qu'on doit atteindre. Les lignes sont les pièces dont on dispose pour atteindre chaque valeur de M. En première ligne, on ne dispose d'aucune pièce, en ligne 2 on dispose d'une infinité de pièces de 1c, en ligne 3 on dispose d'une infinité de pièces de 1c et de 3c, etc. Les cellules de la matrice indiquent le nombre minimal de pièces à utiliser parmi celles autorisées par la ligne courante, pour atteindre la valeur en colonne. Ainsi on peut lire dans la matrice que pour payer 6 centimes avec des pièces de 1, 3 et 4 il faut utiliser 2 pièces (2 $\times$ 3c). Le remplissage de chaque cellule se fait en calculant le minimum de deux voisins (illustrés avec des flèches rouges). Si un voisin est hors de la matrice, il ne compte pas dans le calcul du minimum. Le pseudo-code de construction de la matrice peut s'écrire de la façon suivante :
```
fonction Progdyn(S, M) : S est le stock des pièces, M est le montant
soit mat la matrice d'indices [0, |S|] x [0 , M]
pour i = 0 à |S| faire
pour m = 0 à M faire
si m = 0 alors
mat[i][m] = 0
sinon si i = 0 alors
mat[i][m] = infini
sinon
mat[i][m] = min(
si m - S[i-1] >= 0 alors 1 + mat[i][m - S[i-1]] sinon infini
si i >= 1 alors mat[i-1][m] sinon infini
)
renvoyer mat [|S|][M]
```
%% Cell type:markdown id:1287c1b0-4900-4b18-ab59-5719f867e18d tags:
**Question 3.1 -** Proposer une solution Python de cette méthode. Dans une première étape, trouver simplement le nombre minimal de pièces. Renvoyer `None` si il n'existe pas de solution possible.
%% Cell type:code id:fc6d6123-9d62-4d58-a9c7-dd0540c77d9c tags:
``` python
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:90172789-470c-452d-96ff-8adc56d8c134 tags:
**Question 3.2 -** Modifier votre solution pour renvoyer également les pièces utilisées.
%% Cell type:code id:51354054-3292-48c6-8d43-8f42ffd11127 tags:
``` python
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:d544f60e-b75c-4b8f-a695-7b9ec7bd138b tags:
**Question 3.3 -** On souhaite à présent implémenter en Programmation Dynamique la limite du stock de pièces présentée pour les exercices 1.2 et 1.3. Pour ce faire on modifie la formule de $Q_{opt}$ donnée précédemment (valable ici uniquement pour $i\geq1$) :
$Q_{opt}(i,m) = min
\begin{cases}
Q_{opt}(i-1, m) \qquad \qquad \qquad \qquad \qquad \qquad \qquad \text{ on n'utilise aucune pièce de type i}\\
1 + Q_{opt}(i-1, m-v_i) \quad si\ d_i\geq1\ et\ (m-v_i) \geq 0 \qquad \text{ on utilise 1 pièce de type i}\\
2 + Q_{opt}(i-1, m-2v_i) \quad si\ d_i\geq2\ et\ (m-2v_i) \geq 0 \quad \text{ on utilise 2 pièces de type i}\\
3 + Q_{opt}(i-1, m-3v_i) \quad si\ d_i\geq3\ et\ (m-3v_i) \geq 0 \quad \text{ on utilise 3 pièces de type i}\\
...
\end{cases}
$
Implémentez la limite de pièces avec cette nouvelle formule pour l'algorithme en Programmation Dynamique. Notez ici qu'on ne va plus chercher les valeurs sur la même ligne dans la matrice, mais qu'on remonte _systématiquement_ d'une ligne, pour qu'après l'utilisation de $k$ pièces de valeur $v_i$, on ne puisse plus ajouter d'autres pièces de cette même valeur.
%% Cell type:code id:8c05a462-3d75-48be-903c-3fa922bbd63d tags:
``` python
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:5ee21bfb-c169-48d0-93cc-75c6be089d03 tags:
**Question 3.4 -** On dispose à présent du poids de chaque pièce en plus de sa valeur (Table 2), et on cherche à minimiser le poids total des pièces pour atteindre la valeur M. À l'aide d'une matrice analogue à la Figure 2, trouvez le poids minimal pour atteindre la valeur M=7. Notez qu'on ne cherchera plus à minimiser le nombre de pièces utilisées, seul le critère de poids compte pour cet exercice.
| S (valeur de chaque pièce) | P (poids de chaque pièce) |
|---------------------------|---------------------------|
| 1c | 2.30g |
| 2c | 3.06g |
| 5c | 3.92g |
| 10c | 4.10g |
| 20c | 5.74g |
| 50c | 7.80g |
| 100 (1€) | 7.50g |
| 200 (2€) | 8.50g |
| 500 (5€) | 0.6g |
| 1000 | 0.7g |
| 2000 | 0.8g |
| 5000 | 0.9g |
| 10000 | 1g |
<center>
<i>Table 2</i>
</center>
%% Cell type:code id:3ecdadb6-b735-47cd-ba9e-d79abb285648 tags:
``` python
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:b48169e9-ada6-4f03-a24c-28745caa99bd tags:
**Question 3.5 -** Implémentez à présent un algorithme de Programmation Dynamique qui calcule le poids minimal pour atteindre une valeur M. Étant donné qu'avec le système monétaire en Table 2 la minimisation du nombre de pièces minimise aussi le poids, vous pourrez tester votre algorithme avec un système plus complexe tel que donné en Table 3. Essayez par exemple avec la valeur $M=6$.
| S (valeur de chaque pièce) | P (poids de chaque pièce) |
|---------------------------|---------------------------|
| 1c | 10g |
| 3c | 27g |
| 4c | 32g |
| 7c | 55g |
<center>
<i>Table 3</i>
</center>
%% Cell type:code id:6e72a851-5698-4cd8-85ae-b830b8469ea9 tags:
``` python
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:d1970f87-775a-4374-9947-2ae1cff39418 tags:
**Question 3.6 -** On propose l'algorithme ci-dessous qui tente de résoudre le problème de façon gloutonne. Implémentez cet algorithme. Pour quelles valeurs de M ($\leq$ 20) avec la Table 3 donne-t-il une solution différente de l'algorithme à Programmation Dynamique ?
```
Fonction Poids_Gloutonne
Entrées : liste S, liste P, entier M
Sortie : poids minimal pour atteindre M
Soit L la liste des tuples (Pi/Si, Si, Pi), triée croissante selon le premier
élément de chaque tuple
M'=M
res = 0
Répéter
Chercher dans L le premier triplet (r, s, p) tel que s <= M'
res = res + p * (M' // s)
M' = M' % s
Jusqu'à M' = 0
res est la valeur de sortie de l'algorithme
Fin Poids_Gloutonne
```
%% Cell type:code id:b7f93bbc-c1d7-4de4-8470-961a68050d9e tags:
``` python
# YOUR CODE HERE
raise NotImplementedError()
```
TD07bis/monnaie-graph.png

69.5 KiB

TD07bis/monnaie-progdyn.png

67.4 KiB

File added
File added
File added
File added
File added
File added
%% Cell type:markdown id:82b57c5e tags:
%% Cell type:markdown id:e8a19c58 tags:
# INF TC1 - Algorithmes et structures de données
## Exercices
%% Cell type:markdown id:2968208d tags:
---
%% Cell type:markdown id:a4e4fad3 tags:
# Data Structures and Complexity
%% Cell type:markdown id:a8adef9b tags:
---
%% Cell type:markdown id:827ebb43-1e5d-4756-83ba-97af3e36b6af tags:
_For the following question, if a complexity is needed please pick one in this list_
%% Cell type:code id:b54157fc-f0d5-4689-bf2b-344a608bc5a9 tags:
``` python
list_complexities = ["O(1)", "O(log(n))", "O(n)", "O(n^2)", "O(nlog(n))", "O(n^3)", "O(2^n)", "O(n!)", "O(n^n)"]
```
%% Cell type:markdown id:568202fd tags:
### Exercise 1: Identify the complexity
%% Cell type:code id:431fe8c1-91d1-40f3-a7a4-f4a3770a4a01 tags:
``` python
def nested_loop_example(arr):
n = len(arr)
for i in range(n):
for j in range(n):
print(arr[i] + arr[j])
```
%% Cell type:code id:e68b3e9a-418f-4950-9f27-18bb0fe90794 tags:
``` python
# nested_loop_example_complexity = ?
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:612dc873-419b-42c5-be36-0accd03ffa79 tags:
### Exercise 2: Identify the complexity
%% Cell type:code id:76102853-e1f3-4717-8a59-1091195a19eb tags:
``` python
def fibonacci_recursive(n):
if n <= 1:
return n
return fibonacci_recursive(n - 1) + fibonacci_recursive(n - 2)
```
%% Cell type:code id:dead6a52-7996-4eae-9d2a-f75d5d26bbb7 tags:
``` python
# fibonacci_recursive_complexity = ?
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:aa4a6ce7-e542-4b23-8a10-ca0bf93de041 tags:
### Exercise 3: Identify the complexity
%% Cell type:code id:70af3e43-8053-49c9-ba58-346a3e915bdb tags:
``` python
def binary_search(arr, target):
low, high = 0, len(arr) - 1
while low <= high:
mid = (low + high) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
low = mid + 1
else:
high = mid - 1
return -1
```
%% Cell type:code id:9c22866c-b4fc-4228-b0ab-5882d964f5f6 tags:
``` python
# binary_searche_complexity = ?
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:87b4921b-ef55-4083-b4f1-a3ca5bb7b011 tags:
### Additional checks (do not change)
%% Cell type:code id:6e8f2878-ce5f-4cd8-a5a5-7bce8f655ab8 tags:
``` python
assert nested_loop_example_complexity in list_complexities
assert fibonacci_recursive_complexity in list_complexities
assert binary_searche_complexity in list_complexities
```
%% Cell type:markdown id:552c2490 tags:
%% Cell type:markdown id:4aaf8acc tags:
# INF TC1 - Algorithmes et structures de données
## Exercices
%% Cell type:markdown id:6a4b8ca4 tags:
---
%% Cell type:markdown id:a4e4fad3 tags:
# Recursion
%% Cell type:markdown id:a8adef9b tags:
---
%% Cell type:code id:0dfe1da3-b50b-49c0-aaff-483616e13863 tags:
``` python
list_complexities = ["O(1)", "O(log(n))", "O(n)", "O(n^2)", "O(nlog(n))", "O(n^3)", "O(2^n)", "O(n!)", "O(n^n)"]
```
%% Cell type:markdown id:568202fd tags:
### Exercise 0: Find the maximum value in a list (iterative)
Write a function `find_maximum_iterative` that takes a list of numbers as input and returns the maximum value in the list. For this question, you are not allowed to use built-in functions like `max()`.
%% Cell type:code id:431fe8c1-91d1-40f3-a7a4-f4a3770a4a01 tags:
``` python
def find_maximum_iterative(L):
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:f6baae3c-3660-4add-ab4b-48016cba3030 tags:
``` python
find_maximum_iterative([1, 3, 5, 7, 9])
```
%% Cell type:code id:e68b3e9a-418f-4950-9f27-18bb0fe90794 tags:
``` python
assert find_maximum_iterative([1, 3, 5, 7, 9]) == 9
assert find_maximum_iterative([-1, -5, -3]) == -1
assert find_maximum_iterative([42]) == 42
assert find_maximum_iterative([4, 8, 8, 2, 7]) == 8
assert find_maximum_iterative([-10, -5, -8, -2, -7]) == -2
```
%% Cell type:markdown id:612dc873-419b-42c5-be36-0accd03ffa79 tags:
### Exercise 1: Find the maximum value in a list (recursive)
Write a recursive version of the previous function; you may use the `max()` function for the recursive call.
%% Cell type:code id:07668fd8 tags:
``` python
def find_maximum_recursive(L):
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:f9784710-4b2b-434c-bc47-da46fa410749 tags:
``` python
find_maximum_recursive([1, 3, 5, 7, 9])
```
%% Cell type:code id:9b0161f8-0539-4e5e-921c-1886e61c0783 tags:
``` python
assert find_maximum_iterative([-10, -5, -8, -2, -7]) == find_maximum_recursive([-10, -5, -8, -2, -7])
```
%% Cell type:markdown id:005efd41-baa1-4505-b80e-3644d61ea094 tags:
### Exercise 2: Sum of digits
%% Cell type:code id:de424156-0b9b-41d0-8e38-ce335f35cec0 tags:
``` python
def sum_of_digits(n):
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:cec0caca-cb2c-4e4d-b004-27b3cf2ff611 tags:
``` python
sum_of_digits(10)
```
%% Cell type:code id:bf276ca2-48ed-4e87-80f2-776f54b7062b tags:
``` python
assert sum_of_digits(10) == sum_of_digits(100000)
assert sum_of_digits(0) == 0
assert sum_of_digits(123) == 6
```
%% Cell type:markdown id:e2de630a-f9bd-4d45-959b-430e34cc9044 tags:
### Exercise 3: Calculate the power
%% Cell type:code id:abca03a0-7bcd-4ee6-b109-ff2f2da52bb6 tags:
``` python
def power(base, exponent):
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:abddd3b1-f75f-4cb6-a09e-54eed489c5b0 tags:
``` python
power(2, 10)
```
%% Cell type:code id:8a6605fe-4f6f-45de-84a3-7601e2e2e6f6 tags:
``` python
assert power(2, 10) == 1024
assert power(2, 0) == 1
assert power(5, 3) == 125
```
%% Cell type:markdown id:715100d3-fc21-49b9-a66d-b5243b4a279d tags:
### Exercise 4: Reverse a string
%% Cell type:code id:ddc9826a-0863-4777-a08d-81b66652b5a5 tags:
``` python
def reverse_string(s):
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:13acad7e-d03c-4ea6-ad86-baf02e0910eb tags:
``` python
reverse_string("Romain")
```
%% Cell type:code id:453c8e04-6cd9-4581-a206-dd479b6115cd tags:
``` python
assert reverse_string("") == ""
assert reverse_string("AA") == "AA"
assert reverse_string("ABC") == "CBA"
```
%% Cell type:code id:159b6d78-03ae-4cf8-8545-e822b7160b32 tags:
``` python
def iterative_reverse_string(s):
reversed_str = ""
for char in s:
reversed_str = char + reversed_str
return reversed_str
```
%% Cell type:code id:43e10b0c tags:
``` python
```
%% Cell type:markdown id:5ab8f3c9-ddee-45ab-a013-fdd67d9853e0 tags:
### Example 5: convert an interative suite into a recursive tail function
%% Cell type:code id:219add7f tags:
``` python
def sequence(n):
u = 1
while n > 0:
u = 2 * u + 1
n -= 1
return u
```
%% Cell type:code id:0787df24-8234-4286-b910-5f9e456dd279 tags:
``` python
print("The result is {}".format(sequence(3)))
```
%% Cell type:code id:9c17cf1b-6d05-4589-af2b-c05cfcc33202 tags:
``` python
def sequence_recursive_tail(n):
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:43507c43-de61-414d-b389-c0a771ad9e0c tags:
``` python
print("The result is still {}".format(sequence_recursive_tail(3)))
```
%% Cell type:code id:dd923b7c-0cab-4678-8dc3-aad2ab9b25f9 tags:
``` python
assert sequence_recursive_tail(3) == 15
```
%% Cell type:markdown id:3c28b36a tags:
### Example 6: check if a word is a palindrom
Check if a word can be read backwards.
%% Cell type:code id:51bb3d08 tags:
``` python
def palindrom_rec(word):
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:0c279628-9b96-4687-8e20-a954ab646e0f tags:
``` python
palindrom_rec("laval")
```
%% Cell type:code id:cf6fa61a-7c6f-4a32-96c2-b9fd50deacc6 tags:
``` python
assert palindrom_rec("")
assert palindrom_rec("AA")
assert not palindrom_rec("ABC")
assert palindrom_rec("ABA")
assert palindrom_rec("LAVAL")
```
%% Cell type:markdown id:798c2875-7940-488a-8458-ad08f6a71c70 tags:
### Example 7: Calculate GCD
%% Cell type:code id:48c65c83-be0c-41c4-8c04-1d29ac4415cb tags:
``` python
def iterative_gcd(a, b):
while b:
a, b = b, a % b
return a
```
%% Cell type:code id:bf6e1a76 tags:
``` python
def recursive_gcd(a, b):
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:4f1feace tags:
``` python
recursive_gcd(10, 2)
```
%% Cell type:code id:e6ff1765-ff4b-49a5-80d3-684d2627e961 tags:
``` python
assert iterative_gcd(10, 2) == recursive_gcd(10, 2)
```
%% Cell type:markdown id:d8b05edf-9536-4fc1-bfcc-ddbbeee426fc tags:
### Example 8: Check if a list is sorted
%% Cell type:code id:1661dfb5-88f2-411f-8fe2-63bbaa29ce72 tags:
``` python
def is_sorted(lst):
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:0c5c72c6-3237-4f98-ab96-50a1838b833f tags:
``` python
is_sorted([1, 2, 3, 4, 5])
```
%% Cell type:code id:9f18b6b7-d980-4a72-a2a8-3aa201003d21 tags:
``` python
assert is_sorted([2, 3])
```
%% Cell type:markdown id:79eef392-1d3d-46c0-aeee-ac805686e6f1 tags:
### Example 9: Check for prime number
%% Cell type:code id:ac374a08-11c9-47e6-ba11-419549911266 tags:
``` python
def is_prime_recursive(n, divisor=2):
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:996fc91f tags:
``` python
is_prime_recursive(3)
```
%% Cell type:code id:5be1051c-3b60-4810-b855-6f8575ad6380 tags:
``` python
assert is_prime_recursive(3)
```
%% Cell type:markdown id:5423682d-c31f-4a8d-9a0f-6812de7b1ae3 tags:
### Example 10: Count occurrences of a given element in a list
%% Cell type:code id:cfeb0ad0-ed82-499e-9af3-64923291a0e7 tags:
``` python
def count_occurrences(arr, target, index=0):
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:49daec07-00b3-412e-ad44-5bafadbd9f36 tags:
``` python
count_occurrences([1, 2, 3, 4, 2, 2, 5, 6, 2], 2)
```
%% Cell type:code id:14a2eb85-1126-4506-93bb-4bf624d046b6 tags:
``` python
assert count_occurrences([1, 2, 3, 4, 2, 2, 5, 6, 2], 2) == 4
```
%% Cell type:markdown id:bb1784f5-2426-4661-aa13-dba96743ceb5 tags:
# Bonus
%% Cell type:markdown id:7fdcd705-dc0b-4614-8e27-9ba62f8fe442 tags:
### Exercise: Find the maximum value in a list (iterative)
Check that an empty lists raises a `ValueError` exception with a `"The list is empty."` message.
%% Cell type:code id:f9334f32-f760-46c0-a649-c82f267aba5b tags:
``` python
try:
result = find_maximum_iterative([])
assert False, "Exception not raised"
except ValueError as e:
assert str(e) == "The list is empty.", f"Expected error message: 'The list is empty.' but got '{str(e)}'"
```
%% Cell type:markdown id:c121315a-4aaa-4e04-912f-73f56555be56 tags:
### Exercise: Find the maximum value in a list (recursive)
Witout using the built-in `max` function
%% Cell type:code id:153e3f05-7598-4920-bc8d-8943cb75e5a8 tags:
``` python
# checks if a function uses another function, eg "max"
import inspect
def calls_builtin_max(func):
source_code = inspect.getsource(func)
return 'max(' in source_code
def my_function(lst):
return max(lst)
```
%% Cell type:code id:775b7e32-a5ae-431b-9987-fcd3671fd022 tags:
``` python
def find_maximum_recursive_no_max_func(L):
if not L:
raise ValueError("The list is empty.")
if len(L) == 1:
return L[0]
rest_max = find_maximum_recursive(L[1:])
return L[0] if L[0] > rest_max else rest_max
```
%% Cell type:code id:f76ed657-3288-4b36-9175-21168d2eb761 tags:
``` python
assert not calls_builtin_max(find_maximum_recursive_no_max_func)
```
%% Cell type:markdown id:bbe7023a-aabd-489d-bae6-3b71566e22ef tags:
# Additional tests (do not change)
%% Cell type:code id:1c73ad0e-ddc9-483d-85a1-f2a65d04f30b tags:
``` python
assert calls_builtin_max(my_function)
assert not calls_builtin_max(find_maximum_iterative)
```
%% Cell type:code id:8ce960f5-08da-449e-9e6f-dae215bc1b6a tags:
``` python
# generates more examples for a given function
def gen_examples(fnct, n=10):
return [fnct(i) for i in range(0, n)]
```
%% Cell type:markdown id:8c52d3d4 tags:
%% Cell type:markdown id:74aa4459 tags:
# INF TC1 - Algorithmes et structures de données
## Exercices
%% Cell type:markdown id:d9a28297 tags:
---
%% Cell type:markdown id:e58599e3-9ab7-4d43-bb22-aeccade424ce tags:
# Lists, search, sort
%% Cell type:markdown id:691b3c38-0e83-4bb2-ac90-ef76d2dd9a7a tags:
---
%% Cell type:code id:1095285f-26e2-43ce-a1c5-9358c5256a0b tags:
``` python
list_complexities = ["O(1)", "O(log(n))", "O(n)", "O(n^2)", "O(nlog(n))", "O(n^3)", "O(2^n)", "O(n!)", "O(n^n)"]
```
%% Cell type:markdown id:11a124a7-1279-469a-b091-2833d3fd1a0f tags:
## Exercise 0: Search the index first occurence of a target value
_Write a Python function `search_list(L, v)` that takes a list `L` and a target element `v` as input. The function should return the index of the first occurrence of the target element in the list `L`._
%% Cell type:code id:665c7b64-428d-468a-860b-65d0d12e98e1 tags:
``` python
def search_list(L, target):
for n, i in enumerate(L):
if i == target:
return n
return -1
```
%% Cell type:code id:d776ca94-1ed2-4443-91e2-a1591a1690b8 tags:
``` python
search_list([1, 2, 2], 2)
```
%% Cell type:code id:e7b5950c-a6f0-4795-995b-903d717f35c9 tags:
``` python
assert search_list([1, 2, 3, 4, 5], 3) == 2
assert search_list([1, 2, 3, 4, 5], 6) == -1
assert search_list([1, 2, 3, 4, 5], 6) == -1
assert search_list([], 42) == -1
assert search_list([7, 2, 3, 4, 5], 7) == 0
assert search_list([1, 2, 3, 4, 5, 6], 7) == -1
```
%% Cell type:markdown id:9d813205-b709-42ab-b414-6f3fc947022a tags:
## Exercise 1: Search in a list with an odd index
_Write a Python function `search_list(L, v)` that takes a list `L` and a target element `v` as input. The function should return the index of the first occurrence of the target element in the list `L`._
%% Cell type:code id:f3b233ec-7077-479d-9c04-f1a4c35f3111 tags:
``` python
def search_list(L, target):
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:f280eeea-4812-4a30-80b5-3fe1cafa9283 tags:
``` python
search_list([1, 2, 2], 3)
```
%% Cell type:code id:423d2637-8bd6-4e8f-95ff-2765dae5bce7 tags:
``` python
assert search_list([1, 2, 3, 4, 5], 3) == 2
assert search_list([1, 2, 3, 4, 5], 6) == -1
assert search_list([1, 2, 3, 4, 5, 6], 6) == -1
assert search_list([], 42) == -1
assert search_list([7, 2, 3, 4, 5, 7], 7) == 0
assert search_list([1, 2, 3, 4, 5, 6], 6) == -1
```
%% Cell type:markdown id:6d8dc6cd-aad0-42a9-b768-6a1a5289e354 tags:
## Exercise 2: Sort a list of tuples
_Given a list of lists of length N, sort by the N-th element of the list._
%% Cell type:code id:8271ff47-efb4-48a0-ac4c-bba6ede7e578 tags:
``` python
def sort_list_of_lists_by_nth_element(list_of_lists, N):
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:4577bd24-9e50-4172-8246-d1bccfa21618 tags:
``` python
list_of_lists = [[3, 5, 1], [1, 2, 9], [7, 4, 6], [2, 8, 3]]
sorted_list = sort_list_of_lists_by_nth_element(list_of_lists, 2)
print(sorted_list)
```
%% Cell type:code id:ed3cdeed-07fa-461f-b7ae-210e1605ca76 tags:
``` python
list1 = [[3, 5, 1], [1, 2, 9], [7, 4, 6], [2, 8, 3]]
sorted_list1 = sort_list_of_lists_by_nth_element(list1, 2)
assert sorted_list1 == [[3, 5, 1], [2, 8, 3], [7, 4, 6], [1, 2, 9]], "Test Case 1 Failed"
list2 = [[9, 5, 7], [3, 6, 1], [0, 2, 4], [8, 1, 5]]
sorted_list2 = sort_list_of_lists_by_nth_element(list2, 0)
assert sorted_list2 == [[0, 2, 4], [3, 6, 1], [8, 1, 5], [9, 5, 7]], "Test Case 2 Failed"
list3 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
sorted_list3 = sort_list_of_lists_by_nth_element(list3, 1)
assert sorted_list3 == [[1, 2, 3], [4, 5, 6], [7, 8, 9]], "Test Case 3 Failed"
```
%% Cell type:markdown id:0bbda1ba-a5e7-4a1d-a742-84d0215b1e24 tags:
## Exercise 3: Access a list element
_Given an input list `L`, and `index` value, access a given `key` and return the value._
%% Cell type:code id:2d6a6f11-d24b-4f0e-a7d2-298243e35c4d tags:
``` python
def access_dict_element(L, index, key):
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:ea0883dc-d08c-40ea-9e0b-4f0a3abc517f tags:
``` python
example_list = [{'name': 'Alice', 'age': 30}, {'name': 'Bob', 'age': 25}]
result = access_dict_element(example_list, 0, 'name')
print(result)
```
%% Cell type:code id:88ae441e-58b1-4d6c-a639-530919658d03 tags:
``` python
example_list = [{'name': 'Alice', 'age': 30}, {'name': 'Bob', 'age': 25}]
assert access_dict_element(example_list, 0, 'name') == 'Alice'
assert access_dict_element(example_list, 1, 'city') is None
assert access_dict_element(example_list, 2, 'name') is None
assert access_dict_element(example_list, 0, 'city') is None
```
%% Cell type:markdown id:98405b12-ad61-4007-8c9f-6955d238df41 tags:
## Exercise 4: Remove Elements from a List
_Write a Python function `remove_elements(L, condition)` that takes a list `L` and a function `condition` as input._
%% Cell type:code id:e079e47b-2f9e-42d1-a78b-56c92ad84d63 tags:
``` python
def is_odd(x):
return x % 2 != 0
```
%% Cell type:code id:536734bd-d4cc-4f83-8042-3c0e774c4034 tags:
``` python
def remove_elements(lst, condition):
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:a65bb6f1-b7c7-4156-ba8b-9b72a25616f3 tags:
``` python
my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
remove_elements(my_list, is_odd)
print(my_list) # Should print [2, 4, 6, 8]
```
%% Cell type:code id:254155dc-b271-4881-ac65-5a11d13990ea tags:
``` python
test_list_1 = [12, 5, 18, 9, 25, 3, 15]
remove_elements(test_list_1, lambda x: x > 10)
assert test_list_1 == [5, 9, 3], "Remove greater than 30"
test_list_2 = [-3, 7, -9, 2, 0, -1, 8]
remove_elements(test_list_2, lambda x: x < 0)
assert test_list_2 == [7, 2, 0, 8], "Remove negative elements"
def custom_condition(x):
return x % 3 == 0
test_list_3 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
remove_elements(test_list_3, custom_condition)
assert test_list_3 == [1, 2, 4, 5, 7, 8]
test_list_4 = [42, 99, 101]
remove_elements(test_list_4, lambda x: True)
assert test_list_4 == [], "Remove all elements"
test_list_5 = [10, 20, 30, 40]
remove_elements(test_list_5, lambda x: False)
assert test_list_5 == [10, 20, 30, 40], "No element to remove"
```
%% Cell type:markdown id:d75dba4b-ac22-4f2c-9a6c-cf333b1ec5e8 tags:
## Exercise 5: Sort a dictionnary by values
List a dictionnary (not a list!) by value.
%% Cell type:code id:556be9d8-b8c3-4f1d-bcb1-8f099968af9d tags:
``` python
def sort_dict_by_values(input_dict):
# YOUR CODE HERE
raise NotImplementedError()
return sorted_dict
```
%% Cell type:code id:3c32c4ee-56f5-46b5-a8b6-b740e6d5fef5 tags:
``` python
test_dict3 = {'c': 3, 'b': 2, 'a': 1}
sorted_dict3 = sort_dict_by_values(test_dict3)
assert sorted_dict3 == {'a': 1, 'b': 2, 'c': 3}
```
%% Cell type:code id:532e5225-a2d8-4c10-a036-1efde9acc5fd tags:
``` python
test_dict = {'banana': 3, 'apple': 1, 'cherry': 2}
sorted_dict = sort_dict_by_values(test_dict)
assert sorted_dict == {'apple': 1, 'cherry': 2, 'banana': 3}
test_dict2 = {'zebra': 42, 'lion': 7, 'elephant': 15, 'giraffe': 23}
sorted_dict2 = sort_dict_by_values(test_dict2)
assert sorted_dict2 == {'lion': 7, 'elephant': 15, 'giraffe': 23, 'zebra': 42}
test_dict3 = {'one': 3, 'two': 3, 'three': 3, 'four': 3}
sorted_dict3 = sort_dict_by_values(test_dict3)
assert sorted_dict3 == {'one': 3, 'two': 3, 'three': 3, 'four': 3}
```
%% Cell type:markdown id:fef2ad7f-55ef-43f2-991d-5067bb085eb6 tags:
## Exercise 6: insertion sort
Implement the `insertion sort` that operates as follows:
- Start with an unsorted list.
- Iterate through each element in the list.
- For each element, compare it with the elements to its left in the list.
- Move elements to the right until you find the correct position for the current element.
- Insert the current element into its proper place in the sorted portion of the list.
- Repeat this process for all elements, gradually building a sorted list from left to right.
%% Cell type:code id:51a86852-f8e2-4c05-8053-13fdf2a4832b tags:
``` python
def insertion_sort(arr):
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:fbfa74f1-4675-452f-897b-21b0c7025f81 tags:
``` python
print(insertion_sort([2, 1, 3, 4, 5]))
```
%% Cell type:code id:266e89e0-4b59-441f-bb67-69ad4e35e2a9 tags:
``` python
# Test case 2: Already sorted list
arr = [1, 2, 3, 4, 5]
assert insertion_sort(arr) == arr
# Test case 3: Reverse sorted list
arr = [5, 4, 3, 2, 1]
assert insertion_sort(arr) == [1, 2, 3, 4, 5]
# Test case 4: Random order list
arr = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
assert insertion_sort(arr) == [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]
```
%% Cell type:markdown id:ab78dd44-bb6a-451d-8642-223639d31bf9 tags:
## Bonus
You may get bonus points for the following answers:
- add exceptions https://docs.python.org/3/library/exceptions.html
- add more test cases
%% Cell type:markdown id:8fdc643f tags:
%% Cell type:markdown id:ed639e9f tags:
# INF TC1 - Algorithmes et structures de données
## Exercices
%% Cell type:markdown id:fca7f37a tags:
---
%% Cell type:markdown id:2f1f2dcd-96a9-45ef-90a6-4ad488635679 tags:
# Stacks and queues
%% Cell type:markdown id:b9bd540c-dd15-49ac-bfbd-f2e758688a85 tags:
---
%% Cell type:markdown id:03a0653e-65c2-4e79-9e83-31765cf19098 tags:
## Exercise 1: Reverse a string using a Stack
_Use the `Stack` below to reverse a string given as input._
%% Cell type:code id:4932473d-2734-4e81-b777-ca10decfd9e8 tags:
``` python
class Stack:
def __init__(self):
self.items = []
def push(self, item):
self.items.append(item)
def pop(self):
if not self.is_empty():
return self.items.pop()
def peek(self):
if not self.is_empty():
return self.items[-1]
def is_empty(self):
return len(self.items) == 0
```
%% Cell type:code id:8b77ae34-ef7c-4664-94e0-8928156f2224 tags:
``` python
def reverse_string(s):
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:63719c8e-f60c-4544-8e41-cb6380ae4bcf tags:
``` python
reverse_string("Hello")
```
%% Cell type:code id:81e93620-0664-4a9d-ba5f-894937c9769e tags:
``` python
assert reverse_string("Hello") == "olleH"
```
%% Cell type:markdown id:81df9b1e-cfe5-4b69-96a5-c8065259cc7d tags:
## Exercise 2: Check if a word is a palindrom (using a Stack)
_A palindrome is a sequence of characters that reads the same forward and backward._
%% Cell type:code id:cf6fbdd5-53c5-45c2-a0c5-a5ed845c4f81 tags:
``` python
def is_palindrome(s):
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:586bafba-2fbb-4833-b2e3-609db9b28fbf tags:
``` python
is_palindrome("ABA")
```
%% Cell type:code id:d0005a10-9152-4aa5-a94b-fcbff1bd2281 tags:
``` python
assert is_palindrome("ABA")
```
%% Cell type:markdown id:f767bf25-9f4f-4a0d-8cb9-b729bbec5c27 tags:
## Exercise 3: Implement a min-heap
Use a `PriorityQueue` to return the smallest element when using `pop` of a stack (or a queue).
%% Cell type:code id:ddcccaf6-d235-4327-826f-7a62a4c23f28 tags:
``` python
from queue import PriorityQueue
```
%% Cell type:code id:2da2db1e-f55d-43b4-877f-96ef944818e8 tags:
``` python
# how to use the modue
priority_queue = PriorityQueue()
priority_queue.put((3, 'apple'))
priority_queue.put((1, 'banana'))
priority_queue.put((2, 'cherry'))
element = priority_queue.get()
print(element)
```
%% Cell type:code id:804ea32d-5bf8-42b9-ae52-6318b26f4065 tags:
``` python
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:1b2d28c4-277b-44fa-b7e8-590aa00f8f70 tags:
``` python
min_heap = MinHeap()
min_heap.insert(5)
min_heap.insert(3)
min_heap.insert(8)
```
%% Cell type:code id:ed61bced-f000-41c6-8ecd-d669b4edb700 tags:
``` python
assert min_heap.pop() == 3
assert min_heap.peek() == 5
assert min_heap.peek() == 5
```
%% Cell type:markdown id:a445d290-b04f-49b5-a8e7-2c6e259daf58 tags:
## Exercise 4: Evaluate a postfix expression
_Write a code that given the following expression, provides the following evaluation (using arthmetic operations over numerical values)._
Expression: `"3 4 +"`
Evaluation: `3 + 4 = 7`
First step: write a function `apply_operator` that applies an operation (ie + - * /) over two elements.
%% Cell type:code id:4cc7f805-0887-4422-b6b7-3d591d0df1fb tags:
``` python
def apply_operator(op, b, a):
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:e68bdf7c-ca08-4553-9874-8bd9038fd4b5 tags:
Solution in pseudo-code:
- Split the input expression in to a list of tokens
- If not an operator
- Add the value to the stack
- If an operator
- Make sure there is enough parameters `a` and `b`
- Pop `a` and `b`
- Apply `apply_operator` on `a` and `b`
- Store the result in the stack
%% Cell type:code id:e792c90d-1b38-47f5-9879-399debc934b9 tags:
``` python
def evaluate_postfix(expression):
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:ea6e4840-1b7e-4265-b37d-e8c45ea6b3ed tags:
``` python
postfix_expression = "3 4 + 2 *"
result = evaluate_postfix(postfix_expression)
print("Result:", result)
```
%% Cell type:code id:0dc4dff8-089b-46a6-a08d-f53ee2fe72c3 tags:
``` python
assert evaluate_postfix("3 4 + 2 *") == 14
assert evaluate_postfix("4 2 3 5 * + *") == 68 # (4 * (2 + (3 * 5))
assert evaluate_postfix("8 4 / 6 2 * +") == 14 # ((8 / 4) + (6 * 2))
```
File added
File added
File added