{"cells":[{"metadata":{},"cell_type":"markdown","source":"<h1>Récursivité</h1>\n<ul>\n<h2> Compléments</h2> \n\n<h3>La fonction <em>somme(n)</em> du cours</h3>\n</ul>"},{"metadata":{},"cell_type":"markdown","source":"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/> \nIl faut néanmoins faire attention à **deux points importants** : \n\n* 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.\n* 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é.\n\n<h4>Exemple</h4>\n\nLe code de la fonction <code> somme(n)</code> rappelé ci-dessous,ne se **comporte pas** exactement comme la fonction mathématique.\n\n<ul>"},{"metadata":{"trusted":false},"cell_type":"code","source":"def somme(n):\n if n == 0:\n return 0\n else:\n return n + somme(n - 1)","execution_count":1,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"somme(5)","execution_count":2,"outputs":[{"data":{},"execution_count":2,"metadata":{},"output_type":"execute_result"}]},{"metadata":{},"cell_type":"markdown","source":"La principale différence est que la **fonction mathématique est uniquement définie pour des entiers naturels**, \nalors que la fonction <code>somme(n)</code> peut être appelée avec un **entier Python arbitraire**, qui peut être une **valeur négative**.\n\n\nBien que la fonction mathématique ne soit pas définie pour\n**n = −1**, l’appel <code>somme(-1)</code> ne provoque **aucune erreur immédiate**, \nmais il implique un appel à <code>somme(-2)</code>, qui déclenche un appel à <code>somme(-3)</code>, etc. \nCe processus **infini** va finir par provoquer une **erreur à l’exécution**.\n</ul>"},{"metadata":{"trusted":false},"cell_type":"code","source":"somme(-1)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Pour **éviter ce comportement**, c'est de restreindre les appels à la fonction <code>somme(n)</code> aux **entiers positifs ou nuls**. \nPour cela, on peut utiliser une instruction **assert** comme ci-dessous :"},{"metadata":{"trusted":false},"cell_type":"code","source":"def somme(n):\n assert n >= 0, \"entrer un entier naturel\"\n if n == 0:\n return 0\n else:\n return n + somme(n - 1)","execution_count":4,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"somme(5)","execution_count":5,"outputs":[{"data":{},"execution_count":5,"metadata":{},"output_type":"execute_result"}]},{"metadata":{"trusted":false},"cell_type":"code","source":"somme(-1)","execution_count":6,"outputs":[{"ename":"AssertionError","evalue":"entrer un entier naturel","output_type":"error","traceback":["\u001b[1;31m---------------------------------------------------------------------------\u001b[0m","\u001b[1;31mAssertionError\u001b[0m Traceback (most recent call last)","\u001b[1;32m<ipython-input-6-2263d90c8da2>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0msomme\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m-\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m","\u001b[1;32m<ipython-input-4-b514195a3042>\u001b[0m in \u001b[0;36msomme\u001b[1;34m(n)\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0msomme\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mn\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[1;32massert\u001b[0m \u001b[0mn\u001b[0m \u001b[1;33m>=\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\"entrer un entier naturel\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 3\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[1;33m==\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 4\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n","\u001b[1;31mAssertionError\u001b[0m: entrer un entier naturel"]}]},{"metadata":{},"cell_type":"markdown","source":"Bien que cette solution soit correcte, elle n’est pas encore **complètement satisfaisante**. \n\nEn effet, pour tout appel <code>somme(n)</code> avec **n >= 0**, chaque appel récursif **commencera par faire le test associé à l’instruction assert**, \nalors que **chaque valeur** de n, par la suite, sera **nécessairement positive**.\n\nUne solution pour éviter ces tests inutiles est de définir **deux fonctions** :\n<ul>\n \nLa première, <code>somme_bis(n)</code>, implémente la définition récursive de la fonction\nmathématique <code>somme(n)</code> sans **vérifier son argument**.\n\n</ul> "},{"metadata":{"trusted":false},"cell_type":"code","source":"def somme_bis(n):\n if n == 0:\n return 0\n else:\n return n + somme_bis(n - 1)","execution_count":7,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"La seconde, <code>somme(n)</code>, est la fonction « *principale* » qui sera appelée par l’utilisateur. \nCette fonction ne fait que vérifier (**une et une seule fois**) que\nson argument **n est positif** puis, si c’est le cas, elle appelle la fonction\n<code>somme_bis(n)</code>."},{"metadata":{"trusted":false},"cell_type":"code","source":"def somme(n):\n assert n >= 0, \"entrer un entier naturel\"\n return somme_bis(n)","execution_count":8,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"somme(5)","execution_count":9,"outputs":[{"data":{},"execution_count":9,"metadata":{},"output_type":"execute_result"}]},{"metadata":{"trusted":false},"cell_type":"code","source":"somme(-1)","execution_count":10,"outputs":[{"ename":"AssertionError","evalue":"entrer un entier naturel","output_type":"error","traceback":["\u001b[1;31m---------------------------------------------------------------------------\u001b[0m","\u001b[1;31mAssertionError\u001b[0m Traceback (most recent call last)","\u001b[1;32m<ipython-input-10-2263d90c8da2>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0msomme\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m-\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m","\u001b[1;32m<ipython-input-8-4a3635feae60>\u001b[0m in \u001b[0;36msomme\u001b[1;34m(n)\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0msomme\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mn\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[1;32massert\u001b[0m \u001b[0mn\u001b[0m \u001b[1;33m>=\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\"entrer un entier naturel\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 3\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0msomme_bis\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mn\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n","\u001b[1;31mAssertionError\u001b[0m: entrer un entier naturel"]}]},{"metadata":{},"cell_type":"markdown","source":"<h4>Conclusion</h4> \n<br/>\n<div style=\"background-color:#DDD5ED;\">\n \nUn calcul peut être décrit à l’aide d’une <font color='red'> <strong>définition récursive</strong></font>. \nL’avantage de cette technique est que l’implémentation est souvent plus proche de la définition. \n 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.\n\nIl 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>. \n\nEnfin, 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>. \n(Voir les ***exercices 8 et 9*** sur les puissances)\n\n</div>"},{"metadata":{},"cell_type":"markdown","source":"<ul><h2> Fiche 2 </h2> \n<h3> Exercice 1</h3>\n\nFonction **Factorielle**\n\nL'un des exemples les plus classiques est une fonction mathématique, la fonction factorielle. \nOn dit « factorielle n » et on note avec un point d'exclamation n!. \nCette fonction est définie « par récurrence » sur les entiers naturels : 0! = 1 et pour tout entier naturel n non nul, \nn! = n × (n – 1)!. \nAutrement dit, pour tout n non nul, n! = n × (n — 1) × (n — 2) × … × 2 × 1. \n\nEn utilisant le langage **Python** \n\n1) Ecrire une fonction itérative calculant factorielle n, ayant pour paramètre un entier positif, et retourne le calcul. \na) En utilisant le boucle « for »"},{"metadata":{"trusted":false},"cell_type":"code","source":"def fact_for(n):\n \"\"\"\n Calcule la factorielle de n\n paramètre:\n n : (int) entier >= 0\n retour:\n un entier positif\n \"\"\"\n ...","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"fact_for(5)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"b) En utilisant le boucle « while »"},{"metadata":{"trusted":false},"cell_type":"code","source":"def fact_while(n):\n \"\"\"\n Calcule la factorielle de n\n paramètre:\n n : (int) entier >= 0\n retour:\n un entier positif\n \"\"\"\n ...","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"fact_while(5)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"2) Fonction récursive"},{"metadata":{"trusted":false},"cell_type":"code","source":"def fact_rec(n):\n \"\"\"\n Calcule la factorielle de n\n paramètre:\n n : (int) entier >= 0\n retour:\n un entier positif\n \"\"\"\n if n == 0:\n return 1\n else:\n return n * fact_rec(n - 1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"fact_rec(5)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3> Exercice 2</h3>\n\n**Calcul de pgcd de deux nombres entiers**\n\nÉcrire en Python une fonction récursive pgcd (a , b) renvoyant le plus grand diviseur commun de deux \nnombres a et b. \nPour cela, on utilisera le résultat mathématique suivant : « pgcd(a , b) = pgcd(b , r), où a = bq + r » \n"},{"metadata":{"trusted":false},"cell_type":"code","source":"def pgcd(a, b):\n \"\"\"\n Calcule la pgcd de a et b\n paramètre:\n a : (int) entier >= 0\n b : (int) entier >= 0\n retour:\n un entier positif\n \"\"\"\n ...","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"pgcd(220, 12)","execution_count":null,"outputs":[]},{"metadata":{"collapsed":true},"cell_type":"markdown","source":"<h3> Exercice 3</h3>\n\n**Nombres d'adhérents** \n\nUne association a remarqué que d'une année à l'autre, \n<ul> <li>elle perd 5 % de ses adhérents ; </li>\n <li>elle gagne 200 adhérents.</li>\n</ul>\n\n1)\tEn 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. \n2)\tDans ce même programme, afficher le nombre théorique d'adhérents au cours des 20 prochaines années. \n"},{"metadata":{"trusted":false},"cell_type":"code","source":"def nombre(n):\n \"\"\"\n Calcule le nombre théorique d'adhérents\n paramètre:\n n : (int) entier >= 1\n retour:\n un flotant positif\n \"\"\"\n ","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"nombre(10)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"2) Nombres d'adhérents au cours des 20 prochaines années"},{"metadata":{"trusted":false},"cell_type":"code","source":"for ...:\n print...","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3> Exercice 4</h3>\n\n**Suite de Fibonacci** \n\nLa suite de Fibonacci est la suite numérique définie par : \nF0 = F1 = 1 , pour tout n ≥ 0, Fn+2 = Fn+l + Fn \n\nÉ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.\n"},{"metadata":{"trusted":false},"cell_type":"code","source":"def fibo(n):\n \"\"\"\n Calcule la valeur de la suite de Fibonacci pour n\n paramètre:\n n : (int) entier >= 0\n retour:\n un entier positif\n \"\"\"\n...","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3> Exercice 5</h3>"},{"metadata":{"trusted":false},"cell_type":"code","source":"# Version récursive normale\ndef somme_rec(a, b):\n \"\"\"\n Calcule la somme de a et b\n paramètre:\n a : (int) entier >= 0\n b : (int) entier >= 0\n retour:\n un entier positif\n \"\"\"\n....\n\n\n# Version récursive terminale\ndef somme_rec_term(a, b):\n \"\"\"\n Calcule la somme de a et b\n paramètre:\n a : (int) entier >= 0\n b : (int) entier >= 0\n retour:\n un entier positif\n \"\"\"\n....","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"somme_rec(3, 5)","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"somme_rec_term(3, 5)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3> Exercice 6</h3>\n1) Version itérative"},{"metadata":{"trusted":false},"cell_type":"code","source":"def somme(a, b):\n \"\"\"\n Calcule la somme de a et b\n paramètre:\n a : (int) entier >= 0\n b : (int) entier >= 0\n retour:\n un entier positif\n \"\"\"\n\n.....\n\nsomme(-3, 5)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"2) Version récursive normale"},{"metadata":{"trusted":false},"cell_type":"code","source":"def somme_rec(a, b):\n \"\"\"\n Calcule la somme de a et b\n paramètre:\n a : (int) entier relatif\n b : (int) entier relatif\n retour:\n un entier relatif\n \"\"\"\n....\n \nsomme_rec(-3, 5) ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"3) Version récursive terminale"},{"metadata":{"trusted":false},"cell_type":"code","source":"def somme_rec_term(a, b):\n \"\"\"\n Calcule la somme de a et b\n paramètre:\n a : (int) entier relatif\n b : (int) entier relatif\n retour:\n un entier relatif\n \"\"\"\n....\n\nsomme_rec_term(-3, 5)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3> Exercice 7</h3>\n\n**Fonction paire** "},{"metadata":{"trusted":false},"cell_type":"code","source":"def pair(n):\n \"\"\"\n Détermine si un nombre entier positif est pair\n paramètre:\n n : (int) entier >= 0\n retour:\n un booléen\n \"\"\"\n....\n \npair(8)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3> Exercice 8</h3> \n\n**Fonction puissance** \n1) Version récursive normale"},{"metadata":{"trusted":false},"cell_type":"code","source":"def puissance(x, n):\n \"\"\"\n Calcule la puissance de x à l'exposant n\n paramètre:\n x : (flottant) \n n : (int) entier relatif\n retour:\n un flottant\n \"\"\"\n....\n\npuissance(7, 28)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"2) Version récursive terminale - On utilise un accumulateur"},{"metadata":{"trusted":false},"cell_type":"code","source":"def puissance(x, n, acc):\n \"\"\"\n Calcule la puissance de x à l'exposant n\n paramètre:\n x : (float) \n n : (int) entier relatif\n acc: (int) accumulateur\n retour:\n un flottant\n \"\"\"\n if n == 0:\n return acc\n else:\n return puissance(x, n-1, x*acc)\n\npuissance(7, 28, 1)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3> Exercice 9</h3> \n\n**Fonction puissance deuxième version**\n\nOn peut cependant utiliser une autre définition de la fonction mathématique\n<code>puissance(x, n)</code> qui **réduit** drastiquement le nombre d’appels récursifs emboîtés. \n\nD'après l'énoncé, on peut décomposer la fonction **puissance** en utilisant la fonction **carre**.\nOn 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 :\n\n*puissance(x, n) =* \n\n 1                   si n = 0, \n x                   si n = 1, \n carre(puissance(x, n//2))       si n > 1 et n est pair, \n x * carre(puissance(x, (n - 1)//2))   si n > 1 et n est impair.\n\n<br/>\n\nPour le codage en Python\n<ul>\n \nL’appel à la fonction <code>carre(x)</code> est simplement remplacé par la multiplication <code>r * r</code>. \nLe 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>).\n\n</ul> "},{"metadata":{"trusted":false},"cell_type":"code","source":"def puissance(x,n):\n \"\"\"\n Calcule la puissance de x à l'exposant n\n paramètre:\n x : (flottant) \n n : (int) entier relatif\n retour:\n un flottant\n \"\"\"\n....","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"puissance(7, 28)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3> Exercice 10</h3> \n\n**Fonction somme sur les élément d'une tableau ou liste en python**\n\n1) On donne ici trois solutions possibles"},{"metadata":{"trusted":false},"cell_type":"code","source":"def somme(liste):\n \"\"\"\n Calcule la somme des éléments d'un tableau\n paramètre:\n liste : (list) une tableau de nombres\n retour:\n un flottant\n \"\"\"\n....","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"ex_liste = [4, 7, 2]","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"somme1(ex_liste)","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"somme2(ex_liste)","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"somme3(ex_liste)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"2) Explication du déroulement \n\nOn utilise la fonction <code>somme(liste)</code> et la liste <code>ex_liste = [4, 7, 2]</code> en paramètre.\n..."},{"metadata":{},"cell_type":"markdown","source":"<h3> Exercice 11</h3> \n\n**Fonction mystere**\n\n1)...\n"},{"metadata":{"trusted":false},"cell_type":"code","source":"mystere(18)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"2) On va utiliser une fonction à deux paramètres.\nD'après le cours de première, il suffit d'ajouter **2^n si r est négatif**. \nEn utilisant la fonction <code>mystere(n)</code> de l'énoncé, on obtient :"},{"metadata":{"trusted":false},"cell_type":"code","source":"def binaire(r, n): # on suppose -2**(n-1) <= n < 2**(n-1)\n \"\"\"\n Détermine l'écriture binaire d'un nombre entier relatif\n paramètre:\n r : (int) entier relatif\n n : (int) un entier naturel stritement positif\n retour:\n chaîne de caractères\n \"\"\"\n ...","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"binaire(18, 8)","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"binaire(-18, 8)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"3) Résultat codé avec n caractères. On peut avoir eventuellement des 0 au début."},{"metadata":{"trusted":false},"cell_type":"code","source":"def binaire(r, n, cpt = 0): # on suppose -2**(n-1) <= n < 2**(n-1)\n \"\"\"\n Détermine l'écriture binaire d'un nombre entier relatif\n paramètre:\n r : (int) entier relatif\n n : (int) un entier naturel stritement positif\n cpt: (int) compteur\n retour:\n chaîne de caractères\n \"\"\"\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"binaire(-18, 16)","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"binaire(7, 8)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3> Exercice 12</h3> \n\n**Fonction inverse**. \nCette fonction inverse une chaîne de caractères."},{"metadata":{"trusted":false},"cell_type":"code","source":"def inverse(ch):\n \"\"\"\n Inverse l'ordre d'une chaîne de caractères\n paramètre:\n ch : (str) chaîne de caractères\n retour:\n chaîne de caractères\n \"\"\"\n....","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"inverse(\"azerty\") ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3> Exercice 13</h3> \n\n**Fonction nettoie**. \nCette fonction elimine les doublons dans une liste.\n\nVersion itérative de l'énoncé :"},{"metadata":{"trusted":false},"cell_type":"code","source":"def nettoie(liste):\n \"\"\"\n Elimine les doublons d'une liste\n paramètre:\n liste : (list) une liste de nombres\n retour:\n une liste\n \"\"\"\n n = len(liste)\n k = 0\n while k < n - 1:\n if liste[k] != liste[k+1]:\n k = k + 1\n else:\n del liste[k]\n n = len(liste)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Version récursive :"},{"metadata":{"trusted":false},"cell_type":"code","source":"def nettoie_rec(liste, k = 0):\n \"\"\"\n Elimine les doublons d'une liste\n paramètre:\n liste : (list) une liste de nombres\n retour:\n une liste\n \"\"\"\n....","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"ex_liste = [1, 1, 2, 6, 6, 6, 8, 8, 9, 10]\nnettoie(ex_liste)\nex_liste","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"ex_liste = [1, 1, 2, 6, 6, 6, 8, 8, 9, 10]\nnettoie_rec(ex_liste)\nex_liste","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"}},"nbformat":4,"nbformat_minor":2}
\ No newline at end of file
%% 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
defsomme(n):
ifn==0:
return0
else:
returnn+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 :
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
defsomme_bis(n):
ifn==0:
return0
else:
returnn+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
Un calcul peut être décrit à l’aide d’une <fontcolor='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 <fontcolor='red'>**fonction récursive**</font>nécessite de distinguer les <fontcolor='red'>**cas de base**</font>, pour lesquels on peut donner un résultat facilement, et les<fontcolor='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<fontcolor='red'>**domaine**</font> de la fonction mathématique, par exemple en utilisant l’instruction<fontcolor='red'>**assert**</font>.
Enfin, il faut comprendre le modèle d’exécution des fonctions récursives pour choisir la définition qui<fontcolor='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
deffact_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
deffact_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
deffact_rec(n):
"""
Calcule la factorielle de n
paramètre:
n : (int) entier >= 0
retour:
un entier positif
"""
ifn==0:
return1
else:
returnn*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
defpgcd(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
defnombre(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
deffibo(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
defsomme_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
defsomme_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
defsomme(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
defsomme_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
defsomme_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
defpair(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
defpuissance(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
defpuissance(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
"""
ifn==0:
returnacc
else:
returnpuissance(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                 si n = 0,
x                 si n = 1,
carre(puissance(x, n//2))       si n > 1 et n est pair,
x * carre(puissance(x, (n - 1)//2))   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
defpuissance(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
defsomme(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
defbinaire(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
defbinaire(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
definverse(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.