Implementando un Camino Aleatorio
Tags: python, spanish
Published: 2022-10-06 14:37:30

Un sistema no determinista es aquel que incluye aleatoriedad y también se apoya en diferentes distribuciones de probabilidad para modelar sistemas complejos.

Los caminos aleatorios se utilizan en sistemas no deterministas e incluyen elementos aleatorios. En otras palabras son sistemas que no obtienen el mismo output para el mismo input al ejecutar varias veces la misma operación.

Los caminos aleatorios son usados por ejemplo en simulación de movimientos de partículas, el comportamiento de los mercados, etc.

Esta estrategia computacional se utiliza en especial en soluciones de software en las que se busca simular el comportamiento de un suceso o fenómeno físico, tal vez en el estudio del comportamiento de partículas o en la realización de software para ser usado en los mercados de financiero.

Pensemos en un programa que permite manejar el trafico en un cruce de la carretera. En otras palabras, que controla las luces del semáforo.

La forma no determinista de implementar este programa es entender cuales son los patrones de uso de la intersección (estos patrones se pueden obtener al usar calculo de probabilidades, realizando muchas veces una simulación del tráfico, entre otras formas de solución) y estimar cuando tendríamos que prender el semáforo a verde y cuando a rojo.

En este contexto, recordemos que la probabilidad es la medida de la certidumbre asociada a un evento o suceso futuro y suele expresarse como un numero entre 0 y 1.

```python
class Borracho:

    def __init__ (self, nombre):
        self.nombre = nombre
    

class BorrachoTradicional(Borracho):
    def __init__ (self, nombre):
        super().__init__(nombre)
    
    def caminar(self):
        return random.choice([(0,1), (0, -1), (1,0),(-1,0)])
```

```python
class Campo:
    def __init__(self):
        self.coordenadas_de_borrachos = {}

    def agregar_borracho(self, borracho, coordenada):
        self.coordenadas_de_borrachos[borracho] = coordenada
    
    def mover_borracho(self,borracho):
        delta_x, delta_y = borracho.caminar()
        coordenada_actual=self.coordenadas_de_borrachos[borracho]
        nueva_coordenada = coordenada_actual.mover(delta_x, delta_y)

        self.coordenadas_de_borrachos[borracho] = nueva_coordenada
    
    def obtener_coordenada(self, borracho):
        return self.coordenadas_de_borrachos[borracho]
```

```python
class Coordenada:
    def __init__(self, x,y):
        self.x = x
        self.y = y
    
    def mover(self, delta_x, delta_y):
        return Coordenada(self.x + delta_x, self.y + delta_y)
    
    
    def distancia(self, otra_coordenada):
        delta_x = self.x - otra_coordenada.x
        delta_y = self.y - otra_coordenada.y

        return (delta_x**2 + delta_y**2)**0.5
```

```python
from borracho import BorrachoTradicional
from campo import Campo
from coordenadas import Coordenada
from bokeh.plotting import figure, show

def caminata(campo, borracho, pasos):
    inicio = campo.obtener_coordenada(borracho)
    for _ in range(pasos):
        campo.mover_borracho(borracho)
    
    return inicio.distancia(campo.obtener_coordenada(borracho))

def simular_caminata(pasos, numero_de_intentos, tipo_de_borracho):
    borracho = tipo_de_borracho(nombre="David")
    origen = Coordenada(0,0)
    distancias = []

    for _ in range(numero_de_intentos):
        campo = Campo()
        campo.agregar_borracho(borracho, origen)
        simulacion_caminata = caminata(campo, borracho, pasos)
        distancias.append(round(simulacion_caminata,1))
    return distancias

def graficar(x, y):
    grafica = figure(title="Camino Aleatorio", x_axis_label="Pasos", y_axis_label="Distancia")
    grafica.line(x,y, legend="Distancia Media")
    show(grafica)

def main(distancias_de_caminata, numero_de_intentos, tipo_de_borracho):
    distancias_media_por_caminata=[]
    for pasos in distancias_de_caminata:
        distancias = simular_caminata(pasos, numero_de_intentos, tipo_de_borracho)
        distancia_media = round(sum(distancias)/len(distancias),4)
        distancia_maxima = max(distancias)
        distancia_minima = min(distancias)
        distancias_media_por_caminata.append(distancia_media)
        print(f'{tipo_de_borracho.__name__} caminata aleatoria de {pasos} pasos')
        print(f'Media = {distancia_media}')
        print(f'Max = {distancia_maxima}')
        print(f'Min = {distancia_minima}')
    graficar(distancias_de_caminata, distancias_media_por_caminata)


if __name__ == '__main__':
    distancias_de_caminata = [10,100,1000,10000]
    numero_de_intentos = 100

    main(distancias_de_caminata, numero_de_intentos,BorrachoTradicional)
```