L'objectif de ce travail est de réaliser des dessins de fractales, en utilisant le même moteur de calcul sur plusieurs polynômes complexes.
Nous rappelons q'un nombre complexe est de la forme $c = a + i b$, où $a$ est la partie réelle de $c$ et $b$ sa partie imaginaire. Nous coderons les nombres complexes comme des couples de flottants, ainsi, $c = 0.123 + 4.56 i$ sera encodé par la valeur
Vous utiliserez les fonctions du module Pervasive de la librairie standard pour manipuler les nombres flottants et accéder aux fonctions mathématiques standards. L'API des modules est disponible sur cette page : la libraire standard.
L'addition et la multiplication complexe sont donnés par $(a + i b) + (c + i d) = (a+c) + i (b + d)$ et $(a + i b)* (c + i d) = a*c - b*d + i (b*c + a*d)$. Utiliser cela pour coder l'addition, la multiplication et la puissance par un entier positif de nombres compexes en Ocaml.
Solution :
Le module d'un complexe est sa distance à l'origine dans le plan complexe. Si $c = a + i b$, alors le module de $c$ est $\left|c\right| = \sqrt{a^2 + b^2}$. Coder la fonction module d'un nombre complexe.
Solution :
On considère, pour un complexe donné $c$, le polynôme complexe $P_c(z) = z^2 + c$. L'ensemble de Mandelbrot est défini comme l'ensemble des complexes $c$ tels que $u_n = P_c^{(n)}(0)$ converge, avec $P_c^{(n)}(z) = P_c(P_c^{(n-1)}(z))$ si $n \geq 1$ et $P_c^{(0)}(z) = z$. On le voit en noir sur l'image suivante :
Savoir si $u_n$ converge ou diverge est un problème difficile, nous nous contentons d'utiliser une heuristique simple. Nous fixons un rayon $r \geq 2$ réel (par exemple, $r = 10^{10}$), et une borne entière $B$ (par exemple, $B=100$). Si $\left| P^{(n)}_c(0) \right| \geq r$ pour un $n \leq B$, alors $u_n$ diverge certainement. Sinon, on supposera (peut-être à tort) que $u_n$ converge. Autrement dit, nous calculons au plus $B$ itérations de la suite, et dès que nous obtenons un module supérieur à $r$, nous savons que la suite diverge.
$r = 2$ suffit pour être sûr que la suite diverge. En prenant une grande valeur de $r$, nous obtenons une meilleure précision sur la vitesse à laquelle la suite diverge (au prix d'un temps de calcul plus long).
Écrire une fonction récursive, qui étant donné $r$, $B$ et $P_c$, détermine le plus petit $n$ tel que $\left| P^{(n)}_c(0) \right| \geq r$, ou renvoie $B+1$ s'il n'existe pas de tel $n$.
Solution :
On utilise la librairie Graphics, que l'on charge avec le code suivant :
Écrire une fonction récursive iter_range de type (int -> unit) -> int -> int -> unit, telle que iter_range f mini maxi applique $f$ sur tous les entiers de l'intervalle $[\text{mini},\text{maxi}]$.
Solution :
Nous disposons donc maintenant de l'équivalent d'une boucle for, qui permet d'exécuter un calcul sur tous les éléments d'un intervalle. Par exemple,
provoque à l'exécution l'impression en sortie standard des 100 premiers entiers strictement positifs.
En déduire une fonction iter_rectangle de type (int -> unit) -> int -> int -> int -> int -> unit, telle que iter_rectangle f min_row max_row min_column max_column applique f row column pour tout row $\in$ [min_row,max_row] et tout column $\in$ [min_column, max_column].
Solution :
À un nombre complexe $a + i b$, on associe le point du plan $(a,b)$.
Écrire une fonction pour afficher une image, de type :
Le premier argument est une fonction color_of_complex qui associe une couleur à chaque nombre complexe. Le second argument est le complexe au centre de l'image. Les deux arguments suivants précisent la largeur et la hauteur du rectangle du plan complexe à afficher.
L'effet de cette fonction sera donc d'afficher pour chaque pixel de la fenêtre graphique la couleur d'un point du plan calculé avec color_of_complex, en cohérence avec les arguments de centre et de dimension.
Vous utiliserez les fonctions size_x et size_y de la librairie Graphics pour connaître la taille de la fenêtre d'affichage.
Vous pouvez découper cette question comme suit.
Solution :
Étant donné $P_c$, la couleur attribuée au point $c$ est le noir si l'indice de divergence est $B+1$. Sinon, la couleur attribuée est fonction de l'indice de divergence. Les choix possibles de couleurs sont infinis. Une idée générale est d'attribuer deux couleurs d'autant plus proches que les indices de divergence sont eux-mêmes proches, afin d'obtenir des gradients de couleurs. Coder différentes fonctions de type float -> Graphics.color transformant un indice de divergence (converti en flottant pour rester cohérent avec la suite du TP) en couleur.
Solution possible :
Combiner les résultats des précédentes questions pour obtenir un dessin de fractale, en utilisant $P_c(z) = z^2 + c$.
Solution :
L'indice de divergence étant entier, les images font apparaitre des zones uniformes de couleurs qui nuisent à leur aspect esthétique. Cette partie propose un amélioration pour lisser les couleurs. Pour cela, il faut raffiner le calcul de l'indice de divergence. Notons $d$ le degré du polynome $P_c$ utilisé (pour l'instant, $d = 2$).
Désormais, l'indice de divergence est donné par la formule:
$$ n - \frac{\log \frac{\log |z|}{\log r}}{\log d}$$
avec :
Utiliser cette formule pour améliorer le rendu.
Solution :
Essayer de tracer d'autres fractales, en utilisant des polynomes différents, par exemple $z \rightarrow z^3 + c$, en testant des fonctions de colorations plus subtiles et en zoomant sur des points intéressants du plan.