Exercices pour s'entraîner☘
Les exercices ci-dessous ont pour but de vous familiariser avec la
programmation de fonction récursive.
Prenez l'habitude de revenir vous entraîner régulièrement avec ces
exercices tout au long de l'année. Ils sont généralement accompagnés de
pistes et de leur solution pour vous permettre de progresser.
Rappels
- Chaque programme Python doit être sauvegardé sous forme de fichier texte
avec l'extension
.py
.
Enregistrez ce fichier dans le dossier[D03-Récursivité]
avec le nom donné à l'exercice :ProgD03.61.py
,ProgD03.62.py
, etc... - Pour exécuter ce programme, il suffit de le sauvegarder puis d'appuyer
sur la touche
[F5]
.
Prog D03.61☘
On rappelle la définition de la fonction rectangle()
utilisée dans le cours :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
En utilisant cette fonction, complétez le code récursif de la fonction
fct3()
telle que l'appel fct3(100)
permet de tracer la figure ci-dessous :
1 2 3 4 |
|
Une illustration par vidéo
Voici une petite vidéo du tracé pour visualiser l'empilement des
instructions lors de l'appel fct3(100)
:
Une piste
Ne pas oublier le travail réalisé dans le cours sur les piles d'appels...
Une solution
def fct3(n):
"""
n - int
"""
if n > 10:
rectangle(n)
fct3(n-10)
rectangle(n)
Exercice D03.62☘
On considère la fonction suivante :
1 2 3 4 5 6 7 8 |
|
Réalisez à la main le dessin obtenu avec l'appel fct4(50)
. Sur votre
cahier, un carreau représente 10 pixels.
Vérifiez ensuite en effectuant cet appel dans la console Python.
Réponse et explications
On pourrait croire que la figure obtenue est :
Mais l'exécution de fct4(50)
dans la console donne cette animation :
En effet :
-
fct4(50)
exécutefct4(40)
puis, une fois cette exécution terminée tracerectangle(50)
et enfin exécute à nouveaufct4(40)
: -
fct4(40)
exécutefct4(30)
puis, une fois cette exécution terminée, tracerectangle(40)
et enfin exécute à nouveaufct4(30)
: -
fct4(30)
exécutefct4(20)
puis, une fois cette exécution terminée, tracerectangle(30)
et enfin exécute à nouveaufct4(20)
: -
et ainsi de suite... jusqu'à l'appel
fct4(10)
qui n'exécute rien puisque l'argument n'est pas strictement supérieur à 10 :
En remontant la pile d'appels de gauche à droite, on obtient ainsi la figure dessinée par la tortue :
Prog D03.63☘
On rappelle ci-dessous l'algorithme d'Euclide, qui permet de calculer le pgcd de deux entiers positifs :
- On calcule le reste r de la division de a par b
- Tant que le reste n'est pas nul, a prend la valeur de b et b, celle de r
- Le PGCD est le dernier reste non nul.
Complétez une version récursive de ce calcul :
1 2 3 4 5 |
|
Exemple de test
>>> pgcd(91, 143)
13
Des pistes
- Pensez au cas de base ;
- Pensez à l'expression d'un appel en fonction du suivant.
Définition récursive explicite
- Pour tout entier a (non nul), pgcd(a, 0) = a.
- Pour tous les entiers a et b non nuls, pgcd(a, b) = pgcd(b, r), où r est le reste dans la division euclidienne de a par b.
Une solution
1 2 3 4 5 6 7 8 9 |
|
Prog D03.65*☘
On définit la suite de Fibonacci par F_0 = F_1 = 1 et, pour tout entier naturel n : F_{n+2} = F_{n+1} + F_n.
-
Complétez une version récursive du calcul d'un terme de cette suite :
1 2 3 4 5
def fibonacci(n): """ n – entier positif ou nul Sortie: entier – le nombre F_n """
Un test
>>> fibonacci(13) 377
Une solution
1 2 3 4 5 6 7 8 9
def fibonacci(n): """ n – entier positif ou nul Sortie: entier – le nombre F_n """ if n <= 1 : return 1 else : return fibonacci(n-1) + fibonacci(n-2)
-
On effectue l'appel
fibonacci(7)
. Représentez sur un cahier l'arbre d'appels correspondant.Une solution
Visualisation de l'arbre dans la console
Une fonction peut prendre pour paramètre une autre fonction, cela permet de créer des décorateurs. Ainsi, la fonction
tracer()
ci-dessous permet de visualiser dans la console l'arbre des appels d'une fonction récursive :
Les instructions1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
def tracer(f): """ f - une fonction récursive Sortie: None - Affiche l'arbre des appels de la fonction f """ decalage = 0 def g(x): nonlocal decalage # decalage est une variable de tracer(), pas de g() print('| ' * decalage, end='') print(f'|Appel {f.__name__}({x})') # f.__name__ sera remplacé par le nom de la fonction "tracée" decalage +=1 imagefx = f(x) print('| ' * decalage, end='') print('|-> renvoie {}'.format(imagefx) ) decalage -= 1 return imagefx return g
renvoient :>>> fibonacci = tracer(fibonacci) >>> fibonacci(7)
|Appel fibonacci(7) | |Appel fibonacci(6) | | |Appel fibonacci(5) | | | |Appel fibonacci(4) | | | | |Appel fibonacci(3) | | | | | |Appel fibonacci(2) | | | | | | |Appel fibonacci(1) | | | | | | | |-> renvoie 1 | | | | | | |Appel fibonacci(0) | | | | | | | |-> renvoie 1 | | | | | | |-> renvoie 2 | | | | | |Appel fibonacci(1) | | | | | | |-> renvoie 1 | | | | | |-> renvoie 3 | | | | |Appel fibonacci(2) | | | | | |Appel fibonacci(1) | | | | | | |-> renvoie 1 | | | | | |Appel fibonacci(0) | | | | | | |-> renvoie 1 | | | | | |-> renvoie 2 | | | | |-> renvoie 5 | | | |Appel fibonacci(3) | | | | |Appel fibonacci(2) | | | | | |Appel fibonacci(1) | | | | | | |-> renvoie 1 | | | | | |Appel fibonacci(0) | | | | | | |-> renvoie 1 | | | | | |-> renvoie 2 | | | | |Appel fibonacci(1) | | | | | |-> renvoie 1 | | | | |-> renvoie 3 | | | |-> renvoie 8 | | |Appel fibonacci(4) | | | |Appel fibonacci(3) | | | | |Appel fibonacci(2) | | | | | |Appel fibonacci(1) | | | | | | |-> renvoie 1 | | | | | |Appel fibonacci(0) | | | | | | |-> renvoie 1 | | | | | |-> renvoie 2 | | | | |Appel fibonacci(1) | | | | | |-> renvoie 1 | | | | |-> renvoie 3 | | | |Appel fibonacci(2) | | | | |Appel fibonacci(1) | | | | | |-> renvoie 1 | | | | |Appel fibonacci(0) | | | | | |-> renvoie 1 | | | | |-> renvoie 2 | | | |-> renvoie 5 | | |-> renvoie 13 | |Appel fibonacci(5) | | |Appel fibonacci(4) | | | |Appel fibonacci(3) | | | | |Appel fibonacci(2) | | | | | |Appel fibonacci(1) | | | | | | |-> renvoie 1 | | | | | |Appel fibonacci(0) | | | | | | |-> renvoie 1 | | | | | |-> renvoie 2 | | | | |Appel fibonacci(1) | | | | | |-> renvoie 1 | | | | |-> renvoie 3 | | | |Appel fibonacci(2) | | | | |Appel fibonacci(1) | | | | | |-> renvoie 1 | | | | |Appel fibonacci(0) | | | | | |-> renvoie 1 | | | | |-> renvoie 2 | | | |-> renvoie 5 | | |Appel fibonacci(3) | | | |Appel fibonacci(2) | | | | |Appel fibonacci(1) | | | | | |-> renvoie 1 | | | | |Appel fibonacci(0) | | | | | |-> renvoie 1 | | | | |-> renvoie 2 | | | |Appel fibonacci(1) | | | | |-> renvoie 1 | | | |-> renvoie 3 | | |-> renvoie 8 | |-> renvoie 21
-
Expliquez en quoi ce script est inefficace et proposer une version non récursive plus efficace, en complexité linéaire.
Une solution
L'arbre d'appel permet de voir que certains calculs sont lancés à plusieurs reprises, d'où l'inefficacité de ce script. Les temps de calcul deviennent rapidement excessifs : essayez par exemple de calculer
fibonacci(50)
avec ce script...Une version itérative plus efficace est, par exemple :
1 2 3 4 5 6 7 8 9 10 11 12
def fibonacci(n): """ n – entier positif ou nul Sortie: entier – le nombre F_n """ f0 = 1 f1 = 1 for i in range(1, n): f2 = f0 + f1 f0 = f1 f1 = f2 return f1
Prog D03.66*☘
On dispose d'un tableau contenant des entiers ou des tableaux d'entiers, ou des tableaux de tableaux d'entiers ou des tableaux de tableaux de tableaux d'entiers ou ...
Par exemple :
tab = [3, [2,3,[4,5,6], [2,[3,5]] ] , 8, [[[[ 4, [1] ]]]], 15 ]
Complétez la définition récursive de la fonction somme_tab_imbriques
,
qui prend pour paramètre un tableau imbriqué et qui renvoie la somme de
l'ensemble des entiers contenus dans ce tableau.
1 2 3 4 5 |
|
Un test
>>> tab = [2, 3, 4, [2, 5, [4, 7], 8], [2, [[[7]]]], [] ]
>>> somme_tab_imbriques(tab)
44
Une piste
On pourra utiliser la fonction isinstance()
:
>>> isinstance(8, int) # 8 est-il de type int ?
True
>>> isinstance(8, list) # 8 est-il de type list ?
False
>>> isinstance([8], list)
True
Une autre piste
Le cas de base intervient quand le paramètre est un entier. Il suffit alors de renvoyer cette valeur.
Pour comprendre les appels récursifs à réaliser, il suffit de se rappeler
que la somme des éléments d'un tableau imbriqué est la somme totale des
tableaux imbriqués contenus dans ce tableau.
En d'autres termes, si tab
est de la forme tab = [A, B, C]
où
A
, B
et C
sont des tableaux imbriqués alors
somme_tab_imbriques(tab)
est égale à somme_tab_imbriques(A) +
somme_tab_imbriques(B) + somme_tab_imbriques(C)
.
Une solution
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|