domingo, 22 de enero de 2012

De la cola del cine a la danza estelar


Llevabamos ya casi media hora en la cola y aquello parecía no avanzar, lo que para ser un jueves por la tarde era bastante poco corriente. Mi amigo Descollante y yo solemos ir al cine este día porque suele haber poca afluencia, sin embargo, hoy la cosa estaba imposible.

Para la próxima sacamos la entrada por Internet -dije- aunque haya que pagar algo más. No entiendo como puede haber tanta gente hoy y el Jueves pasado estuvimos solos.

Pues no hay ningún misterio -dijo Descollante sin ni siquiera volver su mirada- los grupos de personas se comportan como sistemas dinámicos. No hay más.

Como era habitual, Descollante, que no distingue una pregunta retórica de otra real, se disponía a buscarle una explicación racional a un hecho que nadie más que él se atrevería a analizar seriamente, así que tenía dos opciones: callarme y seguir disfrutando de la cola, o hacer como si me interesara su comentario y enfrentarme a una disertación que no estaba seguro de querer escuchar. Por desgracia mi maldita manía de empatizar hasta con las piedras me hizo pronunciar dos fatídicas palabras: ¿sistemas dinámicos?

Así que Descollante se animó: Sí, la dinámica de sistemas trata de explicar el comportamiento de sistemas complejos que evolucionan a través del tiempo. El comportamiento de las masas se asemeja a un sistema dinámico, ya que la forma de actuar de un grupo grande de personas a menudo no puede entenderse a partir del comportamiento de cada individuo por separado.

¿No irás a decirme que esta cola podríamos haberla predecido? Porque si es así nos forramos prediciendo el comportamiento de las masas.

Tú siempre pensando en el dinero -rió mi amigo. De hecho no creo, ya que hablamos de lo que los matemáticos llaman sistemas no lineales: sistemas cuyo comportamiento global no puede expresarse como la suma de los comportamientos individuales.

Descollante, estoy seguro de que lo que me estás contando sería muy interesante si fuera capaz de comprender algo, pero no termino de entender a dónde quieres llegar.

Está bien, Alberto, te pondré un ejemplo más simple. ¿Recuerdas del instituto cómo se calcula la fuerza gravitatoria ejercida entre dos cuerpos?
Si -respondí- era algo así como que la fuerza es directamente proporcional al producto de sus masas e inversamente proporcional al cuadrado de su distancia.
Exacto -dijo Descollante mientras dábamos el par de pasos que había avanzado la cola- y esa formulita te permite calcular fácilmente cosas como la órbita que describirá un cuerpo alrededor del otro. Sin embargo, si añadimos un tercer cuerpo, tenemos un problema ya que lo convertimos en un sistema no lineal y describir analíticamente sus órbitas es una tarea tremendamente compleja con las matemáticas de las que disponemos. De hecho, se transforma en un sistema caótico donde empiezan a mandar las leyes de la teoría del caos.

Ahora sí que estaba desconcertado ya que aquello no tenía demasiado sentido para mí, así que no pude callarme: pero si no podemos calcular la trayectoria de sólo tres cuerpos, ¿cómo calculan los astrónomos la evolución de un cúmulo estelar o incluso de toda una galaxia?

Descollante me miró casi perdonandome la vida: he dicho que no podemos calcularlo analíticamente, pero disponemos de una herramienta maravillosa, la simulación por ordenador.

Aquello ya empezaba a gustarme más, habiendo un ordenador de por medio siempre me siento más cómodo. Así, entre teoría del caos por aquí y problemas no lineales por allá logramos entrar en el cine y ver la peli. Ya de regreso a casa de Descollante nos sentamos ante el ordenador porque quería mostrarme cómo hacer la simulación de un sistema complejo como un cúmulo estelar.

Verás Alberto, para representar cada estrella en el ordenador almacenaremos su posición en el espacio como una coordenada tridimensional X,Y,Z. Necesitamos también almacenar su velocidad y su aceleración como vector tridimensional. La idea es que todas las estrellas ejercen una fuerza entre sí. Como hay muchas estrellas, la fuerza resultante ejercida sobre una estrella es la suma de todas las fuerzas que actúan sobre ella. Como sabes F=G(m1*m2)/d, siendo la distancia d=sqrt(x1-x2*y1-y2*z1-z2). En realidad, como bien me dijiste antes hay que utilizar el cuadrado de la distancia, pero para la simulación vamos a utilizar directamente la distancia ya que buscamos también un efecto estético en la animación del movimiento de las estrellas. Tampoco vamos a meter de por medio a la constante G, la dejaremos descansar hoy. Desde luego, si fuéramos astrofísicos tratando de hacer cálculos exactos sí que tendríamos que tener todo esto en cuenta.
Conocida la fuerza, podemos calcular la aceleración que sufre la estrella en cada eje de coordenadas:

aceleración_x=F*(posición_x2-posición_x1)/d

Es decir, la aceleración en el eje X es igual al producto de la fuerza por la diferencia entre la posición en el eje X de la otra estrella y la posición X de la estrella sobre la que estamos realizando el cálculo. Todo ello dividido por la distancia. La aceleración total que sufre la estrella es la suma de todas las aceleraciones (una por cada estrella que la rodea). El cálculo en el eje Y y Z es idéntico.
Ahora que tenemos calculado el resultado de todas las interacciones entre estrellas, sólo nos resta calcular su velocidad sumándole la aceleración calculada en el paso anterior.

velocidad_x = velocidad_x + aceleración_x

Y lo mismo para las otras dos componentes Y y Z.
Ya sólo tenemos que sumar la velocidad a la posición de la estrella y habremos calculado su movimiento.

posición_x = posición_x + velocidad_x

Y de nuevo repetimos el cálculo para los ejes Y y Z.
Repitiendo estos cálculos en un bucle y representando cada estrella gráficamente tendremos una simulación animada de un cúmulo estelar.

Mientras me lo explicaba, Descollante iba tecleando un pequeño programa en Java. Una vez terminó de teclear lo ejecutó y esto es lo que se vio en la pantalla:



He creado un campo aleatorio de 25 estrellas -dijo Descollante- pero si quieres más sólo tienes que cambiar el valor de la variable NUM_STARS. Tampoco he simulado la colisión entre estrellas ni nada parecido, ya que simplemente queremos ver visualmente como evoluciona un sistema dinámico.
Hoy en día, los gobiernos y grandes corporaciones utilizan herramientas de análisis de sistemas dinámicos para predecir comportamientos económicos o incluso sociales.
Pues no sabes cómo me tranquiliza eso -dije.
¿Te tranquiliza? dijo sorprendido Descollante.
Reí pero no dije nada más. Aunque se lo explicara, dudo que Descollante fuera capaz de entender lo que es una frase irónica.

Aquí se reproduce el código que tecleó Descollante:

  1. import java.util.Random;
  2. import java.awt.*;
  3. import javax.swing.*;
  4.  
  5. class SpaceDance extends JFrame {
  6.  
  7.   int RES_X = 640;
  8.   int RES_Y = 480;
  9.  
  10.   public SpaceDance() {
  11.     super("SpaceDance");
  12.     setBounds(0, 0, RES_X, RES_Y);
  13.     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  14.     Container con = this.getContentPane();
  15.     con.setBackground(Color.black);
  16.     GCanvas canvas = new GCanvas();
  17.     con.add(canvas);
  18.     setVisible(true);
  19.   }
  20.  
  21.   public static void main(String[] args) {
  22.     new SpaceDance();
  23.   }
  24. }
  25.  
  26. class Star {
  27.  
  28.   int posx, posy, posz; // posición
  29.   double spdx, spdy, spdz; // velocidad
  30.   double accx, accy, accz; // aceleración
  31.   double mass; // masa
  32. }
  33.  
  34. class GCanvas extends Canvas implements Runnable {
  35.  
  36.   int RES_X = 640;
  37.   int RES_Y = 480;
  38.   int NUM_STARS = 25;
  39.   int tiempo_pausa = 100;
  40.   Star[] stars = new Star[NUM_STARS];
  41.   Thread t;
  42.  
  43.   public GCanvas() {
  44.     t = new Thread(this);
  45.     t.start();
  46.   }
  47.  
  48.   public void paint(Graphics g) {
  49.     // dibujar las estrellas
  50.     g.setColor(Color.black);
  51.     g.fillRect(0, 0, RES_X, RES_Y);
  52.     for (int i = 0; i < NUM_STARS; i++) {
  53.       if (stars[i] != null) {
  54.         int tam = (int) stars[i].mass;
  55.         if (tam < 2) {
  56.           g.setColor(Color.white);
  57.         } else if (tam < 4) {
  58.           g.setColor(Color.yellow);
  59.         } else if (tam < 6) {
  60.           g.setColor(Color.blue);
  61.         } else {
  62.           g.setColor(Color.red);
  63.         }
  64.         g.fillOval(stars[i].posx, stars[i].posy, tam, tam);
  65.       }
  66.     }
  67.   }
  68.  
  69.   public void initStars() {
  70.     // inicializar estrellas aleatoriamente
  71.     Random rnd = new Random();
  72.     for (int i = 0; i < NUM_STARS; i++) {
  73.       stars[i] = new Star();
  74.       stars[i].posx = rnd.nextInt(RES_X);
  75.       stars[i].posy = rnd.nextInt(RES_Y);
  76.       stars[i].posz = rnd.nextInt(RES_Y);
  77.       stars[i].accx = 0;
  78.       stars[i].accy = 0;
  79.       stars[i].accz = 0;
  80.       stars[i].mass = (rnd.nextFloat() + 0.1) * 10;
  81.     }
  82.   }
  83.  
  84.   public void run() {
  85.     initStars();
  86.     do {
  87.  
  88.       for (int i = 0; i < NUM_STARS; i++) {
  89.         // cálculo de las aceleraciones
  90.         stars[i].accx = 0;
  91.         stars[i].accy = 0;
  92.         stars[i].accz = 0;
  93.         for (int j = 0; j < NUM_STARS; j++) {
  94.           if (i != j) {
  95.             int dx = stars[i].posx - stars[j].posx;
  96.             int dy = stars[i].posy - stars[j].posy;
  97.             int dz = stars[i].posz - stars[j].posz;
  98.             // distancia
  99.             double d = Math.sqrt(dx * dx + dy * dy + dz * dz);
  100.             if (d != 0) {
  101.               // fuerza
  102.               double f = (stars[i].mass*stars[j].mass) / d;
  103.               // aceleraciÛn
  104.               stars[i].accx += f * (stars[j].posx - stars[i].posx) / d;
  105.               stars[i].accy += f * (stars[j].posy - stars[i].posy) / d;
  106.               stars[i].accz += f * (stars[j].posz - stars[i].posz) / d;
  107.             }
  108.           }
  109.         }
  110.       }
  111.  
  112.       // cálculo de las velocidades
  113.       for (int i = 0; i < NUM_STARS; i++) {
  114.         stars[i].spdx += stars[i].accx;
  115.         stars[i].spdy += stars[i].accy;
  116.         stars[i].spdz += stars[i].accz;
  117.       }
  118.  
  119.       // cálculo de las coordenadas
  120.       for (int i = 0; i < NUM_STARS; i++) {
  121.         stars[i].posx += (int) stars[i].spdx;
  122.         stars[i].posy += (int) stars[i].spdy;
  123.         stars[i].posz += (int) stars[i].spdz;
  124.       }
  125.  
  126.       repaint();
  127.       try {
  128.         Thread.sleep(tiempo_pausa);
  129.       } catch (InterruptedException e) {
  130.         e.printStackTrace();
  131.       }
  132.     } while (true);
  133.   }
  134. }
  135.  

No hay comentarios:

Publicar un comentario en la entrada