Skip to content
Snippets Groups Projects
Commit 9b3e8aaa authored by Mathieu Schiess (EDU-GE)'s avatar Mathieu Schiess (EDU-GE)
Browse files

ajout d'un 3eme notebook sur la recursivite

parent 360ab802
No related branches found
No related tags found
No related merge requests found
%% Cell type:markdown id: tags:
<h1>Récursivité</h1>
<ul>
<h2> Compléments</h2>
<h3>La fonction <em>somme(n)</em> du cours</h3>
</ul>
%% Cell type:markdown id: tags:
Une fois que l’on dispose d’une définition récursive pour une fonction, il est en général assez facile de la programmer en Python.<br/>
Il faut néanmoins faire attention à **deux points importants** :
* Le domaine mathématique d’une fonction, c’est-à dire les valeurs sur lesquelles elle est définie, n’**est pas** toujours le même que l’**ensemble des valeurs du type Python** avec lesquelles elle sera appelée.
* Le choix d’une **définition récursive plutôt qu’une autre peut dépendre du modèle d’exécution des fonctions récursives**, en particulier quand il s’agit de prendre en compte des contraintes d’efficacité.
<h4>Exemple</h4>
Le code de la fonction <code> somme(n)</code> rappelé ci-dessous,ne se **comporte pas** exactement comme la fonction mathématique.
<ul>
%% Cell type:code id: tags:
``` python
def somme(n):
if n == 0:
return 0
else:
return n + somme(n - 1)
```
%% Cell type:code id: tags:
``` python
somme(5)
```
%% Output
%% Cell type:markdown id: tags:
La principale différence est que la **fonction mathématique est uniquement définie pour des entiers naturels**,
alors que la fonction <code>somme(n)</code> peut être appelée avec un **entier Python arbitraire**, qui peut être une **valeur négative**.
Bien que la fonction mathématique ne soit pas définie pour
**n = −1**, l’appel <code>somme(-1)</code> ne provoque **aucune erreur immédiate**,
mais il implique un appel à <code>somme(-2)</code>, qui déclenche un appel à <code>somme(-3)</code>, etc.
Ce processus **infini** va finir par provoquer une **erreur à l’exécution**.
</ul>
%% Cell type:code id: tags:
``` python
somme(-1)
```
%% Cell type:markdown id: tags:
Pour **éviter ce comportement**, c'est de restreindre les appels à la fonction <code>somme(n)</code> aux **entiers positifs ou nuls**.
Pour cela, on peut utiliser une instruction **assert** comme ci-dessous :
%% Cell type:code id: tags:
``` python
def somme(n):
assert n >= 0, "entrer un entier naturel"
if n == 0:
return 0
else:
return n + somme(n - 1)
```
%% Cell type:code id: tags:
``` python
somme(5)
```
%% Output
%% Cell type:code id: tags:
``` python
somme(-1)
```
%% Output
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-6-2263d90c8da2> in <module>
----> 1 somme(-1)
<ipython-input-4-b514195a3042> in somme(n)
1 def somme(n):
----> 2 assert n >= 0, "entrer un entier naturel"
3 if n == 0:
4 return 0
5 else:
AssertionError: entrer un entier naturel
%% Cell type:markdown id: tags:
Bien que cette solution soit correcte, elle n’est pas encore **complètement satisfaisante**.
En effet, pour tout appel <code>somme(n)</code> avec **n >= 0**, chaque appel récursif **commencera par faire le test associé à l’instruction assert**,
alors que **chaque valeur** de n, par la suite, sera **nécessairement positive**.
Une solution pour éviter ces tests inutiles est de définir **deux fonctions** :
<ul>
La première, <code>somme_bis(n)</code>, implémente la définition récursive de la fonction
mathématique <code>somme(n)</code> sans **vérifier son argument**.
</ul>
%% Cell type:code id: tags:
``` python
def somme_bis(n):
if n == 0:
return 0
else:
return n + somme_bis(n - 1)
```
%% Cell type:markdown id: tags:
La seconde, <code>somme(n)</code>, est la fonction « *principale* » qui sera appelée par l’utilisateur.
Cette fonction ne fait que vérifier (**une et une seule fois**) que
son argument **n est positif** puis, si c’est le cas, elle appelle la fonction
<code>somme_bis(n)</code>.
%% Cell type:code id: tags:
``` python
def somme(n):
assert n >= 0, "entrer un entier naturel"
return somme_bis(n)
```
%% Cell type:code id: tags:
``` python
somme(5)
```
%% Output
%% Cell type:code id: tags:
``` python
somme(-1)
```
%% Output
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-10-2263d90c8da2> in <module>
----> 1 somme(-1)
<ipython-input-8-4a3635feae60> in somme(n)
1 def somme(n):
----> 2 assert n >= 0, "entrer un entier naturel"
3 return somme_bis(n)
AssertionError: entrer un entier naturel
%% Cell type:markdown id: tags:
<h4>Conclusion</h4>
<br/>
<div style="background-color:#DDD5ED;">
Un calcul peut être décrit à l’aide d’une <font color='red'> <strong>définition récursive</strong></font>.
L’avantage de cette technique est que l’implémentation est souvent plus proche de la définition.
L’écriture d’une <font color='red'> **fonction récursive** </font>nécessite de distinguer les <font color='red'> **cas de base**</font>, pour lesquels on peut donner un résultat facilement, et les<font color='red'> **cas récursifs**</font>, qui font appel à la définition en cours.
Il faut faire attention à ce que la fonction en Python ne s’applique que sur le<font color='red'> **domaine**</font> de la fonction mathématique, par exemple en utilisant l’instruction<font color='red'> **assert**</font>.
Enfin, il faut comprendre le modèle d’exécution des fonctions récursives pour choisir la définition qui<font color='red'> **limite le nombre d’appels récursifs**</font>.
(Voir les ***exercices 8 et 9*** sur les puissances)
</div>
%% Cell type:markdown id: tags:
<ul><h2> Fiche 2 </h2>
<h3> Exercice 1</h3>
Fonction **Factorielle**
L'un des exemples les plus classiques est une fonction mathématique, la fonction factorielle.
On dit « factorielle n » et on note avec un point d'exclamation n!.
Cette fonction est définie « par récurrence » sur les entiers naturels : 0! = 1 et pour tout entier naturel n non nul,
n! = n × (n – 1)!.
Autrement dit, pour tout n non nul, n! = n × (n — 1) × (n — 2) × … × 2 × 1.
En utilisant le langage **Python**
1) Ecrire une fonction itérative calculant factorielle n, ayant pour paramètre un entier positif, et retourne le calcul.
a) En utilisant le boucle « for »
%% Cell type:code id: tags:
``` python
def fact_for(n):
"""
Calcule la factorielle de n
paramètre:
n : (int) entier >= 0
retour:
un entier positif
"""
...
```
%% Cell type:code id: tags:
``` python
fact_for(5)
```
%% Cell type:markdown id: tags:
b) En utilisant le boucle « while »
%% Cell type:code id: tags:
``` python
def fact_while(n):
"""
Calcule la factorielle de n
paramètre:
n : (int) entier >= 0
retour:
un entier positif
"""
...
```
%% Cell type:code id: tags:
``` python
fact_while(5)
```
%% Cell type:markdown id: tags:
2) Fonction récursive
%% Cell type:code id: tags:
``` python
def fact_rec(n):
"""
Calcule la factorielle de n
paramètre:
n : (int) entier >= 0
retour:
un entier positif
"""
if n == 0:
return 1
else:
return n * fact_rec(n - 1)
```
%% Cell type:code id: tags:
``` python
fact_rec(5)
```
%% Cell type:markdown id: tags:
<h3> Exercice 2</h3>
**Calcul de pgcd de deux nombres entiers**
Écrire en Python une fonction récursive pgcd (a , b) renvoyant le plus grand diviseur commun de deux
nombres a et b.
Pour cela, on utilisera le résultat mathématique suivant : « pgcd(a , b) = pgcd(b , r), où a = bq + r »
%% Cell type:code id: tags:
``` python
def pgcd(a, b):
"""
Calcule la pgcd de a et b
paramètre:
a : (int) entier >= 0
b : (int) entier >= 0
retour:
un entier positif
"""
...
```
%% Cell type:code id: tags:
``` python
pgcd(220, 12)
```
%% Cell type:markdown id: tags:
<h3> Exercice 3</h3>
**Nombres d'adhérents**
Une association a remarqué que d'une année à l'autre,
<ul> <li>elle perd 5 % de ses adhérents ; </li>
<li>elle gagne 200 adhérents.</li>
</ul>
1) En admettant que le nombre d'adhérents de cette association était égal à 2000 au Ier janvier 2019, écrire en Python une fonction récursive nommée nombre(n) affichant le nombre théorique d'adhérents après n années, n ≥ 1.
2) Dans ce même programme, afficher le nombre théorique d'adhérents au cours des 20 prochaines années.
%% Cell type:code id: tags:
``` python
def nombre(n):
"""
Calcule le nombre théorique d'adhérents
paramètre:
n : (int) entier >= 1
retour:
un flotant positif
"""
```
%% Cell type:code id: tags:
``` python
nombre(10)
```
%% Cell type:markdown id: tags:
2) Nombres d'adhérents au cours des 20 prochaines années
%% Cell type:code id: tags:
``` python
for ...:
print...
```
%% Cell type:markdown id: tags:
<h3> Exercice 4</h3>
**Suite de Fibonacci**
La suite de Fibonacci est la suite numérique définie par :
F0 = F1 = 1 , pour tout n ≥ 0, Fn+2 = Fn+l + Fn
Écrire en Python une fonction récursive fibo(n) permettant d'afficher le terme Fn , où n ≥ 0, puis afficher les termes de F0 à F20.
%% Cell type:code id: tags:
``` python
def fibo(n):
"""
Calcule la valeur de la suite de Fibonacci pour n
paramètre:
n : (int) entier >= 0
retour:
un entier positif
"""
...
```
%% Cell type:markdown id: tags:
<h3> Exercice 5</h3>
%% Cell type:code id: tags:
``` python
# Version récursive normale
def somme_rec(a, b):
"""
Calcule la somme de a et b
paramètre:
a : (int) entier >= 0
b : (int) entier >= 0
retour:
un entier positif
"""
....
# Version récursive terminale
def somme_rec_term(a, b):
"""
Calcule la somme de a et b
paramètre:
a : (int) entier >= 0
b : (int) entier >= 0
retour:
un entier positif
"""
....
```
%% Cell type:code id: tags:
``` python
somme_rec(3, 5)
```
%% Cell type:code id: tags:
``` python
somme_rec_term(3, 5)
```
%% Cell type:markdown id: tags:
<h3> Exercice 6</h3>
1) Version itérative
%% Cell type:code id: tags:
``` python
def somme(a, b):
"""
Calcule la somme de a et b
paramètre:
a : (int) entier >= 0
b : (int) entier >= 0
retour:
un entier positif
"""
.....
somme(-3, 5)
```
%% Cell type:markdown id: tags:
2) Version récursive normale
%% Cell type:code id: tags:
``` python
def somme_rec(a, b):
"""
Calcule la somme de a et b
paramètre:
a : (int) entier relatif
b : (int) entier relatif
retour:
un entier relatif
"""
....
somme_rec(-3, 5)
```
%% Cell type:markdown id: tags:
3) Version récursive terminale
%% Cell type:code id: tags:
``` python
def somme_rec_term(a, b):
"""
Calcule la somme de a et b
paramètre:
a : (int) entier relatif
b : (int) entier relatif
retour:
un entier relatif
"""
....
somme_rec_term(-3, 5)
```
%% Cell type:markdown id: tags:
<h3> Exercice 7</h3>
**Fonction paire**
%% Cell type:code id: tags:
``` python
def pair(n):
"""
Détermine si un nombre entier positif est pair
paramètre:
n : (int) entier >= 0
retour:
un booléen
"""
....
pair(8)
```
%% Cell type:markdown id: tags:
<h3> Exercice 8</h3>
**Fonction puissance**
1) Version récursive normale
%% Cell type:code id: tags:
``` python
def puissance(x, n):
"""
Calcule la puissance de x à l'exposant n
paramètre:
x : (flottant)
n : (int) entier relatif
retour:
un flottant
"""
....
puissance(7, 28)
```
%% Cell type:markdown id: tags:
2) Version récursive terminale - On utilise un accumulateur
%% Cell type:code id: tags:
``` python
def puissance(x, n, acc):
"""
Calcule la puissance de x à l'exposant n
paramètre:
x : (float)
n : (int) entier relatif
acc: (int) accumulateur
retour:
un flottant
"""
if n == 0:
return acc
else:
return puissance(x, n-1, x*acc)
puissance(7, 28, 1)
```
%% Cell type:markdown id: tags:
<h3> Exercice 9</h3>
**Fonction puissance deuxième version**
On peut cependant utiliser une autre définition de la fonction mathématique
<code>puissance(x, n)</code> qui **réduit** drastiquement le nombre d’appels récursifs emboîtés.
D'après l'énoncé, on peut décomposer la fonction **puissance** en utilisant la fonction **carre**.
On peut le faire avec **deux cas récursifs** qui distinguent la parité de ***n*** et deux cas de base (***n = 0*** et ***n = 1***), comme ci-dessous :
*puissance(x, n) =*
1 &emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp; &#160; &#160;&ensp;si n = 0,
x &emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp; &#160; &#160;&ensp;si n = 1,
carre(puissance(x, n//2)) &emsp;&emsp;&emsp;&ensp;&ensp; si n > 1 et n est pair,
x * carre(puissance(x, (n - 1)//2)) &ensp; si n > 1 et n est impair.
<br/>
Pour le codage en Python
<ul>
L’appel à la fonction <code>carre(x)</code> est simplement remplacé par la multiplication <code>r * r</code>.
Le test de parité est réalisé par un **test à zéro du reste de la division entière par 2** (soit <code>r % 2 == 0</code>).
</ul>
%% Cell type:code id: tags:
``` python
def puissance(x,n):
"""
Calcule la puissance de x à l'exposant n
paramètre:
x : (flottant)
n : (int) entier relatif
retour:
un flottant
"""
....
```
%% Cell type:code id: tags:
``` python
puissance(7, 28)
```
%% Cell type:markdown id: tags:
<h3> Exercice 10</h3>
**Fonction somme sur les élément d'une tableau ou liste en python**
1) On donne ici trois solutions possibles
%% Cell type:code id: tags:
``` python
def somme(liste):
"""
Calcule la somme des éléments d'un tableau
paramètre:
liste : (list) une tableau de nombres
retour:
un flottant
"""
....
```
%% Cell type:code id: tags:
``` python
ex_liste = [4, 7, 2]
```
%% Cell type:code id: tags:
``` python
somme1(ex_liste)
```
%% Cell type:code id: tags:
``` python
somme2(ex_liste)
```
%% Cell type:code id: tags:
``` python
somme3(ex_liste)
```
%% Cell type:markdown id: tags:
2) Explication du déroulement
On utilise la fonction <code>somme(liste)</code> et la liste <code>ex_liste = [4, 7, 2]</code> en paramètre.
...
%% Cell type:markdown id: tags:
<h3> Exercice 11</h3>
**Fonction mystere**
1)...
%% Cell type:code id: tags:
``` python
mystere(18)
```
%% Cell type:markdown id: tags:
2) On va utiliser une fonction à deux paramètres.
D'après le cours de première, il suffit d'ajouter **2^n si r est négatif**.
En utilisant la fonction <code>mystere(n)</code> de l'énoncé, on obtient :
%% Cell type:code id: tags:
``` python
def binaire(r, n): # on suppose -2**(n-1) <= n < 2**(n-1)
"""
Détermine l'écriture binaire d'un nombre entier relatif
paramètre:
r : (int) entier relatif
n : (int) un entier naturel stritement positif
retour:
chaîne de caractères
"""
...
```
%% Cell type:code id: tags:
``` python
binaire(18, 8)
```
%% Cell type:code id: tags:
``` python
binaire(-18, 8)
```
%% Cell type:markdown id: tags:
3) Résultat codé avec n caractères. On peut avoir eventuellement des 0 au début.
%% Cell type:code id: tags:
``` python
def binaire(r, n, cpt = 0): # on suppose -2**(n-1) <= n < 2**(n-1)
"""
Détermine l'écriture binaire d'un nombre entier relatif
paramètre:
r : (int) entier relatif
n : (int) un entier naturel stritement positif
cpt: (int) compteur
retour:
chaîne de caractères
"""
```
%% Cell type:code id: tags:
``` python
binaire(-18, 16)
```
%% Cell type:code id: tags:
``` python
binaire(7, 8)
```
%% Cell type:markdown id: tags:
<h3> Exercice 12</h3>
**Fonction inverse**.
Cette fonction inverse une chaîne de caractères.
%% Cell type:code id: tags:
``` python
def inverse(ch):
"""
Inverse l'ordre d'une chaîne de caractères
paramètre:
ch : (str) chaîne de caractères
retour:
chaîne de caractères
"""
....
```
%% Cell type:code id: tags:
``` python
inverse("azerty")
```
%% Cell type:markdown id: tags:
<h3> Exercice 13</h3>
**Fonction nettoie**.
Cette fonction elimine les doublons dans une liste.
Version itérative de l'énoncé :
%% Cell type:code id: tags:
``` python
def nettoie(liste):
"""
Elimine les doublons d'une liste
paramètre:
liste : (list) une liste de nombres
retour:
une liste
"""
n = len(liste)
k = 0
while k < n - 1:
if liste[k] != liste[k+1]:
k = k + 1
else:
del liste[k]
n = len(liste)
```
%% Cell type:markdown id: tags:
Version récursive :
%% Cell type:code id: tags:
``` python
def nettoie_rec(liste, k = 0):
"""
Elimine les doublons d'une liste
paramètre:
liste : (list) une liste de nombres
retour:
une liste
"""
....
```
%% Cell type:code id: tags:
``` python
ex_liste = [1, 1, 2, 6, 6, 6, 8, 8, 9, 10]
nettoie(ex_liste)
ex_liste
```
%% Cell type:code id: tags:
``` python
ex_liste = [1, 1, 2, 6, 6, 6, 8, 8, 9, 10]
nettoie_rec(ex_liste)
ex_liste
```
%% Cell type:code id: tags:
``` python
```
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment