Representando caminos aleatorios en 2D (II)

Posted: 25/02/2013 in Java
Tags: , , , , ,

Esta es la continuación de la primera parte.

Ya sabemos lo que es un camino aleatorio, lo que es un fractal y de qué manera establecerán los números aleatorios generados por Java la dirección en la que se mueve nuestro bicho por el tablero de GridWorld.

El código fuente que contiene toda la acción (es decir, las llamadas a las funciones que se encargan de mover al bicho o de cambiar su dirección) se llama BugRunner.java. El código en su forma original está copiado en el primer post del blog. Lo único que hacía ese código era crear un tablero, crear un bicho, una roca (que no hace nada, solo está ahí para estorbar al bicho), incorporarlos al tablero y mostrarlo.
Ahora haremos más cosas. Primero que todo pegaré el código tuneado:

import info.gridworld.actor.ActorWorld;
import info.gridworld.actor.Bug;
import info.gridworld.actor.Rock;
import info.gridworld.grid.UnboundedGrid;
import info.gridworld.grid.Location;
 
public class BugRunner
{

	public static void justMove(Bug anyBug){
		if (anyBug.canMove()){
			anyBug.move();
		}
		else{
			anyBug.turn();			
		}
	}


	public static void randomBug(int n, Bug bug_three){
		double result;
		int i = 0;

		for(i = 0; i < n; i++){
			result = Math.random();
			if(result < 0.25){
				bug_three.setDirection(Location.NORTH);
				justMove(bug_three);		
			}
			else if((0.25 <= result) && (result < 0.5)){
				bug_three.setDirection(Location.EAST);	
				justMove(bug_three);	
			}
			else if((0.5 <= result) && (result < 0.75)){
				bug_three.setDirection(Location.SOUTH);		
				justMove(bug_three);
			}
			else{
				bug_three.setDirection(Location.WEST);	
				justMove(bug_three);	
			}
		}
	}	

    public static void main(String[] args)
    {
        int steps = 500000;
        ActorWorld world = new ActorWorld(new UnboundedGrid());
		Bug redBug = new Bug();
		world.add(redBug);
		randomBug(steps, redBug);
        world.show();
    }
}

Salvo quizá la función randomBug(), no parece muy complicado intuir qué es lo que hace la primera función, ¿verdad? Ésta simplemente recibe un objeto llamado Bug (que previamente creamos en la función principal, main) y decide qué es lo que hace el bicho según si éste tiene espacio para moverse en una dirección o no. La función canMove() evalua si el bicho puede avanzar una casilla en la dirección a la que apunta su cabeza; si puede, se ejecuta el método move() (no voy a ofenderte explicándote qué hace esta función), en caso contrario, el bicho gira 45º en sentido horario.

El cómo están escritos los métodos canMove(), turn() y move(), es lo de menos aquí. Lo único que hay que saber sobre éstos es que son métodos definidos en el objeto Bug (o sea, nuestro bicho), que a su vez hereda los métodos de la clase Actor, que es la que define todos los “protagonistas” de GridWorld (como los bichos, las rocas, o las flores que va dejando el bicho detrás suyo cada vez que avanza una casilla).

Ahora vamos a mirar la función randomBug. Si habéis atendido a las explicaciones dadas en el primer post, no hay mucho más que añadir, simplemente que antes de dar cada paso, redefinimos la dirección a la que apunta el bicho (la probabilidad de que apunte a una dirección u otra es, además, la misma) y a continuación llamamos justMove().

Finalmente, también hay algunos cambios en la función principal, como que en lugar de crear un tablero con los bordes definidos (como teníamos por defecto en la función principal) creamos un tablero sin bordes, esto es, infinito. Evidentemente esto es necesario para poder apreciar las formas tan extrañas que pinta el bicho (en forma de flores que va dejando por su paso).
Además de eso he quitado la roca que molesta, ya que la gracia de un camino aleatorio es que nada altere la partícula que lo traza.
Se crea el objeto redBug y se lo pasamos a la función randomBug, que hace el resto. Además, si nos fijamos, necesitaremos que el bicho se mueva muchas veces, ya que si solo avanzamos una casilla, tampoco podremos apreciar nada. Para eso definimos una variable que recibe un número y la pasamos también por valor a la función randomBug, que es la que utilizará en el bucle como cota superior y la que, por lo tanto, definirá el número de pasos que dará el bicho. Yo le he puesto medio millón de pasos. ¡Vaya caminata va a pegarse! 😀

También he modificado la función zoomOut() de la clase GridPanel, ya que la interfaz gráfica no me permetía alejarme del tablero todo lo que yo quería:

public void zoomOut()
{
    /* cellSize = Math.max(cellSize / 20, MIN_CELL_SIZE/20); */
 
    cellSize = cellSize / 10;
    revalidate();
}

Lo que está en ese comentario es la línea que había antes de que yo la sustituyera por

cellSize = cellSize / 10;

que es como funciona la función antagónica, zoomIn(), solo que ésta en vez de engrandecer el tablero diez veces lo hace simplemente el doble.

Ejecutamos el programa primero con la vista por defecto y después con el zoom:

Click para ampliar

Click para ampliar

No, no he puesto la pantalla negra del terminal ahí en medio para molestar, sino para que el bicho sea más fácil de ver. ¿Veis donde queda la cruz del botón anaranjado del terminal? Pues justo a la izquierda está el bicho. Y si os fijáis en la última línea del terminal, aparece las coordenadas del bichito. Simplemente he añadido esto en la penúltima línea en la función main:

System.out.println("Location: " + redBug.getLocation());

Porque imaginaos el panorama. Si yo ya tenía poca paciencia para encontrar a Wally entre quinientos muñecajos, imaginaos lo que supone buscar una mariquita entre medio millón de flores… 😛

Alejamos el zoom:

Click para ampliar

Click para ampliar

Súperzoom (las imágenes se verán más claras al darle con la lupa encima):

Click para ampliar

Click para ampliar

Bonito, ¿eh? 😀 Vamos a desplazarnos un poco a la izquierda del mapa:

Click para ampliar

Click para ampliar

Y ahora una ejecución más del mismo programa, directamente con el zoom a tope, solo para ver qué otras formas pintamos:

Click para ampliar

Click para ampliar

Click para ampliar

Click para ampliar

¿No resulta increíble que todas esas formas resulten de una sola función que devuelve números aleatorios? 🙂

Ahora es un buen momento para señalar un aspecto más de los caminos aleatorios. Resulta que en Física podemos modelar los gases como un conjunto muy vasto de partículas que se mueven a altas velocidades, chocando entre ellas, cambiando su dirección constantemente y, en definitiva, creando la sensación de que cada partícula se mueve de una forma absolutamente aleatoria.
Pero ocurre que ninguna de estas partículas se mueve de forma aleatoria, sino que está sometida a un proceso caótico, esto es, un proceso en el que una pequeña variación de las condiciones iniciales implica un cambio drástico en el comportamiento del sistema. Si tenemos una partícula encerrada en una caja, moviéndose siguiendo un patrón, podemos predecir su posición en cada instante de tiempo. También somos capaces de hacerlo con dos partículas. Pero con tres ya no (ver: problema de los tres cuerpos), al menos no de manera analítica, sino tirando ya de aproximaciones. Ocurre que en un gas no tenemos una, dos o tres partículas, sino que tenemos del orden de billones, de trillones de partículas… y todas ellas en movimiento, interactuando las unas con las otras, forman un sistema caótico. Ojo, no hay que confundir “caótico” con “aleatorio”. Un sistema caótico lo es en el sentido de que, debido a su complejidad, es imposible predecir su estado exacto en un momento dado, pero éste sigue sujeto a un conjunto reducido de leyes físicas: es determinista.

Pero ahora viene lo bueno. Resulta que los sistemas deterministas se comportan (cuidado, no confundir “comportarse” con “ser”) como sistemas aleatorios. Y aquí un ejemplo de ello:

Click para ampliar

Click para ampliar

¿Os resulta familiar? Pues no ha sido mi bicho. Esa imagen (extraída de la Wikipedia) es una representación de lo que se llama un proceso de Wiener, también llamado movimiento Browniano, que es el nombre que se le pone al movimiento (aparentemente) aleatorio que siguen las partículas de un gas, o de cualquier fluido en general. Precisamente el hecho de que los sistemas (caóticos) deterministas se comporten como si no lo fueran, ha servido de puente para que puedan ser estudiados en términos estadísticos y de probabilidades.
En particular, los caminos aleatorios sirven para modelizar los procesos de Wiener. Y lo mejor de todo es que estos procesos no quedan restringidos a la física estadística, sino que otras ramas de la ciencia como la Economía o la Biología también se sirven de los procesos de Wiener (o sea, de los caminos aleatorios) para modelar otros fenómenos, como la evolución de los precios del mercado o el flujo de moléculas en un proceso de difusión.

Advertisements
Comments
  1. […] ← Compilando mediante la variable CLASSPATH en el bash de Linux Representando caminos aleatorios en 2D (II) → […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s