Implémenter Mandelbrot

Comment visioner la fractale de Mandelbrot

Le problème exposé dans ce sujet a été résolu.

Salutation,

j’ai récemment lu l’article Dessiner la fractale de Mandelbrot et je me suis donc lancé dans la création de ce dernier, mais d’une façon nettement plus simple, car certaines lignes de codes présentes dans le code de ce tutoriel me semblaient inutiles. De cette façon, j’en suis arrivé à cette simple fonction qui d’après mes connaissances, permet déjà de déterminer si un nombre complexe est stable ou pas.

def mandelbrot(x, y):
    c = complex(x, y)
    z = 0

    for i in range(50):
        z = z**2 + c

        if abs(z) >= 2:
            break
        else:
            # code permettant d'afficher le pixel

Maintenant, j’aimerai savoir comment m’y prendre pour afficher la fractale de Mandelbrot. Ma démarche, mentalement est pour l’instant la suivante: je donne la longueur et la largeur d’une surface place et je fais en sorte d’itérer sur chacun de ses pixels. Pour chaque pixel, que j’obtiens grâce au nombre complexe qui me sert de "pointeur", je l’affiche seulement si il ne dépasse pas 2 au bout de 50 ou 100 itérations (donc je définis pixel par pixel si il sera affiché, ou non).

+0 -0

Salut,

mais d’une façon nettement plus simple, car certaines lignes de codes présentes dans le code de ce tutoriel me semblaient inutiles

Euh… Tu as la même chose que le tutoriel, en fait. C’est juste que le tuto gère le niveau de zoom et des couleurs, et que PHP gère pas les complexes nativement. Donc forcément, ça rajoute du "bruit" autour du code mais l’algo utilisé est exactement le même.

Ma démarche, mentalement est pour l’instant la suivante: je donne la longueur et la largeur d’une surface place et je fais en sorte d’itérer sur chacun de ses pixels. Pour chaque pixel, que j’obtiens grâce au nombre complexe qui me sert de "pointeur", je l’affiche seulement si il ne dépasse pas 2 au bout de 50 ou 100 itérations (donc je définis pixel par pixel si il sera affiché, ou non).

Ça parait raisonable, qu’est-ce qui te bloque ?

+1 -0

Ça parait raisonable, qu’est-ce qui te bloque ?

Eh bien.. l’outil avec lequel j’afficherai Mandelbrot. J’ai vue beaucoup de choses différentes sur internet comme matplotlib, Tkinter et PIL. Mais aucun de ces exemples ne me permet d’imprimer via sa coordonné un pixel.

Vous en connaisez qui me le permettrais ?

+0 -0

J’ai vue beaucoup de choses différentes sur internet comme matplotlib, Tkinter et PIL. Mais aucun de ces exemples ne me permet d’imprimer via sa coordonné un pixel.

matplotlib et pillow permettent tous les deux de fournir une matrice (ton tableau de pixels) et d’en faire une carte, donc pas sûr de ce que tu veux dire (et j’imagine que tkinter a un outil pour afficher une grille qui pourrait marcher, mais pour le coup c’est pas vraiment fait pour produire des images de qualité).

+2 -0

matplotlib et pillow permettent tous les deux de fournir une matrice (ton tableau de pixels) et d’en faire une carte, donc pas sûr de ce que tu veux dire

Tu voudrais dire que je créerai une matrice à deux dimension qui correspondrait à la surface plane sensé contenir à la fin Mandelbrot ? Cette matrice, serait initialement rempli de 0, et en itérant dessus, je remplacerai chaque 0 par 1 (peut être affiché: pixel noir) ou 0 (dépasse 2 à un moment: pixel blanc) (ça voudrait dire que matplotlib et pillow interpréteraient les nombres comme des couleurs). Et enfin, j’afficherai cette matrice.

En essayant de faire ça avec une ligne numpy, j’ai ça:

x1 = -2.1
x2 = 0.6
y1 = -1.2
y2 = 1.2

img = np.zeros((x1 - x2, y1 - y2, 3))

Et en exécutant, j’ai ça:

    img = np.zeros((x1 - x2, y1 - y2, 3))
TypeError: 'float' object cannot be interpreted as an integer

C’est problématique car, la fractal de Mandelbrot apparait en partie sur les axes des négatifs (-x et -y), et que s’il est illégal de créer une matrice avec des nombres flottants, et à virgules, alors pas de Mandelbrot. De plus, dans l’exemple du tutoriel en haut, l’auteur dit qu’il faut un pas de 1:

Pour x = 0 tant que x < image_x par pas de 1 

Or un pas de 1, lorsque on travail sur un tableau qui va jusqu’à 3 trois unités environ, c’est déplorable, il faudrait un pas de 0.1 ou 0.01 environ, nan ?

+0 -0

J’ai reçu ça, sur Stackoverflow mais je me demande pourquoi et comment le reste des personnes que j’ai vu faire l’ensemble de Mandelbrot n’y sont pas confrontés.

You can’t create matrix with float axis sizes. Use some scaler, like k=1000 and shift like dx=2.1, dy = 1.2 to draw on matrix.

img = np.zeros((int(k*(x1 - x2 + dx)), int(k*(y1 - y2+dy)), 3))

when displaying pixel:

img[int(k*(x + dx)), int(k*(y + dy)] = ...

Ben… C’est à ça que sert une partie du code que tu as qualifié "d’inutile" en fait.

Par ailleurs, plutôt que d’utiliser x et y et tenter de retrouver une position entière sur l’image, je ferais l’inverse. Si tu veux que ton image aille par exemple de x1 = -2.0 à x2 = 1.0 avec disons nx = 200 points, alors le point d’indice entier i (de 0 à 199) est situé en x = x1 + (x2 - x1) * i / (nx - 1). Pareil selon y, et tu peux alors facilement passer de la coordonnée en pixel entière (i, j) à la coordonnée en flottant (x, y) qui t’intéresse pour faire les calculs.


Au passage, plutôt que d’évaluer ‘abs(z)’ il vaudrait mieux une méthode du type abs2 pour éviter une racine carrée.

On peut aussi gagner une itération avec z=c comme initialisation

Pour l’instant, c’est de la micro-optimisation sur laquelle il est contre-productif de s’attarder. Ce qui va dominer les performances de toute façons, ce sera d’une part le fait de vectoriser les calculs pour passer le plus de temps possible dans numpy, et d’autre part la création de l’image elle-même. Je serais même pas surpris que créer l’image soit le bottleneck en terme de performance, mais bien sûr faut mesurer pour être sûr.

+1 -0

Au final, j’y suis arrivé comme ça:

import numpy as np
import matplotlib.pyplot as plt


def create_complex_matrix(x1, x2, y1, y2, quality):
    x = np.linspace(x1, x2, int((x2 - x1) * quality))
    y = np.linspace(y1, y2, int((y2 - y1) * quality))
    return x[np.newaxis, :] + y[:, np.newaxis] * 1j

def is_stable(c, i):
    print("C is :", c)
    z = c
    for _ in range(i): z = z ** 2 + c
    return abs(z) <= 2

plan = create_complex_matrix(-2, 0.5, -1.5, 1.5, quality=1000)

plt.imshow(is_stable(plan, i=30), cmap="binary")
plt.show()
Mandelbrot
Mandelbrot

Le seul soucis, c’est qu’il n’est pas modifiable pour créer des fractales de Julia.

+0 -0

Pas mal ! T’as profilé pour voir ce qui prend du temps ?

Le seul soucis, c’est qu’il n’est pas modifiable pour créer des fractales de Julia.

C’est un problème un peu différent, mais à priori il n’y a rien qui t’empêche de généraliser ton code. Qu’est-ce qui te pose problème ?

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte