# TD5 : Récursivité ## Définitions Une fonction **récursive** est une fonction qui s'appelle elle-même. Elle doit avoir une manière de gérer une **itération** ainsi qu'une **condition d'arrêt**. Le gros problème d'une fonction récusive sont les ressources nécessaires et l'explosion de la taille de la pile d'appel. Par exemple, la fonction `recsum`: ```python= def recsum(x): if (x === 0): return 0 else: return x + recsum(x - 1) ``` Nécessite la pile d'appel suivante pour un appel à `recsum(5)`: ```python recsum(5) 5 + recsum(4) 5 + (4 + recsum(3)) 5 + (4 + (3 + recsum(2))) 5 + (4 + (3 + (2 + recsum(1)))) 5 + (4 + (3 + (2 + (1 + recsum(0))))) 5 + (4 + (3 + (2 + (1 + 0)))) 5 + (4 + (3 + (2 + 1))) 5 + (4 + (3 + 3)) 5 + (4 + 6) 5 + 10 15 ``` Le principal problème vient de la ligne 4 et de l'impossibilité pour l'interprète du langage de compléter le calcul sans le résultat... Une fonction **récursive terminale** corrige le problème en rendant l'appel à la fonction récursive pur et sans autre opération. Par exemple, la fonction `tailrecsum` : ```python= def tailrecsum(x, running_total): if (x == 0): return running_total else: return tailrecsum(x - 1, running_total + x) ``` Dans ce cas, la pile d'appel pour `tailrecsum(5)` devient : ```python tailrecsum(5, 0) tailrecsum(4, 5) tailrecsum(3, 9) tailrecsum(2, 12) tailrecsum(1, 14) tailrecsum(0, 15) 15 ``` ## Exercices 1. **Factorielle** Calcul de `n! = 1 * 2 * 3 * ... * n` **Solution :** ```python def factorielle(n): if n <= 1: return 1 return n * factorielle(n-1) ``` --- 2. **Fibonacci** Calcul de `u0 = 0, u1 = 1 et un+2 = un+1 + un` **Solution :** ```python def fibonacci(n): if n == 0: return 0 elif n == 1: return 1 else: return fibonacci(n-1) + fibonacci(n-2) ``` --- 3. **Fibonacci terminale** **Solution :** ```python def fibonacci_term(a, b, n): if n > 1: return fibonacci_term(b, a+b, n-1) elif n == 1: return b else: return a ``` --- 4. **Fonction d'Ackermann** ```python ack(0,n) = n+1 ack(m,0) = ack(m-1,1) ack(m,n) = ack(m-1, ack(m, n-1)) ``` **Solution :** ```python def ack(m, n): if m == 0: return n + 1 if n == 0: return ack(m - 1, 1) return ack(m - 1, ack(m, n - 1)) ``` --- 5. **PGCD** **Solution :** ```python def pgcd(a, b): if a < b: return pgcd(b, a) if b == 0: return a return pgcd(b, a % b) ``` --- 6. **Flocon de Koch** Code donné : ```python import koch_drawer as kd def decoupe(pd, pf, n): # COMMENT UTILISER N? # Trace le segment kd.draw(pd, pf) # Calcule les points p1/3, ps et p2/3 du schéma # Utiliser ces points pour les appels récursifs quand il le faut p1 = ((2*pd[0]+pf[0])/3, (2*pd[1]+pf[1])/3) p2 = ((pd[0]+2*pf[0])/3, (pd[1]+2*pf[1])/3) ps = ((pd[0]+pf[0])/2 - (pf[1]-pd[1])/(2*(3**0.5)), \ (pd[1]+pf[1])/2 + (pf[0]-pd[0])/(2*(3**0.5))) # pour traiter le flocon : # ecrire une fonction koch(flocon, n) # qui découpe (n fois) chaque segment de la liste flocon def koch(flocon, n): ... if __name__=="__main__": x = (0,0) y = (1,0) z = (0.5, 3**0.5/2) courbe = [x, y] flocon = [x, z, y, x] n = 7 # Pour découper decoupe(x, y, n) koch(flocon, n) print(kd.longueur()) kd.draw_koch() ``` **Solution :** ```python def decoupe(pd, pf, n): if n==0: # Trace le segment : récursion maximale atteinte kd.draw(pd, pf) else: # Calcule les points p1/3, ps et p2/3 du schéma p1 = ((2*pd[0]+pf[0])/3, (2*pd[1]+pf[1])/3) p2 = ((pd[0]+2*pf[0])/3, (pd[1]+2*pf[1])/3) ps = ((pd[0]+pf[0])/2 - (pf[1]-pd[1])/(2*(3**0.5)), \ (pd[1]+pf[1])/2 + (pf[0]-pd[0])/(2*(3**0.5))) decoupe(pd, p1, n-1) decoupe(p1, ps, n-1) decoupe(ps, p2, n-1) decoupe(p2, pf, n-1) def koch(courbe, n): for i in range(len(courbe)-1): decoupe(courbe[i], courbe[i+1], n) ``` ###### tags: `python`