T.P. 1 : Fractales en Python. Complexes / réels, Itératif / Récursif. Introduction A la fin de ce T.P., si tout va bien, vous saurez obtenir des dessins de ce genre avec Python : 1.0 3.0 0.8 2.5 0.6 2.0 0.4 1.5 0.2 1.0 0.0 0.5 0.2 0.4 0.0 0.5 0.0 1.0 1.0 0.5 0.0 0.5 1.0 1.5 2.0 Mais qu’importe le trésor, flocon ou chou, l’essentiel est la quête. Les vrais buts de ce T.P. sont en fait les suivants : ● Rafraı̂chir vos connaissance sur la gestion des tableaux numpy qui peuvent ensuite être affichés via matplotlib.pyplot. Pour ceux et celles qui ont fait du SciLab, le gros module pylab est un module python qui simplifie beaucoup l’utilisation de Python à la manière de SciLab en incluant directement aussi bien les fonctions de numpy que de matplotlib. ● Comparer les points de vue : utilisation des transformations complexes ou des matrices 2 × 2 pour la gestion d’opérations géométriques dans le plan. ● Comparer les points de vue : algorithme itératif (boucles vues en premières années) et algorithme récursif : notion qui n’était pas au programme d’I.P.T. en première année, et que nous reprendrons en cours. 1 1.1 La courbe de Von Koch, en coordonnées complexes, et en itératif Construction mathématique de la courbe de Von Koch L’idée de base de la construction d’une courbe de Von Koch, sur le dessin suivant à partir du segment [A, B] ⊂ C avec A d’affixe 0 et B d’affixe 1, est de transformer ce segment en la ligne brisée suivante où C est d’affixe 1/3, E d’affixe 2/3 et CDE est équilatéral : 1 Ensuite, on itère cette opération sur chacun des quatre segments de la figure précédente, ce qui donne : 0.5 0.4 0.3 0.2 0.1 0.0 0.1 0.2 0.0 0.2 0.4 0.6 0.8 1.0 On peut alors recommencer autant de fois qu’on veut, par exemple après cinq itérations : 0.5 0.4 0.3 0.2 0.1 0.0 0.1 0.2 0.0 1.2 0.2 0.4 0.6 0.8 1.0 Rappels de Python avec pylab Dans toute la suite, nous considérons que nous avons rentré la commande : 2 import pylab as pl Le gros module pylab contient numpy, matplotlib (mais pas scipy). Néanmoins, à cause de l’importance de numpy en lui-même et pour respecter les habitudes, on importera aussi numpy via import numpy as np pour invoquer les fonctions numpy avec le préfixe np. 1.2.1 Comment manipuler des nombres complexes en Python ? Il y a au moins deux façons de rentrer un nombre complexe en Python : >>>z=1+2j >>>z=complex(1,2) En pratique l’entrée sous forme a+bj n’est pas très commode (syntaxe pénible : coller la partie imaginaire à j ...), et on recommande plutôt la commande complex. Pour un nombre complexe z, avec z.real et z.imag on aura ce que vous pensez. Remarque : Le help(complex) ne vous renseignera que si vous savez déjà des choses sur les classes en Python... nous en reparlerons. 1.2.2 Comment afficher des points reliés par des segments ? plot, exemple : Pour tracer des points reliés par des segments, on va utiliser la commande pl.plot, à laquelle on doit donner à manger deux tableaux : l’un sera la liste des abscisses des points successifs, l’autre celle des ordonnées des points successifs. Voici un : Exemple très simple : tracé du segment [A, B] où A = (1, 2) et B = (3, 4). X=[1,3] Y=[2,4] pl.plot(X,Y,color="blue") # color est un argument optionnel... pl.show() # provoque l’affichage si vous ^ etes chanceux pl.savefig(’Desktop/segment.pdf’) # enregistre dans un pdf. : marche mieux Quelques fonctions graphiques utiles : pl.axis(’equal’) # pour travailler en repère orthonormé pl.clf() # pour effacer la figure courante. 1.2.3 Les tableaux qu’on peut donner à manger à plot Outre les listes du Python de base, plot gère les array de numpy. Ces array sont gérés par l’importation de pylab donc on peut taper : X=pl.array([1,3]) Y=pl.array([2,4]) pl.plot(X,Y) On peut aussi, si on a l’habitude d’importer numpy faire import numpy as np et déclarer des np.array. L’avantage des tableaux numpy, pl.array ou np.array comme on veut, vient des opérations qu’on peut faire sur ces tableaux : on peut appliquer des fonctions mathématiques (fonctions numpy) à un tableau numpy. Un np.array peut être réduit à une ligne comme A=np.array([1,2]) Il peut être formé de plusieurs lignes comme M=np.array([[1,2],[3,4]]) On obtient un joli affichage avec print(A) et print(M). 3 1.2.4 Tracé à partir d’un tableau de nombres complexes On reprend l’exemple du 1.2.2 où cette fois on a stocké A et B dans un tableau contenant deux complexes. A=complex(1,2) B=complex(3,4) L=np.array([A,B]) Cette fois pour faire le même tracé, nous devons d’abord fabriquer des tableaux d’abscisses et d’ordonnées et là, on voit la puissance de numpy. X=np.real(L) Y=np.imag(L) pl.clf() # clf pour clearfigure pl.plot(X,Y) pl.savefig(’Desktop/segment2.pdf’) 1.3 1.3.1 A vous de jouer ! Fonction transformant un segment Ecrire une fonction TSC (pour Transformation Segment Complexe), qui prend en argument deux complexes A et B, et renvoie la liste formée des complexes A,C,D,E de la construction de la courbe de Von Koch donnée au § 1.1. Indications : ● Les coordonnées complexes de C, D, E sont relatives à celles de A et B. ● Une méthode simple, qui fait l’intérêt des nombres complexes, est de considérer que le segment [C,D] s’obtient par rotation d’angle π/3 (utiliser np.pi), qui se code bien en complexes. ● On pourra vérifier le résultat en le plottant comme indiqué dans les paragraphes précédents. 1.3.2 Fonction donnant une transformation globale Ecrire une fonction TGC (pour Transformation Globale Complexe) qui prend en argument une liste L de nombres complexes et qui retourne une liste qu’on appellera LT dans la fonction, qui est la liste obtenue à partir de L en intercalant entre deux nombres successifs A,B de la liste, les nombres C,D,E de la construction de Von Koch. 1.3.3 Fonction d’affichage avec choix du nombre d’itérations Ecrire une fonction VonKochIC (pour Von Koch par Itération en coordonnées Complexes) qui prend comme argument obligatoire un entier n et comme argument facultatif une liste L de nombres complexes, avec la valeur par défaut L=[0,1] et qui affiche la courbe de Von Koch obtenue en n itérations à partir du segment L grâce à la fonction TGC précédente. 1.4 Passage de la courbe de Von Koch au triangle de Von Koch En appliquant la fonction VonKochIC précédente aux trois côtés d’un triangle équilatéral, obtenir le flocon de neige de l’introduction. 2 Le même travail avec des coordonnées réelles 2.1 Mémo : La multiplication matricielle avec numpy En numpy, pour coder la matrice R de la rotation vectorielle d’angle α, on donne : R=np.array([[np.cos(alpha),-np.sin(alpha)],[np.sin(alpha),np.cos(alpha)]]) 4 Une façon de faire la multiplication matricielle M.N, si M et N sont des np.array de taille compatible : np.dot(M,N) Attention : Ne pas faire M ∗ N . Cette fois, les points A et B de départ seront codés par des matrices colonnes 2 × 1 autrement dit, par exemple : A=np.array([[0],[0]]) 2.2 2.2.1 A vous de jouer : Même départ Ecrire une fonction TSR (pour Transformation Segment Réelle) faisant la même chose que TSC mais en coordonnées réelles et de même une fonction TGR analogue à TGC. 2.2.2 Une petite gymnastique supplémentaire nécessaire pour l’affichage Ecrire une fonction Affichage qui prend en argument une liste L=[A1,...,An] dont les éléments Ai sont chacun des np.array représentant des vecteurs colonnes 2×1 et affiche (éventuellement dans un fichier pdf) la ligne brisée [A1 , A2 ] ∪ ⋅ ⋅ ⋅ ∪ [An−1 , An ]. 2.2.3 Même fin Ecrire alors une fonction VonKochIR avec les mêmes spécifications que VonKochIC sauf que la liste par défaut est une liste de points de R2 . 3 Le point de vue récursif sur Von Koch réel : def KochRec(n,A=np.array([[0],[0]]),B=np.array([[1],[0]])): alpha=np.pi/3 R=np.array([[np.cos(alpha),-np.sin(alpha)], [np.sin(alpha),np.cos(alpha)]]) if n==0: pl.plot([A[0],B[0]],[A[1],B[1]]) pl.axis(’equal’) pl.savefig(’Desktop/essai-VonKoch.pdf’) else : Ecart=(B-A)/3 C=A+Ecart E=B-Ecart D=C+np.dot(R,Ecart) KochRec(n-1,A,C) KochRec(n-1,C,D) KochRec(n-1,D,E) KochRec(n-1,E,B) Que fait ce code ? exécutez-le et appelez cette fonction avec de petites valeurs de n. Que pensez vous du résultat ? 5 4 Le Brocoli en récursif On se donne quatre points A,B,C,D qui sont les quatre sommets d’un carré. Ecrire une fonction récursive appelée Brocoli qui prend ces quatre points en argument, et un argument n et qui effectue les taches suivantes : D’abord la fonction trace le carré A,B,C,D. Ensuite si n > 0 la fonction définit les points E,F,G,I,H déduit de A,B,C,D pour former la figure ̂ vaut π/6 et appelle : suivante (où A,B,C,D sont les sommets du gros carré) où l’angle α = CDE brocoli(D,E,G,F,n-1) brocoli(E,C,I,H,n-1) Appelez cette fonction pour n = 6 et vous obtiendrez la seconde figure de l’introduction. 6