En SNT [1], des élèves de Seconde ont été chargés de reproduire les deux dernières figures de l’article de Sierpiński titré « Sur une courbe dont tout point est un point de ramification » (1915). Ils l’ont fait au crayon sur papier pointé, ou à l’aide du module turtle de Python.
L’une des courbes fractales les plus connues est celle que Wacław Sierpiński a publiée en 1915 sous le titre « sur une courbe dont tout point est un point de ramification ». Cette courbe est surtout connue sous le nom de triangle de Sierpiński. Or ce n’est pas un triangle (surface), mais bien une courbe.
Pour s’en convaincre, on peut dessiner la courbe avec une « tortue ». Ou plus précisément, une approximation polygonale à 3n côtés. Ce fut fait en Seconde (en Sciences du Numérique et Technologie) durant l’année scolaire 2021-2022.
Cet article est mis à disposition selon les termes de la Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 2.0 France.
Documents
En plus de la construction par triangles enlevés, Sierpiński a montré une approximation par polygones permettant de mieux voir qu’il s’agit d’une courbe.
Sierpiński
Voici l’article original de Sierpiński, publié en 1915 (en pleine première guerre mondiale) dans les Comptes rendus de l’Académie des Sciences :
Ce sont les figures 3 à 6 qui montrent ces mystérieux polygones. Mystérieux, car Sierpiński n’explique pas comment on passe d’une étape à la suivante. Or, Sierpiński a par la suite été cryptographe, d’où le titre de l’activité présentée en Seconde : on imagine un code (secret) permettant de construire les polygones...
Sujet du TP
Le sujet suivant a été donné à faire durant presque toute l’année scolaire. Toutes les deux semaines (sauf jour férié, interruption sur l’orientation ou septaine) j’avais la classe en groupes (de 17 ou 18 élèves tout de même) de telle manière que chaque élève était devant un ordinateur avec Python installé, voire sa calculatrice Numworks avec interpréteur Python.
Voici le sujet :
Les élèves qui arrivent en Seconde sont théoriquement déjà familiarisés avec le paradigme de la tortue, qui est à l’origine de Scratch, utilisé au moins en Troisième pour la préparation du DNB. Le document qui leur a été distribué comporte néanmoins une partie consacrée à l’apprentissage de la programmation de la tortue (en Python), suivant ce plan :
- Instructions (de rotation et déplacement pour la tortue)
- Variables (application : le côté des polygones)
- Fonctions (dessins de parties des polygones, pour éviter de faire plusieurs fois la même chose)
- Boucles (vues entre temps dans le concours algorea)
- Parcours d’un texte lettre par lettre
Souvent, le fait d’avoir un problème à résoudre incite les élèves à se servir des tests, boucles et définitions de fonctions de manière active : d’habitude une fois que j’ai expliqué comment fonctionnent ces subtilités, des élèves me demandent à quoi ça sert, et là c’est l’inverse : les élèves, au moment où ils ont quelque chose de répétitif à faire, me demandent comment on rédige une boucle en Python. Les notions qui sont au programme sont ainsi introduites pour répondre à des besoins.
Avant de voir comment on dessine la courbe de Sierpiński en Python, il faut déjà voir comment on programme en Python. Pour cela, programmer les déplacements d’un robot aide les élèves à s’impliquer. Et les plus rapides peuvent créer des œuvres d’art graphique.
Exemples
Dans les onglets suivants, on présente quelques œuvres d’élèves (en début de Seconde) avec les scripts Python qui les dessinent.
Rosace
Un grand classique de la tortue Python :
- from turtle import *
- reset()
- for count in range(50):
- forward(60)
- left(64)
Le nombre 50 a été trouvé par tâtonnements. On obtient cette rosace où il semble qu’un trait de trop a été dessiné :
Une étude mathématique aurait permis d’optimiser le nombre de répétitions : en fait il suffisait de 45 itérations pour refermer la rosace.
Intuitif
Le script ci-dessous a été créé par tâtonnements (son exécution est d’ailleurs très longue) :
- from turtle import *
- L = 128
- for count in range(1000):
- forward(L)
- right(L)
- forward(L)
- L = 90
- for count2 in range(1000):
- forward(L)
- right(L)
- forward(L)
- L = 2
- for count3 in range(1000):
- forward(L)
- right(L)
- forward(L)
Il donne
Aquarelle
Un élève a pris le nom d’aquarelle pour ce script :
- from turtle import *
- L = 128
- for count in range(48):
- forward(L)
- right(L)
- forward(L)
- L = 90
- for count2 in range(48):
- forward(L)
- right(L)
- forward(L)
- L = 2
- for count3 in range(200):
- forward(L)
- right(L)
- forward(L)
Le résultat produit est intéressant :
Hexagones
Un script pour jouer avec des hexagones :
- from turtle import *
- reset()
- L = 32
- pendown()
- for count8 in range(4):
- for count in range(2):
- forward(L)
- left(60)
- for count3 in range(4):
- for count2 in range(5):
- forward(L)
- right(60)
- for count4 in range(2):
- left(120)
- forward(L)
- left(60)
- forward(L)
- for count6 in range(4):
- for count5 in range(2):
- forward(L)
- left(60)
- for count7 in range(2):
- left(120)
- forward(L)
- left(60)
- forward(L)
Il produit cette structure mais en passant un grand nombre de fois au même endroit :
Rosace
Ce script est mon préféré :
- from turtle import *
- reset()
- for count2 in range(73):
- L = 64
- left(5)
- for count in range(6):
- forward(L)
- right(60)
Mis à part la gestion pas optimale de la variable, l’élégance de ce script laisse difficilement prévoir le résultat :
Motifs
La figure 3 de Sierpiński est assez facile à reproduire, car la tortue n’avance (et tourne) que trois fois. Mais en essayant de reproduire les figures suivantes, on a vite tendance à chercher comment simplifier un programme Python, et cela motive
- l’introduction des variables
- l’utilisation des fonctions
- les boucles
Dans les onglets suivant on présente des exemples de production d’élèves.
Variables
Voici un script Python pour la figure 4 (9 côtés) :
A priori ce script est correct (il dessine bien la figure 4 de Sierpiński), mais lorsque fut rappelée la consigne de tracer des traits de 64 pixels (et non 60), cela a représenté beaucoup de travail pour modifier certains paramètres (pas tous : l’angle lui reste de 60°) alors que si le script avait commencé par
L = 60
en utilisant ensuite cette variable L, il aurait suffi de modifier la ligne en
L = 64
Finalement, c’est parfois bien pratique, les variables !
Fonctions
Constatant que le motif de la figure 4 est formé de 3 copies de la figure 3 dont une tournée vers la gauche, une élève a rapidement eu l’idée de programmer deux fonctions et ensuite simplement les appeler :
Ici l’exercice montre tout son intérêt : il y a décomposition de la courbe de Sierpiński en trois parties et chacune de ces parties est codée dans une fonction. Le stade suivant aurait été de mettre le tout dans une nouvelle fonction puis se servir de cette fonction pour la figure 5. Mais ce premier pas vers la récursivité n’a été franchi par aucun des élèves de Seconde.
Les fonctions sont lues sur la figure originale :
Boucles
Quand on doit répéter plusieurs fois une même tâche, on utilise des boucles. Pour cela on utilise la fonction range() qui, à partir d’un entier n, crée un objet capable de boucler n fois, ou plus précisément un objet capable de faire prendre n valeurs différentes à une variable : la variable de boucle i. La syntaxe est
for i in range(n):
avec un double-point qui augmente l’indentation.
Avec ces explications, un élève a trouvé avant les fêtes de fin d’année 2021 ce script pour la figure 4 :
Indentation
En Python, les sous-structures sont représentées par l’indentation. Chaque fois qu’une ligne commence par 2×n espaces, ce qu’il se passe dans cette ligne se déroule dans une sorte de monde virtuel inaccessible aux mondes moins virtuels (ceux qui comportent moins d’espaces en début de ligne).
Cette situation rappelle un peu la sémantique de Kripke pour les mondes possibles de Leibniz (ici les mondes possibles seraient les niveaux d’indentation) :
- Après def on entre dans un monde régi comme une forge, c’est là où on fabrique les fonctions. Les variables créées dans ce monde sont inaccessibles au monde supérieur.
- Après for ou while on entre dans un monde provisoire où des choses seront répétées un certain nombre de fois.
- Après if ou else on entre dans un monde hypothétique, c’est-à-dire qu’on n’y entrera peut-être pas du tout : tout dépend de la condition qui est après le if.
Après avoir abordé les rudiments de la programmation Python, il a été expliqué que for variable in n’est pas nécessairement suivi d’un range(n) mais peut très bien être suivi d’un mot (une chaîne de caractères) ce qui permet de boucler sur les lettres d’un mot. C’est d’ailleurs comme ça que les humains apprenaient à lire avant la méthode globale [3].
Figure 5
La figure 5 comportant 27 segments, il devient crucial de trouver comment faire court. Les connaissances sur les fonctions sont donc rapidement réinvesties.
Stylo
Pour ceux qui séchaient sur la partie Python, une activité préliminaire, consistant à reproduire la figure 5 au crayon, a été proposée.
L’imprimeur avait un peu insisté sur les traits de construction de Sierpiński et certains élèves ont décidé de refaire la figure entière, traits de construction compris :
D’autres ont fait preuve de soin, en repérant d’abord les sommets par lesquels passer :
Fonctions
Les fonctions gardent leur succès :
Mais comme il a déjà été dit plus haut, le passage à des fonctions qui appellent des fonctions, s’est révélé un obstacle non franchi.
Mots
Mais voici un script qui dessine lui aussi la figure 5 de Sierpiński, tout en étant bien plus court :
L’algorithme consiste à parcourir (comme on le fait en lisant, de gauche à droite) les lettres d’un mot, et
- chaque fois que la lettre est un D, tourner à droite,
- chaque fois que la lettre est un G, tourner à gauche.
Puis, de toute façon, avancer puisque seuls les angles changent selon l’endroit où on se trouve sur la courbe.
Toute la difficulté est de trouver le mot (succession de D et de G) de 27 lettres donnant la figure 5 de Sierpiński. Cela a été fait, en général, par parcours de la courbe sur la figure 5 de l’article.
Commentaires
Les scripts Python ont été d’abord testés puis recopiés à la main. Les commentaires (lignes en vert) ne sont pas exécutés par python et ne servent à rien. Ci-dessous ils représentent les mots donnant les autres figures de l’article de Sierpiński :
Cela permet en tout cas de voir que chaque figure comporte 3 fois plus de côtés que la figure précédente, autrement dit que chaque mot est trois fois plus long que le mot précédent.
Indentation
L’erreur d’indentation (forward au mauvais endroit) est une erreur de recopie, le programme sur l’ordinateur fonctionnait correctement :
La découverte de la méthode des mots avec la figure 5 a remotivé pratiquement tous les élèves et leur a donné l’énergie d’essayer de reproduire la figure 6 elle aussi. C’est à ce stade que l’activité prenait une allure cryptographique puisqu’il s’agissait de trouver un mot de 81 lettres sans jamais se tromper une seule fois.
figure 6
Tout d’abord, la possibilité était offerte pour les herpétophobes, de dessiner la figure au crayon sur papier pointé :
Dessins
En fait Sierpiński a tourné sa figure 6 de manière incohérente par rapport aux autres figures (peut-être une erreur de dessin, rattrapée comme il a pu). Cela a posé problème déjà pour la reproduire, ne sachant pas toujours où commencer :
Parfois le triangle extérieur a été tracé aussi pour servir de guide :
Mais parfois le tracé des 81 segments a été abandonné en cours de route pour se concentrer sur la version Python :
Lecture
Pour lire le mot, rien de tel que de regarder sur la figure 6 de Sierpiński, quitte à annoter celle-ci :
Parfois il a suffi d’annoter une partie, le reste s’en déduisant :
Ce travail est long :
mais ensuite il suffit de parcourir la courbe pour lire les lettres du mot au fur et à mesure, et là on gagne du temps.
Script
Plusieurs élèves ont réussi avant la fin de l’heure sur l’ordinateur, seuls les plus rapides ont pu recopier le script sur leur feuille. Voici un exemple :
Commentaires
Le script avec commentaires inutiles a été recopié correctement, y compris les commentaires :
Pour les plus rapides, a été posée la question : À quoi ressemblerait la figure 7 de Sierpiński s’il y en avait eu une ?. C’est l’occasion de voir si, pour la figure 6 déjà, on ne pouvait pas faire plus simple.
Syllabes
La question fondamentale est de savoir comment Sierpiński est passé
- de D à DDD
- de DDD à DGGDDDDGG
- etc
afin d’appliquer la même recette au mot de 81 lettres vu pour la figure 6 et obtenir le mot de 243 lettres qui décrit l’approximation suivante de la courbe de Sierpiński. Et au passage voir si on ne pouvait pas faire plus simple pour les figures 5 et 6.
Analyse
L’idée remonte à Aristid Lindenmayer : pour passer d’un mot au mot suivant, on remplace chaque lettre de l’ancien mot par une syllabe (ici, de 3 lettres).
Au début (avant la figure 3 de Sierpiński), on a une seule lettre, mettons D puisque la tortue va vers la droite.
La figure 3 pose un problème, parce que comme Sierpiński n’orientait pas ses figures de manière stable, on ne peut pas savoir si le mot est GDD ou DDD. Pour trancher, on regarde comment passer du mot de 3 lettres (pour l’instant inconnu) au mot suivant qui fait 9 lettres :
- la première lettre (D ou G, on ne sait pas) devient DGG
- le D du milieu devient DDD
- le D final devient DGG
On gagne en simplicité à considérer que le mot de la figure 3 est DDD. Mais certains D deviennent DGG et d’autres D deviennent DDD. L’alphabet de deux lettres D et G ne suffit donc pas, il faut y ajouter des minuscules :
- la première lettre est D
- ce D devient DDD, DDd, DdD, Ddd, dDD, dDd, ddD ou ddd. Mais lequel ?
Pour le savoir, on regarde en quoi se transforment chacune des lettres de ce mot de trois lettres. On a vu plus haut que le D du milieu se transforme en DDD (ou avec des minuscules) alors que les autres se transforment en DGG (ou avec des minuscules). Donc le seul choix convenable est que le premier D se transforme en dDd. Et le d se transforme en DGG, DGg, DgG, Dgg, dGG, dGg, dgG ou dgg.
Pour savoir lequel des 8 mots précédents est le transformé de d, on compare les figures 4 et 5 de Sierpiński : on voit que le premier G ou g se transforme en GGG (ou avec des minuscules) alors que le second G se transforme en GDD (ou avec des minuscules). Par analogie on suppose alors que le premier G est un G majuscule et se transforme en gGg et que le second G est par contre un g qui se transforme en GDD (ou avec des minuscules). Quant au D de DGg, vu qu’il se transforme en dDd, c’est qu’il est forcément en majuscule.
Bref,
- D → dDd
- d → DGg
- G → gGg
- g → GDD ou avec minuscules
À ce stade, il ne semble pas déraisonnable de supposer que la symétrie gauche-droite est respectée et donc que G → GDd. L’hypothèse se vérifie soit en comparant les figures 5 et 6 de Sierpiński, soit en programmant en Python pour vérifier la concordance entre le tracé de la tortue et la fractale connue.
Fonction
Voici le résumé des règles de réécriture obtenues :
- {D} → {dDd}
- {d} → {DGg}
- {G} → {gGg}
- {g} → {GDd}
Ce diagramme sagittal est celui d’une application de l’alphabet de 4 lettres D, d, G et g vers l’ensemble des mots de 3 lettres formés sur cet alphabet. En Python, les applications [4] sont stockées en mémoire sous la forme de « dictionnaires » ce qui donne
- {'D': 'dDd', 'd': 'DGg', 'G': 'gGg', 'g': 'GDd'}
Pour peu que l’on ait donné cette valeur à une variable suffixe (parce que ces mots de 3 lettres seront des suffixes à ajouter au mot en cours de construction) :
- suffixe = {'D': 'dDd', 'd': 'DGg', 'G': 'gGg', 'g': 'GDd'}
alors pour avoir le transformé de la lettre lettre on écrit
- suffixe[lettre]
Mot
Pour obtenir le nouveau mot nm à partir de l’ancien mot mot, on
- crée une variable nm contenant le nouveau mot, initialisé au mot vide (il n’y a rien entre les guillemets),
- lit le mot lettre par lettre,
- ajoute au nouveau mot nm, non pas la lettre lue, mais le suffixe (mot de 3 lettres) qui lui est associé :
- nm = ''
- for lettre in mot:
- nm += suffixe[lettre]
Pour obtenir les figures de Sierpiński, il suffit de remplacer l’ancien mot par le nouveau, et répéter le tout.
Pour la figure 3 :
- mot = 'D'
- for _ in range(1):
- nm = ''
- for lettre in mot:
- nm += suffixe[lettre]
- mot = nm
Pour la figure 4 :
- mot = 'D'
- for _ in range(2):
- nm = ''
- for lettre in mot:
- nm += suffixe[lettre]
- mot = nm
Pour la figure 5 :
- mot = 'D'
- for _ in range(3):
- nm = ''
- for lettre in mot:
- nm += suffixe[lettre]
- mot = nm
et pour la figure 6 :
- mot = 'D'
- for _ in range(4):
- nm = ''
- for lettre in mot:
- nm += suffixe[lettre]
- mot = nm
Dessin
Pour passer du mot au dessin, on fait comme plus haut, à ceci près que la tortue ne tourne pas à droite uniquement lorsque la lettre est un D mais lorsque c’est une lettre du mot Dd :
- from turtle import *
- def sierpinski(mot):
- for lettre in mot:
- if lettre in 'Dd':
- right(60)
- else:
- left(60)
- forward(L)
Sur le site Numworks on peut tester le script ci-dessous :
En bouclant 6 fois on reconnaît bien la courbe de Sierpiński :
Il est intéressant de comparer cette approche avec celle-ci : les règles de réécriture sont équivalentes mais ne se ressemblent pas.