{"cells":[{"metadata":{},"cell_type":"markdown","source":"<h1 class=\"alert alert-success\">Complément sur la récursivité en programmation</h2>"},{"metadata":{},"cell_type":"markdown","source":"## <h2 class=\"alert alert-info\"> 1. Introduction à la récursivité</h2>\nUn algorithme récursif est un algorithme qui résout un problème en calculant des solutions d'instances plus petites du même problème. "},{"metadata":{},"cell_type":"markdown","source":"<h2 class=\"alert alert-success\"> Rappel : Algorithme pour implémenter une fonction récursive</h2>\n\n- Etape 1 - Définir le cas de base : Identifier le cas le plus simple pour lequel une solution est connue.\n\n- Etape 2 - Définir la récursion : Définir le problème en termes de plus petits sous-problèmes et appeler la fonction pour les résoudre.\n\n- Etape 3 - Définir la fin : S'assurer que la récursion va bien arriver au cas de base pour éviter d'avoir une boucle infinie."},{"metadata":{},"cell_type":"markdown","source":"<h2 class = \"alert alert-success\"> Structure d'un code récursif en python :</h2>"},{"metadata":{},"cell_type":"raw","source":"def fonction(n, ...):\n if n==0 :\n # code pour le cas de base\n else :\n # votre code où apparaîtra fonction(n-1,...)"},{"metadata":{},"cell_type":"markdown","source":"### <h2 class=\"alert alert-info\">2. Fractales : courbe de Koch Correction</h2>\n\nLa courbe de Koch est une fractale reposant sur la construction récursive suviante :\n1. Étape 1 : Tracer un segment de longueur a. \n\n\n\n2. Étape 2 : Diviser le segment en 3 parties de même longueur. Construire un triangle équilatéral ayant pour base le segment médian de la première étape, et en supprimer la base.\n\n\n3. Étape 3 : Reprendre l'étape 2 sur chacun des segments créés.\n\n\n4. Et ainsi de suite...\n"},{"metadata":{},"cell_type":"markdown","source":"On peut construire récursivement cette courbe. \n\nLa fonction de tracer prend deux paramètres en entrée :\n* la longeur $a$ du segment.\n* l'étape $n$ de \"profondeur\" de récursivité. \n\nPar exemple, à la profondeur $n=0$, on trace un simple segment : ceci constituera le cas de base et la condition d'arrêt des appels récursifs. À la profondeur $n=1$, le tracé donne la figure de l'étape 2."},{"metadata":{},"cell_type":"markdown","source":"<h2 class=\"alert alert-warning\">Courbe de Koch : fonction récursive.</h2>\n\nEn vous inspirant de la logique du code de la fonction précédente (en la \"rendant récursive\"), écrire une fonction koch(a, n) récursive qui :\n\n- prend comme paramètres un nombre entier a représentant la longueur du segment et un entier n égal au nombre de récursions souhaité.\n- construit la courbe de Koch en divisant récursivement chacun des segments\n\n*Rappel* : si n=0, le tracé est un simplement segment de longueur a."},{"metadata":{},"cell_type":"markdown","source":"<div class = \"alert alert-block alert-success\"> \n\n### Solution\n</div>"},{"metadata":{"trusted":true},"cell_type":"code","source":"import turtle as tt # import du module turtle\n\ntt.speed(10)\ntt.penup()\ntt.setposition(-300, 0) \ntt.pendown()\n\ndef koch(a, n):\n if n == 0:\n tt.forward(a)\n else:\n koch(a/3, n-1)\n tt.left(60)\n koch(a/3, n-1)\n tt.right(120)\n koch(a/3, n-1)\n tt.left(60)\n koch(a/3, n-1)\n\nkoch(360, 3)\n\ntt.done()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h2 class=\"alert alert-warning\">Flocon de Koch : fonction récursive</h2>\n\n\nPour obtenir le flocon de Koch:\n\n<figure> \n <img title='Koch snowflake' \n src=\"https://isquared.digital/assets/images/koch_snowflake.png\"\n width='600px' >\n <figcaption>Etapes 0, 1, 2, 3 et 4</figcaption>\n</figure>\n\nIl suffit d'écrire une fonction `floconkoch(a,n)` qui va itérer `koch(a,n)` avec la bonne rotation."},{"metadata":{"trusted":true},"cell_type":"code","source":"a = 180\n\ntt.speed(10)\ntt.penup()\ntt.setposition(-a/2, a/3) \ntt.pendown()\n\n\ndef floconkoch(a, n):\n for i in range(3):\n koch(a, n)\n tt.right(120)\n\n# test pour afficher l'étape 3\nflocon(a, 3)\n\ntt.penup()\ntt.home()\n\ntt.done()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### <h2 class=\"alert alert-info\">2. Fractales de Koch et perimètres </h2>\n\n1. Complétez ce code récursif pour donner le perimètre du Flocon de Koch à l'étape n.\n\n*Aidez vous de la figure ci-dessous pour comprendre l'évolution de l'aire entre deux itérations*\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"def perimetreKoch(a,n):\n if n==0 :\n # code pour le cas de base \n return 3*a\n else :\n # votre code où apparaîtra fonction(n-1,...)\n return (4/3)*perimetreKoch(a,n-1)\n\n","execution_count":51,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# test pour a=50, n=3\nperimetreKoch(50,3)","execution_count":52,"outputs":[{"output_type":"execute_result","execution_count":52,"data":{"text/plain":"355.5555555555555"},"metadata":{}}]},{"metadata":{"trusted":true},"cell_type":"code","source":"# test pour a=50, n=100\nperimetreKoch(50,100)","execution_count":53,"outputs":[{"output_type":"execute_result","execution_count":53,"data":{"text/plain":"467697361531188.8"},"metadata":{}}]},{"metadata":{},"cell_type":"markdown","source":"**Question:** Que peut-on déduire du périmètre du Flocon de Koch (lorsque n tend vers l'infini) ?\n\n**Réponse:** "},{"metadata":{},"cell_type":"raw","source":""},{"metadata":{},"cell_type":"markdown","source":"Pour calculer l'aire il est plus facile d'utiliser une boucle avec des variables qui comptes le nombres de triangles qu'on rajoute à chaque étape et la largeur de leur base.\n\n2. Complétez le code de la fonction pour donner l'aire du Flocon de Koch à l'étape n."},{"metadata":{"trusted":true},"cell_type":"code","source":"def aireKoch(a,n):\n A = (3**0.5 / 4 * a**2)\n l = a / 3\n c = 3 # le nombre de carré\n for i in range(n):\n A = A + c * (3**0.5 / 4 * l**2)\n l = l / 3\n c = c * 3\n \n return A","execution_count":55,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# test pour a=50, n=3\naireKoch(50,3)","execution_count":56,"outputs":[{"output_type":"execute_result","execution_count":56,"data":{"text/plain":"1603.7507477489605"},"metadata":{}}]},{"metadata":{"trusted":true},"cell_type":"code","source":"# test pour a=50, n=100\naireKoch(50,100)","execution_count":57,"outputs":[{"output_type":"execute_result","execution_count":57,"data":{"text/plain":"1623.7976320958232"},"metadata":{}}]},{"metadata":{},"cell_type":"markdown","source":"**Question:** Que peut-on déduire de l'aire du Flocon de Koch (lorsque n tend vers l'infini) ?\n\n**Réponse:** "},{"metadata":{"trusted":true},"cell_type":"raw","source":""},{"metadata":{},"cell_type":"markdown","source":"### <h2 class=\"alert alert-info\">3. une autre images fractale: le triangle de Sierpinski</h2>\n\nLa courbe de Koch est une fractale reposant sur la construction récursive suviante :\n\n\n\n1. Écrire une fonction `triangle(a)` qui permet de dessiner un triangle de longeur `a`, puis une fonction `etape2(a)` qui permet de dessiner (pas de récursivité ici) la figure correspondant à l'étape 2 en utilisant la fonction `triangle(a)`.\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"import turtle as tt\n\ndef triangle(a):\n for _ in range(3) :\n tt.forward(a)\n tt.left(120)\n\ndef etape2(a) :\n for _ in range(3) :\n triangle(a/2)\n tt.forward(a)\n tt.left(120)\n\n\netape2(200)\ntt.done()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"2. En vous inspirant de la logique du code de la fonction précédente (en la \"rendant récursive\"), écrire une fonction `triangle_sierpinski(a, n)` récursive qui :\n\n - remplace les appels à triangle par des appels récursifs pour n>=1\n - trace un simple triangle lorsque n=0\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"import turtle as tt\n\n\ndef triangle_sierpinski(a, n) :\n if n > 0 :\n for _ in range(3) :\n triangle_sierpinski(a/2, n-1)\n tt.forward(a)\n tt.left(120)\n\ntt.hideturtle() # on cache la tortue\ntt.speed(0) # tortue rapide\n\ntriangle_sierpinski( 200 , 7)\ntt.done()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### <h2 class=\"alert alert-info\">4. Décomposition d'un entier positif en somme d'au plus quatre carrés</h2>\n\nLe théorème des quatre carrés de Lagrange affirme que tout nombre entier positif `n` peut s'écrire comme la somme d'au plus quatre carrés.\n\nPar exemple, $1871 = 1 + 25 + 81 + 1764 = 1^2 + 5^2 + 9^2 + 42^2$.\n\nPour afficher une possibilité on peut donner l'algorithme suivant qui permet de décomposer un entier positif `n` en une somme d'au plus quatre carrés.\n"},{"metadata":{},"cell_type":"markdown","source":"<div style='background-color: #f7bd83;\n border-radius: 0.5em;\n padding: 1em;\n margin: 0em 2em 0em 2em'>\n\n<p><b>Algorithme de décomposition de l'entier positif <code>n</code> en une somme d'au plus quatre carrés</b></p>\n<p><b>Début</b></p>\n<p STYLE=\"padding:0 0 0 40px;\">Si <code>n</code> est le carré d'un entier alors</p>\n<p STYLE=\"padding:0 0 0 80px;\">Retourner un tableau contenant uniquement l'élément <code>n</code></p>\n <p STYLE=\"padding:0 0 0 40px;\">Sinon</p>\n <p STYLE=\"padding:0 0 0 80px;\"><code>liste_carres</code> ← tableau contenant la liste décroissante des nombres compris entre 1 et <code>n</code> qui sont des carrés d'entiers</p>\n<p STYLE=\"padding:0 0 0 80px;\">Pour chaque élément <code>carre</code> de <code>liste_carres</code> faire</p>\n<p STYLE=\"padding:0 0 0 120px;\"><code>decompo</code> ← liste renvoyé par <code>decomposition_carres(n - carre)</code> auquel on ajoute l'élément <code>carre</code> à la fin</p>\n<p STYLE=\"padding:0 0 0 120px;\">Si longueur(<code>decompo</code>) $\\leq$ 4 alors</p>\n<p STYLE=\"padding:0 0 0 160px;\">Retourner <code>decompo</code></p>\n <p><b>Fin</b></p>\n \n</div>"},{"metadata":{},"cell_type":"markdown","source":"**(1)** 💻 Définir une fonction `est_carre` qui prend en paramètre d'entrée un entier positif `n` et qui renvoie `True` si `n` est le carré d'un entier et `False` sinon."},{"metadata":{"trusted":true},"cell_type":"code","source":"def est_carre(n):\n if round(n**0.5)**2 == n:\n return True\n else:\n return False","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Test\nnb = 9\nprint(nb, \"est-il un carre ? \", est_carre(nb))\nnb = 10\nprint(nb, \"est-il un carre ? \", est_carre(nb))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**(2)** 💻 Définir une fonction `liste_carres_entiers` qui prend en paramètre d'entrée un entier positif `n` et qui renvoie la liste décroissante des entiers compris entre 1 et `n` et qui sont des carrés d'entiers."},{"metadata":{"trusted":true},"cell_type":"code","source":"def liste_carres_entiers(n):\n L = []\n for k in reversed(range(1, n+1)):\n if est_carre(k):\n L.append(k)\n return L","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Test\nlistes_carres = liste_carres_entiers(100)\nprint(listes_carres) # doit afficher [100, 81, 64, 49, 36, 25, 16, 9, 4, 1]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**(3)** 💻 Implémenter la fonction `decomposition_carres` qui prend en paramètre d'entrée un entier positif `n` et qui renvoie, sous forme de tableau de longueur inférieure ou égale à 4, une décomposition de `n` en somme de carrés d'entiers."},{"metadata":{"trusted":true},"cell_type":"code","source":"def decomposition_carres(n):\n if est_carre(n):\n return [n]\n else:\n listes_carres = liste_carres_entiers(n)\n for carre in listes_carres:\n decompo = decomposition_carres(n - carre) + [carre]\n if len(decompo) <= 4:\n return decompo","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Test\nnb = 1871\nprint(\"La décomposition de \",nb, \"est : \", decomposition_carres(nb))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**(4)** ✏️ Donner une décomposition en somme d'au plus quatre carrés pour les entiers $300$, $1789$, $2021$ et $12345$."},{"metadata":{"trusted":true},"cell_type":"code","source":"def print_decompoision(n, decompo):\n s = str(n)+ \" = \"\n for n in decompo:\n s = s + str(int(n**0.5))+\"² + \"\n s = s[:-3]\n print(s)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"nb = 1871\nprint_decompoision(nb, decomposition_carres(nb))\nnb = 300\nprint_decompoision(nb, decomposition_carres(nb))\nnb = 1789\nprint_decompoision(nb, decomposition_carres(nb))\nnb = 2021\nprint_decompoision(nb, decomposition_carres(nb))\nnb = 12345\nprint_decompoision(nb, decomposition_carres(nb))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**(5)** ✏️ Proposer une fonction non récursive `decomposition_carres2(n)` qui permet de calculer la décomposition et tester la fonction"},{"metadata":{"trusted":true},"cell_type":"code","source":"def decomposition_carres2(n):\n listes_carres = liste_carres_entiers(n)\n listes_carres = listes_carres + [0]\n for carre1 in listes_carres:\n for carre2 in listes_carres:\n for carre3 in listes_carres:\n for carre4 in listes_carres:\n if carre1 + carre2 +carre3 +carre4 ==n :\n return [carre1, carre2, carre3, carre4]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"nb = 1871\nprint_decompoision(nb, decomposition_carres2(nb))\nnb = 300\nprint_decompoision(nb, decomposition_carres2(nb))\nnb = 1789\nprint_decompoision(nb, decomposition_carres2(nb))\nnb = 2021\nprint_decompoision(nb, decomposition_carres2(nb))\nnb = 12345\nprint_decompoision(nb, decomposition_carres2(nb))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3.8.1 64-bit ('python38': conda)","language":"python","name":"python38164bitpython38conda56991d5ad1414e06a4dcd344400cf456"}},"nbformat":4,"nbformat_minor":2}
\ No newline at end of file
{"cells":[{"metadata":{},"cell_type":"markdown","source":"<div class = \"alert alert-danger\"> \n \n# Cours avec solution\n \nCliquez sur **Exécuter tout** Dans le menu **Cellule**\n</div>"},{"metadata":{},"cell_type":"markdown","source":"<h1 class=\"alert alert-success\">Complément sur la récursivité en programmation</h2>"},{"metadata":{},"cell_type":"markdown","source":"## <h2 class=\"alert alert-info\"> 1. Introduction à la récursivité</h2>\nUn algorithme récursif est un algorithme qui résout un problème en calculant des solutions d'instances plus petites du même problème. "},{"metadata":{},"cell_type":"markdown","source":"<h2 class=\"alert alert-success\"> Rappel : Algorithme pour implémenter une fonction récursive</h2>\n\n- Etape 1 - Définir le cas de base : Identifier le cas le plus simple pour lequel une solution est connue.\n\n- Etape 2 - Définir la récursion : Définir le problème en termes de plus petits sous-problèmes et appeler la fonction pour les résoudre.\n\n- Etape 3 - Définir la fin : S'assurer que la récursion va bien arriver au cas de base pour éviter d'avoir une boucle infinie."},{"metadata":{},"cell_type":"markdown","source":"<h2 class = \"alert alert-success\"> Structure d'un code récursif en python :</h2>"},{"metadata":{},"cell_type":"raw","source":"def fonction(n, ...):\n if n==0 :\n # code pour le cas de base\n else :\n # votre code où apparaîtra fonction(n-1,...)"},{"metadata":{},"cell_type":"markdown","source":"### <h2 class=\"alert alert-info\">2. Fractales : courbe de Koch Correction</h2>\n\nLa courbe de Koch est une fractale reposant sur la construction récursive suviante :\n1. Étape 1 : Tracer un segment de longueur a. \n\n\n\n2. Étape 2 : Diviser le segment en 3 parties de même longueur. Construire un triangle équilatéral ayant pour base le segment médian de la première étape, et en supprimer la base.\n\n\n3. Étape 3 : Reprendre l'étape 2 sur chacun des segments créés.\n\n\n4. Et ainsi de suite...\n"},{"metadata":{},"cell_type":"markdown","source":"On peut construire récursivement cette courbe. \n\nLa fonction de tracer prend deux paramètres en entrée :\n* la longeur $a$ du segment.\n* l'étape $n$ de \"profondeur\" de récursivité. \n\nPar exemple, à la profondeur $n=0$, on trace un simple segment : ceci constituera le cas de base et la condition d'arrêt des appels récursifs. À la profondeur $n=1$, le tracé donne la figure de l'étape 2."},{"metadata":{},"cell_type":"markdown","source":"<h2 class=\"alert alert-warning\">Courbe de Koch : fonction récursive.</h2>\n\nEn vous inspirant de la logique du code de la fonction précédente (en la \"rendant récursive\"), écrire une fonction koch(a, n) récursive qui :\n\n- prend comme paramètres un nombre entier a représentant la longueur du segment et un entier n égal au nombre de récursions souhaité.\n- construit la courbe de Koch en divisant récursivement chacun des segments\n\n*Rappel* : si n=0, le tracé est un simplement segment de longueur a."},{"metadata":{},"cell_type":"markdown","source":"<div class = \"alert alert-block alert-success\"> \n\n### Solution\n</div>"},{"metadata":{"trusted":true},"cell_type":"code","source":"import turtle as tt # import du module turtle\n\ntt.speed(10)\ntt.penup()\ntt.setposition(-300, 0) \ntt.pendown()\n\ndef koch(a, n):\n if n == 0:\n tt.forward(a)\n else:\n koch(a/3, n-1)\n tt.left(60)\n koch(a/3, n-1)\n tt.right(120)\n koch(a/3, n-1)\n tt.left(60)\n koch(a/3, n-1)\n\nkoch(360, 3)\n\ntt.done()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h2 class=\"alert alert-warning\">Flocon de Koch : fonction récursive</h2>\n\n\nPour obtenir le flocon de Koch:\n\n<figure> \n <img title='Koch snowflake' \n src=\"https://isquared.digital/assets/images/koch_snowflake.png\"\n width='600px' >\n <figcaption>Etapes 0, 1, 2, 3 et 4</figcaption>\n</figure>\n\nIl suffit d'écrire une fonction `floconkoch(a,n)` qui va itérer `koch(a,n)` avec la bonne rotation."},{"metadata":{"trusted":true},"cell_type":"code","source":"import turtle as tt # import du module turtle\n\na = 180\n\ntt.speed(10)\ntt.penup()\ntt.setposition(-a/2, a/3) \ntt.pendown()\n\n\ndef flocon(a, n):\n for i in range(3):\n koch(a, n)\n tt.right(120)\n\n# test pour afficher l'étape 3\nflocon(a, 3)\n\ntt.penup()\ntt.home()\n\ntt.done()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### <h2 class=\"alert alert-info\">2. Fractales de Koch et perimètres </h2>\n\n1. Complétez ce code récursif pour donner le perimètre du Flocon de Koch à l'étape n.\n\n*Aidez vous de la figure ci-dessous pour comprendre l'évolution de l'aire entre deux itérations*\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"def perimetreKoch(a,n):\n if n==0 :\n return 3*a\n \n else :\n return (4/3)*perimetreKoch(a,n-1) # P(n) = (4/3)P(n-1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# test pour a=50, n=3\nperimetreKoch(50,3)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# test pour a=50, n=100\nperimetreKoch(50,100)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def perimetreKoch2(a,n):\n return (4/3)**n * 3*a ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"On utilise la relation $P_n = \\left(\\frac{4}{3}\\right)^n P_0$"},{"metadata":{"trusted":true},"cell_type":"code","source":"# test pour a=50, n=3\nperimetreKoch2(50,3)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Question:** Que peut-on déduire du périmètre du Flocon de Koch (lorsque n tend vers l'infini) ?\n\n**Réponse:** "},{"metadata":{},"cell_type":"raw","source":"Son périmètre tend aussi vers l'infini."},{"metadata":{},"cell_type":"markdown","source":"Pour calculer l'aire il est plus facile d'utiliser une boucle avec des variables qui comptes le nombres de triangles qu'on rajoute à chaque étape et la largeur de leur base.\n\n2. Complétez le code de la fonction pour donner l'aire du Flocon de Koch à l'étape n."},{"metadata":{"trusted":true},"cell_type":"code","source":"def aireKoch(a,n):\n A0 = (3**0.5 /4) * a**2 #l'aire des triangles qu'on rajoute\n A = A0\n c = 3 # le nombre de triangles qu'on rajoute\n for i in range(n):\n A0 = 1/9*A0\n A = A + c*A0\n c = 3*c\n \n return A","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# test pour a=50, n=3\naireKoch(50,3)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# test pour a=50, n=100\naireKoch(50,100)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# test pour a=50, n=500\naireKoch(50,500)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Question:** Que peut-on déduire de l'aire du Flocon de Koch (lorsque n tend vers l'infini) ?\n\n**Réponse:** "},{"metadata":{"trusted":true},"cell_type":"raw","source":"On à l'aire qui converge vers 1623,80..."},{"metadata":{},"cell_type":"markdown","source":"Mathématiquement (voir les calculs sur moodle), on peut montrer que l'aire tend vers $A=\\frac{3 \\sqrt{3}}{8}a^2$.\n\nIci si $a=50$, on a $A=\\frac{3 \\sqrt{3}}{8}50^2\\approx 1623,80$"},{"metadata":{},"cell_type":"markdown","source":"### <h2 class=\"alert alert-info\">3. une autre images fractale: le triangle de Sierpinski</h2>\n\nLa courbe de Koch est une fractale reposant sur la construction récursive suviante :\n\n\n\n1. Écrire une fonction `triangle(a)` qui permet de dessiner un triangle de longeur `a`, puis une fonction `etape2(a)` qui permet de dessiner (pas de récursivité ici) la figure correspondant à l'étape 2 en utilisant la fonction `triangle(a)`.\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"import turtle as tt\n\ndef triangle(a):\n for _ in range(3) :\n tt.forward(a)\n tt.left(120)\n\ndef etape2(a) :\n for _ in range(3) :\n triangle(a/2)\n tt.forward(a)\n tt.left(120)\n\n\netape2(200)\ntt.done()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"2. En vous inspirant de la logique du code de la fonction précédente (en la \"rendant récursive\"), écrire une fonction `triangle_sierpinski(a, n)` récursive qui :\n\n - remplace les appels à triangle par des appels récursifs pour n>=1\n - trace un simple triangle lorsque n=0\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"import turtle as tt\n\n\ndef triangle_sierpinski(a, n) :\n if n > 0 :\n for _ in range(3) :\n triangle_sierpinski(a/2, n-1)\n tt.forward(a)\n tt.left(120)\n\ntt.hideturtle() # on cache la tortue\ntt.speed(0) # tortue rapide\n\ntriangle_sierpinski( 200 , 7)\ntt.done()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### <h2 class=\"alert alert-info\">4. Décomposition d'un entier positif en somme d'au plus quatre carrés</h2>\n\nLe théorème des quatre carrés de Lagrange affirme que tout nombre entier positif `n` peut s'écrire comme la somme d'au plus quatre carrés.\n\nPar exemple, $1871 = 1 + 25 + 81 + 1764 = 1^2 + 5^2 + 9^2 + 42^2$.\n\nPour afficher une possibilité on peut donner l'algorithme suivant qui permet de décomposer un entier positif `n` en une somme d'au plus quatre carrés.\n"},{"metadata":{},"cell_type":"markdown","source":"<div style='background-color: #f7bd83;\n border-radius: 0.5em;\n padding: 1em;\n margin: 0em 2em 0em 2em'>\n\n<p><b>Algorithme de décomposition de l'entier positif <code>n</code> en une somme d'au plus quatre carrés</b></p>\n<p><b>Début</b></p>\n<p STYLE=\"padding:0 0 0 40px;\">Si <code>n</code> est le carré d'un entier alors</p>\n<p STYLE=\"padding:0 0 0 80px;\">Retourner un tableau contenant uniquement l'élément <code>n</code></p>\n <p STYLE=\"padding:0 0 0 40px;\">Sinon</p>\n <p STYLE=\"padding:0 0 0 80px;\"><code>liste_carres</code> ← tableau contenant la liste décroissante des nombres compris entre 1 et <code>n</code> qui sont des carrés d'entiers</p>\n<p STYLE=\"padding:0 0 0 80px;\">Pour chaque élément <code>carre</code> de <code>liste_carres</code> faire</p>\n<p STYLE=\"padding:0 0 0 120px;\"><code>decompo</code> ← liste renvoyé par <code>decomposition_carres(n - carre)</code> auquel on ajoute l'élément <code>carre</code> à la fin</p>\n<p STYLE=\"padding:0 0 0 120px;\">Si longueur(<code>decompo</code>) $\\leq$ 4 alors</p>\n<p STYLE=\"padding:0 0 0 160px;\">Retourner <code>decompo</code></p>\n <p><b>Fin</b></p>\n \n</div>"},{"metadata":{},"cell_type":"markdown","source":"**(1)** 💻 Définir une fonction `est_carre` qui prend en paramètre d'entrée un entier positif `n` et qui renvoie `True` si `n` est le carré d'un entier et `False` sinon."},{"metadata":{"trusted":true},"cell_type":"code","source":"def est_carre(n):\n if round(n**0.5)**2 == n:\n return True\n else:\n return False","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Test\nnb = 9\nprint(nb, \"est-il un carre ? \", est_carre(nb))\nnb = 10\nprint(nb, \"est-il un carre ? \", est_carre(nb))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**(2)** 💻 Définir une fonction `liste_carres_entiers` qui prend en paramètre d'entrée un entier positif `n` et qui renvoie la liste décroissante des entiers compris entre 1 et `n` et qui sont des carrés d'entiers."},{"metadata":{"trusted":true},"cell_type":"code","source":"def liste_carres_entiers(n):\n L = []\n for k in reversed(range(1, n+1)):\n if est_carre(k):\n L.append(k)\n return L","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Test\nlistes_carres = liste_carres_entiers(100)\nprint(listes_carres) # doit afficher [100, 81, 64, 49, 36, 25, 16, 9, 4, 1]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**(3)** 💻 Implémenter la fonction `decomposition_carres` qui prend en paramètre d'entrée un entier positif `n` et qui renvoie, sous forme de tableau de longueur inférieure ou égale à 4, une décomposition de `n` en somme de carrés d'entiers."},{"metadata":{"trusted":true},"cell_type":"code","source":"def decomposition_carres(n):\n if est_carre(n):\n return [n]\n else:\n listes_carres = liste_carres_entiers(n)\n for carre in listes_carres:\n decompo = decomposition_carres(n - carre) + [carre]\n if len(decompo) <= 4:\n return decompo","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Test\nnb = 1871\nprint(\"La décomposition de \",nb, \"est : \", decomposition_carres(nb))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**(4)** ✏️ Donner une décomposition en somme d'au plus quatre carrés pour les entiers $300$, $1789$, $2021$ et $12345$."},{"metadata":{"trusted":true},"cell_type":"code","source":"def print_decompoision(n, decompo):\n s = str(n)+ \" = \"\n for n in decompo:\n s = s + str(int(n**0.5))+\"² + \"\n s = s[:-3]\n print(s)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"nb = 1871\nprint_decompoision(nb, decomposition_carres(nb))\nnb = 300\nprint_decompoision(nb, decomposition_carres(nb))\nnb = 1789\nprint_decompoision(nb, decomposition_carres(nb))\nnb = 2021\nprint_decompoision(nb, decomposition_carres(nb))\nnb = 12345\nprint_decompoision(nb, decomposition_carres(nb))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**(5)** ✏️ Proposer une fonction non récursive `decomposition_carres2(n)` qui permet de calculer la décomposition et tester la fonction"},{"metadata":{"trusted":true},"cell_type":"code","source":"def decomposition_carres2(n):\n listes_carres = liste_carres_entiers(n)\n listes_carres = listes_carres + [0]\n for carre1 in listes_carres:\n for carre2 in listes_carres:\n for carre3 in listes_carres:\n for carre4 in listes_carres:\n if carre1 + carre2 +carre3 +carre4 ==n :\n return [carre1, carre2, carre3, carre4]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"nb = 1871\nprint_decompoision(nb, decomposition_carres2(nb))\nnb = 300\nprint_decompoision(nb, decomposition_carres2(nb))\nnb = 1789\nprint_decompoision(nb, decomposition_carres2(nb))\nnb = 2021\nprint_decompoision(nb, decomposition_carres2(nb))\nnb = 12345\nprint_decompoision(nb, decomposition_carres2(nb))","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3.8.1 64-bit ('python38': conda)","language":"python","name":"python38164bitpython38conda56991d5ad1414e06a4dcd344400cf456"}},"nbformat":4,"nbformat_minor":2}
\ No newline at end of file
%% Cell type:markdown id: tags:
<divclass = "alert alert-danger">
# Cours avec solution
Cliquez sur **Exécuter tout** Dans le menu **Cellule**
</div>
%% Cell type:markdown id: tags:
<h1class="alert alert-success">Complément sur la récursivité en programmation</h2>
%% Cell type:markdown id: tags:
## <h2 class="alert alert-info"> 1. Introduction à la récursivité</h2>
Un algorithme récursif est un algorithme qui résout un problème en calculant des solutions d'instances plus petites du même problème.
%% Cell type:markdown id: tags:
<h2class="alert alert-success"> Rappel : Algorithme pour implémenter une fonction récursive</h2>
- Etape 1 - Définir le cas de base : Identifier le cas le plus simple pour lequel une solution est connue.
- Etape 2 - Définir la récursion : Définir le problème en termes de plus petits sous-problèmes et appeler la fonction pour les résoudre.
- Etape 3 - Définir la fin : S'assurer que la récursion va bien arriver au cas de base pour éviter d'avoir une boucle infinie.
%% Cell type:markdown id: tags:
<h2class = "alert alert-success"> Structure d'un code récursif en python :</h2>
%% Cell type:raw id: tags:
def fonction(n, ...):
if n==0 :
# code pour le cas de base
else :
# votre code où apparaîtra fonction(n-1,...)
%% Cell type:markdown id: tags:
### <h2 class="alert alert-info">2. Fractales : courbe de Koch Correction</h2>
La courbe de Koch est une fractale reposant sur la construction récursive suviante :
1. Étape 1 : Tracer un segment de longueur a.

2. Étape 2 : Diviser le segment en 3 parties de même longueur. Construire un triangle équilatéral ayant pour base le segment médian de la première étape, et en supprimer la base.

3. Étape 3 : Reprendre l'étape 2 sur chacun des segments créés.

4. Et ainsi de suite...

%% Cell type:markdown id: tags:
On peut construire récursivement cette courbe.
La fonction de tracer prend deux paramètres en entrée :
* la longeur $a$ du segment.
* l'étape $n$ de "profondeur" de récursivité.
Par exemple, à la profondeur $n=0$, on trace un simple segment : ceci constituera le cas de base et la condition d'arrêt des appels récursifs. À la profondeur $n=1$, le tracé donne la figure de l'étape 2.
%% Cell type:markdown id: tags:
<h2class="alert alert-warning">Courbe de Koch : fonction récursive.</h2>
En vous inspirant de la logique du code de la fonction précédente (en la "rendant récursive"), écrire une fonction koch(a, n) récursive qui :
- prend comme paramètres un nombre entier a représentant la longueur du segment et un entier n égal au nombre de récursions souhaité.
- construit la courbe de Koch en divisant récursivement chacun des segments
*Rappel* : si n=0, le tracé est un simplement segment de longueur a.
%% Cell type:markdown id: tags:
<divclass = "alert alert-block alert-success">
### Solution
</div>
%% Cell type:code id: tags:
``` python
importturtleastt# import du module turtle
tt.speed(10)
tt.penup()
tt.setposition(-300,0)
tt.pendown()
defkoch(a,n):
ifn==0:
tt.forward(a)
else:
koch(a/3,n-1)
tt.left(60)
koch(a/3,n-1)
tt.right(120)
koch(a/3,n-1)
tt.left(60)
koch(a/3,n-1)
koch(360,3)
tt.done()
```
%% Cell type:markdown id: tags:
<h2class="alert alert-warning">Flocon de Koch : fonction récursive</h2>
On utilise la relation $P_n = \left(\frac{4}{3}\right)^n P_0$
%% Cell type:code id: tags:
467697361531188.8
``` python
# test pour a=50, n=3
perimetreKoch2(50,3)
```
%% Cell type:markdown id: tags:
**Question:** Que peut-on déduire du périmètre du Flocon de Koch (lorsque n tend vers l'infini) ?
**Réponse:**
%% Cell type:raw id: tags:
Son périmètre tend aussi vers l'infini.
%% Cell type:markdown id: tags:
Pour calculer l'aire il est plus facile d'utiliser une boucle avec des variables qui comptes le nombres de triangles qu'on rajoute à chaque étape et la largeur de leur base.
2. Complétez le code de la fonction pour donner l'aire du Flocon de Koch à l'étape n.
%% Cell type:code id: tags:
``` python
defaireKoch(a,n):
A=(3**0.5/4*a**2)
l=a/3
c=3# le nombre de carré
A0=(3**0.5/4)*a**2#l'aire des triangles qu'on rajoute
A=A0
c=3# le nombre de triangles qu'on rajoute
foriinrange(n):
A=A+c*(3**0.5/4*l**2)
l=l/3
c=c*3
A0=1/9*A0
A=A+c*A0
c=3*c
returnA
```
%% Cell type:code id: tags:
``` python
# test pour a=50, n=3
aireKoch(50,3)
```
%% Output
1603.7507477489605
%% Cell type:code id: tags:
``` python
# test pour a=50, n=100
aireKoch(50,100)
```
%% Output
%% Cell type:code id: tags:
1623.7976320958232
``` python
# test pour a=50, n=500
aireKoch(50,500)
```
%% Cell type:markdown id: tags:
**Question:** Que peut-on déduire de l'aire du Flocon de Koch (lorsque n tend vers l'infini) ?
**Réponse:**
%% Cell type:raw id: tags:
On à l'aire qui converge vers 1623,80...
%% Cell type:markdown id: tags:
Mathématiquement (voir les calculs sur moodle), on peut montrer que l'aire tend vers $A=\frac{3 \sqrt{3}}{8}a^2$.
Ici si $a=50$, on a $A=\frac{3 \sqrt{3}}{8}50^2\approx 1623,80$
%% Cell type:markdown id: tags:
### <h2 class="alert alert-info">3. une autre images fractale: le triangle de Sierpinski</h2>
La courbe de Koch est une fractale reposant sur la construction récursive suviante :

1. Écrire une fonction `triangle(a)` qui permet de dessiner un triangle de longeur `a`, puis une fonction `etape2(a)` qui permet de dessiner (pas de récursivité ici) la figure correspondant à l'étape 2 en utilisant la fonction `triangle(a)`.
%% Cell type:code id: tags:
``` python
importturtleastt
deftriangle(a):
for_inrange(3):
tt.forward(a)
tt.left(120)
defetape2(a):
for_inrange(3):
triangle(a/2)
tt.forward(a)
tt.left(120)
etape2(200)
tt.done()
```
%% Cell type:markdown id: tags:
2. En vous inspirant de la logique du code de la fonction précédente (en la "rendant récursive"), écrire une fonction `triangle_sierpinski(a, n)` récursive qui :
- remplace les appels à triangle par des appels récursifs pour n>=1
- trace un simple triangle lorsque n=0
%% Cell type:code id: tags:
``` python
importturtleastt
deftriangle_sierpinski(a,n):
ifn>0:
for_inrange(3):
triangle_sierpinski(a/2,n-1)
tt.forward(a)
tt.left(120)
tt.hideturtle()# on cache la tortue
tt.speed(0)# tortue rapide
triangle_sierpinski(200,7)
tt.done()
```
%% Cell type:markdown id: tags:
### <h2 class="alert alert-info">4. Décomposition d'un entier positif en somme d'au plus quatre carrés</h2>
Le théorème des quatre carrés de Lagrange affirme que tout nombre entier positif `n` peut s'écrire comme la somme d'au plus quatre carrés.
Par exemple, $1871 = 1 + 25 + 81 + 1764 = 1^2 + 5^2 + 9^2 + 42^2$.
Pour afficher une possibilité on peut donner l'algorithme suivant qui permet de décomposer un entier positif `n` en une somme d'au plus quatre carrés.
%% Cell type:markdown id: tags:
<div style='background-color: #f7bd83;
border-radius: 0.5em;
padding: 1em;
margin: 0em 2em 0em 2em'>
<p><b>Algorithme de décomposition de l'entier positif <code>n</code> en une somme d'au plus quatre carrés</b></p>
<p><b>Début</b></p>
<pSTYLE="padding:0 0 0 40px;">Si <code>n</code> est le carré d'un entier alors</p>
<pSTYLE="padding:0 0 0 80px;">Retourner un tableau contenant uniquement l'élément <code>n</code></p>
<pSTYLE="padding:0 0 0 40px;">Sinon</p>
<pSTYLE="padding:0 0 0 80px;"><code>liste_carres</code> ← tableau contenant la liste décroissante des nombres compris entre 1 et <code>n</code> qui sont des carrés d'entiers</p>
<pSTYLE="padding:0 0 0 80px;">Pour chaque élément <code>carre</code> de <code>liste_carres</code> faire</p>
<pSTYLE="padding:0 0 0 120px;"><code>decompo</code> ← liste renvoyé par <code>decomposition_carres(n - carre)</code> auquel on ajoute l'élément <code>carre</code> à la fin</p>
**(1)** 💻 Définir une fonction `est_carre` qui prend en paramètre d'entrée un entier positif `n` et qui renvoie `True` si `n` est le carré d'un entier et `False` sinon.
%% Cell type:code id: tags:
``` python
defest_carre(n):
ifround(n**0.5)**2==n:
returnTrue
else:
returnFalse
```
%% Cell type:code id: tags:
``` python
# Test
nb=9
print(nb,"est-il un carre ? ",est_carre(nb))
nb=10
print(nb,"est-il un carre ? ",est_carre(nb))
```
%% Cell type:markdown id: tags:
**(2)** 💻 Définir une fonction `liste_carres_entiers` qui prend en paramètre d'entrée un entier positif `n` et qui renvoie la liste décroissante des entiers compris entre 1 et `n` et qui sont des carrés d'entiers.
**(3)** 💻 Implémenter la fonction `decomposition_carres` qui prend en paramètre d'entrée un entier positif `n` et qui renvoie, sous forme de tableau de longueur inférieure ou égale à 4, une décomposition de `n` en somme de carrés d'entiers.
%% Cell type:code id: tags:
``` python
defdecomposition_carres(n):
ifest_carre(n):
return[n]
else:
listes_carres=liste_carres_entiers(n)
forcarreinlistes_carres:
decompo=decomposition_carres(n-carre)+[carre]
iflen(decompo)<=4:
returndecompo
```
%% Cell type:code id: tags:
``` python
# Test
nb=1871
print("La décomposition de ",nb,"est : ",decomposition_carres(nb))
```
%% Cell type:markdown id: tags:
**(4)** ✏️ Donner une décomposition en somme d'au plus quatre carrés pour les entiers $300$, $1789$, $2021$ et $12345$.
%% Cell type:code id: tags:
``` python
defprint_decompoision(n,decompo):
s=str(n)+" = "
fornindecompo:
s=s+str(int(n**0.5))+"² + "
s=s[:-3]
print(s)
```
%% Cell type:code id: tags:
``` python
nb=1871
print_decompoision(nb,decomposition_carres(nb))
nb=300
print_decompoision(nb,decomposition_carres(nb))
nb=1789
print_decompoision(nb,decomposition_carres(nb))
nb=2021
print_decompoision(nb,decomposition_carres(nb))
nb=12345
print_decompoision(nb,decomposition_carres(nb))
```
%% Cell type:markdown id: tags:
**(5)** ✏️ Proposer une fonction non récursive `decomposition_carres2(n)` qui permet de calculer la décomposition et tester la fonction