Creando un fractal de Mandelbrot en C

Omar Jair Purata Funes
Feb 17 · 6 min read

Todos amamos C, es el lenguaje favorito de algunos y el mejor amigo de nuestros computadores así que en un tiempo de aburrimiento pongámonos a hacer un hermoso fractal utilizando recursividad.

¿Qué demonios es un fractal?

Es curioso pensar que los fractales son algo muy común en la naturaleza y en la computación. Un fractal en sí es un patrón infinito y podemos encontrarlos en todos lados, desde cosas complejas como nuestras neuronas, las figuras de Lichtenberg o los fósiles de los ammonites que solían habitar en nuestro planeta, incluso podemos encontrarlos en algo muy simple como un Copo de nieve, en nuestro caso un Copo de nieve de Koch.

Copo de Nieve de Koch.

Al ser un fractal posee una propiedad muy especial, no importa cuanto acercamiento apliquemos o hacia donde miremos el patrón se repetirá de manera infinita.

Zoom “infinito” en una orilla del copo de nieve

Curiosamente el área de un copo de nieve de Koch no es infinita, si pudieramos dibujar un círculo alrededor, el copo jamás lo llenaría, así que escencialmente el copo de Koch tiene un perímetro infinito, pero no un área ilimitada.

El fractal de Mandelbrot

¿no es hermoso? :)

Dicho lo anterior, podemos comenzar a ver un poco el conjunto de Mandelbrot.

No explicaré la historia del mismo, si desean saber mas pueden revisar el siguiente enlace

El fractal está definido en dos planos, concretamente:

  • Números reales
  • Números imaginarios

Y se calcula de manera “recursiva” o “recurrente”. Con ello la fórmula expresada para calcularlo es:

Fórmula matemática curiosa

“Hay que considerar que tanto Z como C son números complejos. Se puede demostrar que si durante la recursión se alcanza un número complejo cuya distancia al origen es superior a 2, dicha sucesión diverge, por lo que en ese momento ya se sabe que el número c con el que se comenzó no pertenece al conjunto de Mandelbrot.”

Programando el fractal

Primero necesitamos crear nuestra función, para hacer fractales o conjuntos de Mandelbrot es recomendado utilizar ciclos, en nuestro caso ignoraremos eso y utilizaremos una función recursiva.

El archivo donde podremos ver nuestro fractal es un simple archivo con la extensión el cual maneja una escala de grises, su funcionamiento es simple, veamos con un ejemplo que imprime una flecha hacia abajo:

P1
10 10
0 0 0 1 1 1 1 0 0 0
0 0 0 0 1 1 0 0 0 0
0 0 0 0 1 1 0 0 0 0
0 0 0 0 1 1 0 0 0 0
0 0 0 0 1 1 0 0 0 0
0 0 0 0 1 1 0 0 0 0
0 0 1 1 1 1 1 1 0 0
0 0 0 1 1 1 1 0 0 0
0 0 0 0 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0

El formato es simple:

  • - Indica el formato del archivo.
  • - Indican el alto y largo de la imágen.
  • - Finalmente una matriz de valores (como la imágen es monocromo solo se necesitan 1's y 0's).

La imágen procesada debería verse así:

La función al ser recursiva se comerá rápidamente el stack así que no recomiendo asignar valores muy altos.

No explicaré como funciona a fondo el código

Las bibliotecas que usaremos son:

<stdlib.h>
<complex.h>
<stdio.h>
<math.h>

Primero vamos a definir la función que determinará si el número resultante pertenece o no al conjunto de Mandelbrot:

complex double mandelbrot(int max, complex double z0, complex double c) {
if (max > 0) {
return mandelbrot(max - 1, cpow(z0, 2) + c, c);
} else if (cabs(z0) > 2) {
return 0;
} else {
return 1;
}
}

La siguiente función es de tipo (Revisar la cabecera ) y recibe tres parámetros para poder operar, un entero llamado que indica el número de iteraciones de nuestra función y dos números complejos, en este caso y que son los números complejos que necesitamos.

Las evaluaciones vienen dentro de la función, mientras que el número de iteraciones sea mayor a cero la función se repetirá, si el valor absoluto del número complejo ( en este caso) es mayor que 2 entonces se retornará un cero ya que el número resultante no pertenece al conjunto de Mandelbrot, en caso contrario retornaremos un uno y con ello podremos llenar nuestro archivo .

Pasando a la función principal primero tenemos que definir las constantes que utilizaremos, para esto utilizaremos la capacidad de tomar argumentos de línea de comandos de C resultando en lo siguiente:

const int n = atoi(argv[0]);
const int m = atoi(argv[1]);
int iter = atoi(argv[2]);

Con esto el usuario indicará las dimensiones de la imagen, así como el número de iteraciones deseadas, ahora solo nos falta definir una matriz y los límites de nuestro plano a dibujar.

int matrix [m][n];const double sup_real = 2.0; // Límite superior de los números reales
const double inf_real = -2.0; // Límite inferior de los números reales
const double sup_imag = 2.0; // Lim superior de los números imaginarios
const double inf_imag = -2.0; // Lim inferior de los números imaginarios

Ahora tenemos que realizar las operaciones para poder colocar los puntos en la distancia correcta:

double pX = ((sup_real - inf_real) / n);
double pY = ((sup_imag - inf_imag) / m);

Ahora tenemos que poblar nuestra matriz con el resultado de nuestra función de mandelbrot:

complex double C;for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
C = (inf_real + (j * pX)) + ((sup_imag - (i * pY)) * I);
matrix[i][j] = mandelbrot(iter, C, C);
}
}

Finalmente imprimiremos hacia un archivo con extensión .pgm los resultados de nuestra matriz:

FILE *fp;
fp = fopen("mandelbrot-fractal.pgm", "w");
fputs("P2 \n", fp);
fprintf(fp, "%d %d \n", n, m);
fputs("1 \n", fp);
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
fprintf(fp, "%d ", matrix[i][j]);
}
fputs("\n", fp);
}
return 0;

Una vez completado nuestro código podremos divertirnos generando fractales de varios tipos y tamaños (pero no muy grandes, pues las funciones recursivas se comen el stack como tiburones).

Aquí una muestra de diferentes fractales generados con el programa anterior:

50 Iteraciones — 50 x 50

Parece un pájaro kiwi desde arriba

100 Iteraciones — 100 x 100

¿BattleStar Galactica?

500 Iteraciones — 500 x 500

1000 Iteraciones — 1024 x 1024

Contemplad

Desafortunadamente no logré hacer que el programa corriera mas allá de las mil iteraciones, pues al ser una función recursiva la que se está llamando al final terminé matando mi pila.

El código fuente lo pueden encontrar aquí.

Future Lab

Community focused on science and technology outreach as well as projects that use AI, IoT or data.

Omar Jair Purata Funes

Written by

Part time writer, part time programmer, full time mexican tacos.

Future Lab

Community focused on science and technology outreach as well as projects that use AI, IoT or data.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade