Caos infinito



Como en cualquier empresa en la que la mayoría de trabajadores son mileuristas, la hora de la comida es un ir y venir de tupperwares que se desplazan por la sala de descanso en un acompasado paseo que va del microondas a la mesa. Mi compañera Sofía traía una suerte de verdura extraña cocida, que la verdad, no entraba demasiado por los ojos. No pude más que preguntarle qué era eso que se disponía a comer.
Es un romanescu. Un híbrido entre coliflor y brécol. ¿a que mola? - dijo alegremente.
Desde luego tenía una forma curiosa. Antes de atreverme a preguntarle lo que llevaba a una persona más o menos normal a engullir tan extraño alimento, Sofía se adelantó y empezó a explicarme que le encantaba esa verdura porque le recordaba a un fractal.
Uno de mis mayores problemas es que soy incapaz de dejar de demostrar con el gesto de mi cara todo aquello que pasa por mi cabeza, así que en aquél momento, Sofía podría haber sido capaz intuir mi debate interno para dirimir quién era más raro, el romenescu o ella. Afortunadamente, Sofía malinterpreto mi cara y pensó que eso de los fractales debía sonarme a chino, cosa que por otro lado era cierta.



Un fractal es, por definición, un conjunto cuya dimensión de Hausdorff-Besicovitch es estrictamente mayor que su dimensión topológica - Espetó Sofía, así, sin anestesia.
De nuevo, mi cara debió ser un poema, porque empezó a reir y comenzó a hablarme de que los objetos fractales no tenían dimensión entera como una recta (1 dimensión), un plano (2 dimensiones) o un cubo (3 dimensiones). Sino que tienen dimensiones fraccionarias, como la curva de Koch que tiene dimensión 1'2618.
En ese momento ni me atrevía a mirar a Sofía a la cara. Me estaba dando un discurso que era incapaz de descifrar.
Imagina que queremos medir la longitud de la Costa del Sol, en el Sur de España- continuó Sofía. Podrías coger Google Maps e ir trazando líneas de 1Km por el perímetro de la costa y luego sumar el número de líneas.
Esto que me estaba contando ahora sí que podía seguirlo, así que me animé a decirle que sí, pero que no sería una medida muy exacta.
Efectivamente. Hay irregularidades más pequeñas de 1Km que no podríamos medir, pero supongamos que ahora lo hacemos con una regla de 1m.
Pues ahora si que sería más exacto- contesté. Pero aún así hay irregularidades más pequeñas que 1m.
Correcto- dijo Sofía con voz cantarina. Y si bajamos a la escala del Centímetro pasará igual, y así sucesivamente hasta... bueno, hasta el infinito, si es que algo puede ser infinitamente pequeño. Esas irregularidades nos impiden medir certeramente la costa usando las tres dimensiones del espacio euclideo, y es por ello por lo que tenemos que recurrir a la geometría fractal para describir multitud de elementos en la naturaleza.
Tal y como le pasa a este romenescu- Prosiguió. Si te fijas, tiene unas estructuras en forma helicoidal que se van repitiendo a escalas más pequeñas. Es como si un tallo de romenescu contuviera a si vez más tallos iguales, pero más pequeños, y estos a su vez tuvieran otros más pequeños, y así sucesivamente, sin importar la escala a la que fuéramos capaces de observar. Es lo que se llama autosimilitud.
La idea de algo que se repetía infinitamente según disminuíamos de escala me daba un poco de vértigo, pero a la vez llamaba mi atención.

Apenas si pude terminar de comerme mis spaguetis cuando ya había pasado la hora de la comida. Ya de vuelta en las mesas Sofía continuó con la disertación (Sofía se sienta junto a mí, justo en el lado contrario de donde se sienta el Sr. Lego).
El primero en hablar de fractales fué Benoît Mandelbrot- continuó. En 1975 acuñó el término fractal y creó el famoso conjunto de Mandelbrot.
Sofía giró su monitor y me mostró la siguiente imagen.



La imagen me resultó curiosa. Así que me me animé a preguntar si ese objeto fractal podría aumentarse e ir encontrando figuras similares a ella misma.
¡Claro!- respondió. Se puede explorar el conjunto de Mandelbrot y encontrarás paisajes muy hermosos, lugares inhóspitos y, por supuesto, réplicas en miniatura del conjunto de Mandelbrot.
De hecho, es muy sencillo generarlo. Tú mismo podrías hacer un programa que te permita explorar la infinitud del conjunto.
Empezaba a picarme la curiosidad, así que me armé de valor y seguí escuchando...
Bueno, el algoritmo es sencillo. Basta con saber sumar y multiplicar número complejos. ¿recuerdas cómo se hacía?
Antes de darme la oportunidad de responser prosiguió explicándome que un número complejo está compuesto de una parte real y otra imaginaria y que tienen la forma a+bi (por ejemplo 5+3i) donde el primer número es un número real, y el segundo es otro número multiplicado por la unidad imaginaria, que equivale a la raíz cuadrada de -1.
También me explicó que para sumar dos números complejos había que sumar por separado la parte real y la imaginaria. Por ejemplo (3+7i) + (5+3i) = (8+10i).
Sobre la multiplicación, me explicó que debía hacerse como si se tratara de la multiplicación de dos polinomios normales, pero con la particularidad de que i^2 es igual a -1. Por lo tanto (a+bi)*(c+di) = ac+adi+bci+bdi^2 = (ac-bd)+(ad+bc)i.
En realidad, podemos ver un número complejo como las dos coordenadas de un plano, en el que la parte real indica el valor de la abscisa y la parte imaginaria el valor de la ordenada.
Sofía me mostró un pequeño programa en Java que había escrito para realizar la imagen que acababa de mostrarme.


  1. import java.awt.*;
  2. import javax.swing.*;
  3.  
  4. class JMandelbrot extends JFrame {
  5.    
  6.     final int RES=600;  // Resolución
  7.     final int ITERACIONES=255;  // iteraciones
  8.    
  9.     // Porción del conjunto de Mandebrot a mostrar
  10.     final double XPOS=-2.0;
  11.     final double YPOS=-1.5;
  12.     final double LADO= 3;
  13.    
  14.     public JMandelbrot() {  
  15.         super("Mandelbrot");
  16.         setBounds(0,0,RES,RES);
  17.         setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  18.         Container con=this.getContentPane();
  19.         con.setBackground(Color.white);        
  20.         GCanvas canvas=new GCanvas(RES, XPOS, YPOS, LADO, ITERACIONES);    
  21.         con.add(canvas);
  22.         setVisible(true);
  23.     }
  24.     public static void main(String[] args) {
  25.         new JMandelbrot();
  26.     }
  27. }
  28.  
  29. class GCanvas extends Canvas {
  30.    
  31.     int res, iteraciones;
  32.     double xpos, ypos, lado, delta;
  33.    
  34.     public GCanvas(int res, double xpos, double ypos, double lado, int iteraciones) {
  35.         this.res=res;
  36.         this.xpos=xpos;
  37.         this.ypos=ypos;
  38.         this.lado=lado;
  39.         this.delta = lado/res; 
  40.         this.iteraciones=iteraciones;
  41.     }
  42.    
  43.     double c_real, c_imaginaria, z_real, z_imaginaria, zr, zi, modulo;
  44.     int cont, color;
  45.     public void paint(Graphics g) {
  46.         for (int i=0; i<res; i++) {
  47.             for (int j=0; j<res; j++) {
  48.                 // cálculo del número complejo C correspondiente
  49.                 // al pixel actual.
  50.                 c_real=xpos+(i*delta);
  51.                 c_imaginaria=ypos+(j*delta);
  52.  
  53.                 // inicializamos Z
  54.                 z_real=0.0;
  55.                 z_imaginaria=0.0;
  56.                 cont=0;
  57.                
  58.                 // calcular si el punto pertenece al conjunto de Mandelbrot
  59.                 do {
  60.                     zr=((z_real*z_real)-(z_imaginaria*z_imaginaria))+c_real;
  61.                     zi=2*(z_real*z_imaginaria)+c_imaginaria;
  62.                     z_real=zr;
  63.                     z_imaginaria=zi;
  64.                     cont++;
  65.                     modulo=Math.sqrt((z_real*z_real)+(z_imaginaria*z_imaginaria));
  66.                 } while (cont<=iteraciones && modulo<2);
  67.                
  68.                 // transformar el valor a escala de grises.
  69.                 if (cont<iteraciones) {
  70.                     color=((iteraciones-cont)*255)/iteraciones;
  71.                 } else {
  72.                     color=0;
  73.                 }
  74.                
  75.                 // dibujar el pixel
  76.                 g.setColor(new Color(color, color, color));
  77.                 g.drawLine(i, j, i, j);
  78.             }
  79.         }
  80.     }
  81. }

Ayudándose del listado del programa comenzó a explicarme el algoritmo que generaba aquellas imágenes tan intrigantes.
Verás, lo primero que hacemos es especificar qué parte del conjunto queremos explorar. Lo hacemos indicando el área con las variables xpos e ypos. Seguidamente, con la variable lado indicamos que tamaño del lado del cuadrado que tendrá el área a mostrar. Como el área que queremos explorar puede tener cualquier tamaño arbitrario, hay que adaptarlo a la resolución de la imagen (indicada por la constante RES). Para ello calculamos la variable delta dividiendo lado entre la resolución de la imagen, obteniendo así la distancia que media entre dos pixeles adyacentes.
Finalmente, entramos en harina y buscamos qué puntos del plano que estamos explorando pertenecen o no al conjunto de Mandelbrot. Para ello evaluamos cada uno de los pixeles de la imagen mediante dos bucles anidados donde la variable i representa el eje de abscisas y j el eje de ordenadas. Suponiendo que nos encontramos en la fila i, columna j, los pasos que realizaremos son los siguientes:

- Calcular el número complejo c que representa el pixel i,j. La parte real se obtiene sumando a xpos la columna actual multiplicada por delta. La parte imaginaria se obtiene igual, pero multiplicando j por delta. Es decir:
c_real=xpos+(i*delta);
c_imaginaria=ypos+(j*delta)

- Asignar a una variable compleja z el valor 0+0i, y crear una variable contador (cont) también con el valor 0.

Repetir las siguientes dos operaciones hasta que el módulo de z sea mayor que 2 o bien que la variable cont exceda un valor prefijado (en nuestro caso almacenado en la variable iteraciones).

z = z^2 + c
cont = cont +1

Si terminamos y el contador es mayor que el valor prefijado, quiere decir que ese punto pertenece al conjunto de Mandelbrot, ya que no diverge (se mantiene acotado con un módulo inferior a 2 después de varias iteraciones). Este punto lo colorearemos de negro.
Si no, quiere decir que el punto no pertenece al conjunto de Mandelbrot y lo pondremos de blanco o le asignaremos un color en función del valor de la variable cont. En nuestro caso hemos asignado un tono de gris.
Mientras más grande sea el valor prefijado (variable iteraciones) más pequeñas serán las zonas a las que podamos acceder (y más tiempo tardará el conjunto en generarse).

Como curiosidad, Sofía me mostró un par de parajes curiosos. Para ver el primero asignamos los siguientes valores a las constantes:

final double XPOS=-1.275;
final double YPOS=0.0916;
final double LADO= 0.4;



La segunda tiene las siguientes coordenadas:

final double XPOS=-1.17286;
final double YPOS=0.24133;
final double LADO= 0.003;


Sea como fuere, ahora empezaba a ver al romanescu con mejores ojos. Quizás, quién sabe, hasta tenga buen sabor.

1 comentario:

  1. Curiosamente desde hace varios días he estado obsesionado creando un programa de temática similar, y es que el tema es bastante profundo.

    No sólo es interesante el algoritmo para generar el fractal, sino también el algoritmo para colorearlo. En fractales como el conjunto de Mandelbrot se suele utilizar el algoritmo de tiempo de escape, que se basa en medir el número de iteraciones que tarda en diverger cada punto, y a partir de eso asignarle un color. Eso aumenta muchísimo la variedad de detalles que se pueden encontrar.

    Otra técnica que da lugar a resultados impresionantes es la de "trampas orbitales", que consiste en medir qué tanto se acerca un punto a los ejes coordenados (o cualquier línea o punto que el programador elija) durante su recorrido y a partir de eso asignar un color.

    http://chatran20.deviantart.com/gallery/#/d3ljk05

    Por cierto, excelente blog. Llegué aquí investigando sobre el Minimax y fue imposible dejar de leer ;)

    ResponderEliminar