Introducción

1

Programación Dinámica y Estocástica: Optimización y Modelado de Datos

Programación Dinámica

2

Programación Dinámica: Optimización de Problemas con Memorización

3

Optimización de Algoritmos con Programación Dinámica en Python

Caminos Aleatorios

4

Simulaciones con Caminos Aleatorios en Programación

5

Camino Aleatorio en Programación Orientada a Objetos

6

Algoritmo de Caminata Aleatoria en Python: Clase Borracho

7

Simulación de Caminata Aleatoria con Python

8

Visualización de Caminatas Aleatorias con Python y Bokeh

Programas Estocásticos

9

Programación Estocástica: Aplicaciones y Ejemplos Prácticos

10

Cálculo de Probabilidades y Simulación de Montecarlo

11

Simulaciones de Probabilidades con Dados en Python

12

Inferencia Estadística: Conceptos y Aplicaciones Prácticas

13

Cálculo de la Media Aritmética en Python paso a paso

14

Media, Varianza y Desviación Estándar en Estadística

15

Distribución Normal: Propiedades y Aplicaciones Estadísticas

Simulaciones de Montecarlo

16

Simulaciones de Montecarlo: Historia y Aplicaciones Prácticas

17

Simulación de Montecarlo para Probabilidades en Juegos de Cartas

18

Simulaciones de Montecarlo para Aproximar Pi

19

Estimación de Pi mediante Monte Carlo y Simulación Estadística

Muestreo e Intervalos de Confianza

20

Muestreo Estadístico: Aleatorio y Estratificado

21

Teorema del Límite Central: Transformación de Distribuciones

Datos Experimentales

22

Validación de teorías científicas con datos experimentales

23

Regresión Lineal con NumPy y Matplotlib en Google Colab

Conclusiones

24

Optimización de Programas con Programación Dinámica y Simulaciones

No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Visualización de Caminatas Aleatorias con Python y Bokeh

8/24
Recursos

¿Cómo ejecutar el código y corregir errores comunes?

Es frecuente encontrarse con errores al ejecutar código, pero cada uno es una oportunidad para aprender. Para ejecutar un script de Python, abre una terminal, navega a la carpeta del proyecto y usa el comando python script.py. Si aparece un error, no te preocupes. En este caso, los errores encontrados fueron:

  • Nombre no definido: En el código se intentó utilizar la función rango, cuando debería ser range(). Corrige el nombre y vuelve a intentarlo.

  • Número incorrecto de argumentos: Al definir métodos dentro de una clase en Python, siempre se debe incluir self como primer parámetro. Corrige esto para evitar errores de argumentos.

Estos pasos básicos te ayudarán a ejecutar tu código correctamente y aprender qué mejorar cuando se presenten fallos.

¿Cómo interpretar los resultados de la simulación de caminos aleatorios?

Al ejecutar la simulación de caminos aleatorios, obtienes resultados como la media, distancia máxima y mínima de pasos. En este ejemplo:

  • 10 pasos: Media de distancia fue 3, máxima 7, mínima 0. El borracho regresó al origen.
  • 100 pasos: Alejamiento gradual, pero regresos frecuentes al origen.
  • 1000 pasos: Mayor alejamiento del origen.
  • 10,000 pasos: Incremento en la distancia máxima y mínima.

Estos resultados muestran cómo, aun con eventos aleatorios incorporados, la simulación se comporta de manera consistente con lo esperado estadísticamente. Cada ejecución te dará resultados ligeramente diferentes debido a su naturaleza estocástica.

¿Cómo graficar los datos con la librería Bokeh?

Visualizar los datos puede brindarte una mejor comprensión de los resultados. Para esto, usamos la librería Bokeh, recomendada por su facilidad de uso:

  1. Configura un entorno virtual para no instalar la librería de manera global:

    python -m venv venv
    source venv/bin/activate # Linux/MacOS
    .\venv\Scripts\activate  # Windows
    
  2. Instala Bokeh:

    pip install bokeh
    
  3. Importa y utiliza Bokeh en el código:

    from bokeh.plotting import figure, show
    
    def graficar(x, y):
        plot = figure(title="Camino aleatorio", x_axis_label='Pasos', y_axis_label='Distancia')
        plot.line(x, y, legend_label="Distancia Media")
        show(plot)
    
  4. Ejecuta tu código para ver la gráfica después de haber añadido los pasos para los ejes.

¿Qué reto puedes intentar para continuar el aprendizaje?

Como ejercicio práctico, te animamos a modificar la clase del borracho o crear una nueva implementación. Varía el comportamiento, como el incremento en los pasos hacia cualquier dirección, y observa cómo afectas la simulación.

  • Cambia los pasos a más frecuentes en una dirección.
  • Prueba múltiples direcciones con diferentes probabilidades.
  • Compara el comportamiento de diferentes "borrachos" entre sí.

Así, entenderás cómo los ajustes en tu código pueden alterar los resultados, y compartir tus ajustes con la comunidad enriquecerá tu comprensión. Si tienes dudas o comentarios, usa los foros para obtener ayuda. ¡Continúa explorando y aprendiendo!

Aportes 207

Preguntas 50

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Les presento al Drogado, la variante que pensé para resolver el reto de esta clase 😂 hace caminos aleatorios con floats en lugar de ints. Aquí el código y una gráfica de cada trayecto, dependiendo si hizo 10, 100, 1000 o 10000 pasos:

class Drogado(Borracho):

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

    def camina(self):
        return (
            random.choice([
                (random.random(), random.random() * -1),
                (random.random() * -1, random.random()),
                (random.random() * -1, random.random() * -1),
                (random.random(), random.random()),
            ])
        )

Para los que se están preguntando como hacer los caminitos aquí les dejo un código corto usando las mismas clases implementadas BorrachoTradicional Campo y Coordenada

from borracho import BorrachoTradicional
from coordenada import Coordenada
from campo import Campo

from bokeh.plotting import figure, show

def main(distancia, inicio, borracho):
    campo = Campo()
    campo.anadir_borracho(borracho, inicio) #poner un borracho en origen
    ejecutar_caminata(campo, borracho, distancia)

def ejecutar_caminata(campo, borracho, distancia):
    x_arreglo = []
    y_arreglo = []
    x_arreglo.append(campo.obtener_coordenada(borracho).x)
    y_arreglo.append(campo.obtener_coordenada(borracho).y)
    for _ in range(distancia):
        campo.mover_borracho(borracho) #se actualiza las coordenadas del borracho
        x_arreglo.append(campo.obtener_coordenada(borracho).x)
        y_arreglo.append(campo.obtener_coordenada(borracho).y)

    graficar(x_arreglo, y_arreglo)

def graficar(x, y):
    figura = figure()
    figura.line(x, y)
    show(figura)

if __name__ == '__main__':
    distancia = 1000000
    inicio = Coordenada(0,0)
    borracho = BorrachoTradicional('Angel')
    main(distancia, inicio, borracho)

solo tendrán que modificar distancia para hacer sus gráficas más grandes 😉

Esta es la gráfica con un millón de pasos

Esta caminata fue de 5000 pasos. Inicio punto negro, final punto rojo; abajo está el ajuste del código para este tipo de gráfica. Me tome 6 horas entre entender el código y hacer la modificación, pero valió la pena el proceso.

from borracho import BorrachoTradicional
from campo import Campo
from coordenada import Coordenada

from bokeh.plotting import figure, show

def caminata(campo, pasos, tipo_de_borracho):

    borracho = tipo_de_borracho(nombre='David')
    origen = Coordenada(0, 0)
    campo.anadir_borracho(borracho, origen)

    coordenadas_x=[]
    coordenadas_y=[]

    coordenadas_x.append(origen.x)
    coordenadas_y.append(origen.y)

    for _ in range(pasos):
        campo.mover_borracho(borracho)
        coordenadas_x.append(campo.obtener_coordenada(borracho).x)
        coordenadas_y.append(campo.obtener_coordenada(borracho).y)

    return (coordenadas_x, coordenadas_y)

def graficar(x, y,pasos):
    grafica = figure(title='Random Walks',x_axis_label='x axis', y_axis_label='y axis')
    grafica.line(x, y, legend_label='walk', color='yellowgreen',name='juan') #recorrido

    # gráfica la línea inicial de movimiento porque no se puede graficar un punto
    grafica.line(x[0:2],y[0:2],color='black',line_width=10)
    grafica.line(x[-3:-1],y[-3:-1],color='red',line_width=10) #punto final y final-1
    grafica.line(x[0:-1:pasos-1],y[0:-1:pasos-1]) #linea de punto inicial a punto final
    show(grafica)

def main(pasos, tipo_de_borracho):
    campo=Campo()
    coordenadas_x, coordenadas_y = caminata(campo, pasos, tipo_de_borracho)
    graficar(coordenadas_x, coordenadas_y,pasos)

if __name__ == '__main__':

    pasos = 5000
    main(pasos, BorrachoTradicional)

Creo que a esta parte del curso le hace falta una implementación antes de Caminos Aleatorios, algo más simple como simular una partida de dados o algún juego de mesa sencillo. Por mucho que David vaya guiando, no es una buena práctica dar brincos sensibles de complejidad. Me refiero a que para explicar este problema necesitaron 3 lecciones (de más de 10 minutos) cuando sus ejemplos previos son explicados, casi sin excepción, en 1 lección. Un problema explicado en 2 lecciones hubiera sido un salto más prudente, o explicar el mismo problema en diferentes capas de complejidad (por ejemplo, comenzar explicándolo de manera iterativa y después añadir POO), así los alumnos con bases sólidas previas podrían brincar a las lectures con POO y los que lo necesiten, podrían comenzar con el caso más sencillo.

En este tutorial el profesor explica todo pero haciendo una introducción partiendo de una solución sencilla que se entiende con facilidad, después en vídeos posteriores propone soluciones más sofisticadas. La solución está en javascript, pero el punto es la manera en que propone su approach:
Coding Challenge #52: Random Walker

Esa es mi opinión, con base en mi experiencia (también soy profesor).

Esta es mi solución a la variación solicitada en la lecture:

Haciéndole unos pequeños cambios a la implementación se pueden ver los caminos que harían n borrachos con k cantidad de pasos xD.

Para hacer la implementación decidí partir del objetivo central y hacerlo desde cero. Creo que así se puede entender mejor el propósito y la funcionalidad del algoritmo presentado en la clase.

Hice el programa para que se moviera en tres dimensiones, relicé las graficas con matplotlib ya que es el que sé utilizar. Adjunto los gif de 10, 1000 y 10000 pasos.

100 pasos

1000 pasos

10000 pasos

¡Por fin! Han sido valiosos los apuntes y aportes de varios compañeros, especialmente compartiendo la manera en que generaron la gráfica del camino aleatorio del borracho. Confieso que hubo un momento en que me desanimé un poco. Sin embargo dejé pasar algunos días, vi videos de otros cursos y retomé el ejercicio. Finalmente tengo estas 2 gráficas del camino aleatorio, para los mismos parámetros, se ven 2 comportamientos aleatorios muy distintos.

En mi caso agregué 2 tipos de borrachos mas, uno muy mareado, que de repente no avanza es decir, una delta de (0,0) y otro que nunca va a la izquierda:

Me llamó la atención que al quitarle una dirección la curva se vuelve lineal.

De paso grafiqué los caminos:

Si alguien le sirve aquí mi código: https://github.com/raag/caminos_aleatorios

Borracho aguardientero.

Mi propuesta se llama BorrachoDoblado al cual tiene más probabilidad de moverse hacía arriba o hacía la derecha sumando una unidad a su coordenada actual. Sin embargo, cae en la opción de moverse hacía abajo o a la izquierda, se modificará en 2 unidades su coordenada actual.

class BorrachoDoblado(Borracho):

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

	def camina(self):
		return random.choice([(0,1),(0,1),(0,-2),(1,0),(1,0),(-2,0)])

La gráfica resultante con distancias_de_caminata = 10000 y numero_de_intentos = 1000 es la siguiente:

Bueno, en mi caso: Pausa y a revisar todo lo que he aprendido de Python hasta ahora.

Este fue el camino de 2 borrachos con 1,000:

con 10,000:

con 1,000,000 de pasos:

2 noches para pasar de este video…

yo quebrandome la cabeza con tres borrachos simples y veo que hay quien saco gráficas que parecen mapas >.<

porque comparten esos mapas con manchas si la cosa es una linea no mas. pasos vs distancia

Con 100000 pasos

Hola comunidad.

Para los que tengan problema con el entorno virtual en windows: https://platzi.com/tutoriales/1104-python/5883-entornos-virtuales-de-python-en-windows/

He hecho todos los cursos de Data Science hasta acá, y para ser honesto, no entiendo nada

Ahora, un par de simulaciones más de izquierda a derecha respectivamente 100 mil y 1 millón de intentos:

import random, math
from bokeh.plotting import figure, show

class Borracho:

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


class BorrachoTradicional(Borracho):

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

    def camina(self):
        return random.choice([(0, 1), (0, -1), (1, 0), (-1, 0)])


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.x - otra_coordenada.y

        return math.sqrt(delta_x ** 2 + delta_y ** 2)


class Campo:

    def __init__(self):
        self.coordenadas_borrachos = {}

    def anadir_borracho(self, borracho: Borracho, coordenada: Coordenada):
        self.coordenadas_borrachos[borracho] = coordenada

    def mover_borracho(self, borracho: Borracho):
        delta_x, delta_y = borracho.camina()
        coordenada_actual = self.coordenadas_borrachos[borracho]
        nueva_coordenada = coordenada_actual.mover(delta_x, delta_y)
        self.coordenadas_borrachos[borracho] = nueva_coordenada

    def obtener_coordenada(self, borracho: Borracho):
        return self.coordenadas_borrachos[borracho]


def caminata(campo, borracho, pasos):
    x_arreglo = []
    y_arreglo = []
    inicio = campo.obtener_coordenada(borracho)
    x_arreglo.append(inicio.x)
    y_arreglo.append(inicio.y)
    for _ in range(pasos):
        campo.mover_borracho(borracho)
        donde_se_movio = campo.obtener_coordenada(borracho)
        x_arreglo.append(donde_se_movio.x)
        y_arreglo.append(donde_se_movio.y)


    graficar(x_arreglo, y_arreglo)
    return inicio.distancia(campo.obtener_coordenada(borracho))


def simular_caminata(pasos, numero_de_intentos, tipo_de_borracho):
    borracho = tipo_de_borracho(nombre='Benjamin') #Funcion agnostic al tipo
    origen = Coordenada(0, 0)
    distancias = []
    for _ in range(numero_de_intentos):
        campo = Campo()
        campo.anadir_borracho(borracho, origen)
        simulacion_caminata = caminata(campo, borracho, pasos)
        distancias.append(round(simulacion_caminata))

    return distancias

def graficar(x, y):
    grafica = figure(title='Camino Aleatorio Borrachos', 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)
        print(len(distancias))
        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__} tuvo una caminata aleatoria de {pasos} pasos')
        print(f'La distancias media es {distancia_media}')
        print(f'La distancia max es {distancia_maxima}')
        print(f'La distancia min es {distancia_minima}')



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

    main(distancias_de_caminata, numero_de_intentos, BorrachoTradicional)

Hola a todos, les comparto mi grafica con 10,000 pasos. Esta mi subclase, el psicópata, en azul (toma valores aleatorios entre -1 y 1 para x y y) y el borracho tradicional del profesor (en rojo). A su vez el amarillo y el verde son cambinaciones de las xs y ys del borracho con el psicópata. Agradezco sus comentarios.

Estuvo chevere el reto, yo hice un borracho que no pueda ir hacia atras, me disculpan si el codigo se ve desordenado que no soy muy de python, aunque me gusta.

class NoReturnDrunk(Drunk):
	def __init__(self, name):
		self.last_move = (0, 0)
		super().__init__(name)

	def walk(self):
		choices = [(0, 1), (0, -1), (1, 0), (-1, 0)]
                choice_back = (self.last_move[0] * -1, self.last_move[1] * -1)
		if choice_back in choices:
			choices.remove(choice_back)

		self.last_move = random.choice(choices)
		return self.last_move

Basicamente guardo el ultimo movimiento del borracho, luego lo invierto (porque si avanza en X, echarse para atras seria retroceder en X, lo mismo con Y) y luego descarto esa opcion, de las opciones posibles de movimiento.

Esto es con 100 pasos:

1000 pasos:

10000 pasos:

Buenas les dejo mi aporte quize agregar un poco de color a todo esto!!
Las pruebas son de [100000, 10000, 1000, 100] con 100 intentos cada una.
También realice una clase aparte para manejar el grafico:

from bokeh.plotting import figure, show
import random

class Chart:

    def __init__(self, title, x_label,  y_label):
        self.chart = figure(title=title, x_axis_label=x_label, y_axis_label=y_label)

    def draw_line(self, x_axis, y_axis):
        colors = ['green', 'red', 'blue', 'yellow', 'orange', 'pink', 'violet', 'gray', 'black', 'brown']
        self.chart.line(x_axis, y_axis, color=random.choice(colors))

    def show_chart(self):
        show(self.chart)

Cada intento intento toma un color aleatorio de una lista dentro de la clase Chart:

Solo debemos cambiar la distancia por coordenadas.

Hice un tutorial para este curso, sobre una manera de implementar este proyecto, con muy pocas líneas de código, usando sólo 3 funciones cortas y una clase.

Hice 3 tipos de caminantes diferentes, basándome en piezas de ajedrez:

Torre: Se mueve hacia arriba, abajo, izquierda o derecha:


.
Alfil: Se mueve en diagnonal:


.
Reina: Se mueve en todas las direcciones:

Modifiqué la función para que tenga más avance en una sola dirección

En la simulación con igualdad de distancia de movimiento:
Con la nueva función:

También grafiqué el último camino recorrido en ambos casos.
Cuando los avances son iguales:
Y esto es cuando se cambia la magnitud del avance en una sola dirección

He decidido extender un poco el proyecto del profesor os adjunto mi codigo https://gitlab.com/emidev98/ai-and-ml/-/tree/master/random_paths

Termine hasta mareado despues de la clase XD

no me salio nada d esto, puta vida

Corrí los siguientes comandos en el terminal para poder ejecutar bokeh en windows:

PS D:\18.PLATZI\1.INTELIGENCIA ARTIFICIAL\PRUEBA> py -m venv env
PS D:\18.PLATZI\1.INTELIGENCIA ARTIFICIAL\PRUEBA> cd env
PS D:\18.PLATZI\1.INTELIGENCIA ARTIFICIAL\PRUEBA\env> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process
PS D:\18.PLATZI\1.INTELIGENCIA ARTIFICIAL\PRUEBA\env> cd Srcripts
PS D:\18.PLATZI\1.INTELIGENCIA ARTIFICIAL\PRUEBA\env\Scripts> .\activate
(env) PS D:\18.PLATZI\1.INTELIGENCIA ARTIFICIAL\PRUEBA\env\Scripts> pip install bokeh
(env) PS D:\18.PLATZI\1.INTELIGENCIA ARTIFICIAL\PRUEBA\env\Scripts> cd…

Hola,

A la clase Borracho le agregue pesos a cada una de las coordenadas como se muestra en el siguiente código.

import random

class Borracho:

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

class BorrachoTradicional(Borracho):

    def __init__(self, name):
        super().__init__(name)

    def camina(self):
        elements = [(0, 1), (0, -1), (1, 0), (-1, 0)]
        weigths = [0.4, 0.3, 0.2, 0.1]
        return random.choices(elements, weigths)

Como se muestra en la siguiente gráfica el resultado de la gráfica dado los pasos dados.

En el caso de la distancia recorrida la gráfica no cambia su forma

Excelente curso hasta ahora. Aplique la misma lógica de David en Unity para simular el camino aleatorio de un enemigo.

El video: https://giphy.com/gifs/2rs7Esu4W8eenQlNHw/html5

public class EnemyController : MonoBehaviour
{
    ....
    private Animator _animator;
    private Rigidbody2D _rb;

    private Vector2[] _path =
    {
        Vector2.left,
        Vector2.right,
        Vector2.up,
        Vector2.down
    };
    void Update()
    {
        timeToMoveCounter -= Time.deltaTime;

        if(timeToMoveCounter < 0)
        {
            Instantiate(fire1, this.transform.position, Quaternion.identity);
            
            int randonIndex = Random.Range(0, _path.Length);
            _movement = _path[randonIndex];
            
            _rb.velocity = _movement * speed;

            _animator.SetFloat("horizontal", _movement.x);
            _animator.SetFloat("vertical", _movement.y);

            timeToMoveCounter = timeToMove;


        }

        
    }

}

Acá dejo las clases con la modificación a borracho en que tendrá el cuarenta por ciento (40 %) de veces, la posibilidad de ir hacia arriba, repitiendo las cordenadas x = 0, y = 1. Como se puede ver en las gráficas al final de este largo post, la distancia recorrida en promedio es mucho mayor y así mismo es mucho menor la posibildiad de volver al punto de partida.

Clase borracho modificada:

import random

class Borracho:

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

class BorrachoTradicional(Borracho):

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

    def camina(self):
        return random.choice([(0, 1), (0, 1), (0, -1), (1, 0), (-1, 0)])

Clase Campo:

class Campo:

    def __init__(self):
        self.coordenadas_de_borrachos = {}

    def anadir_borracho(self, borracho, coordenada):
        self.coordenadas_de_borrachos[borracho] = coordenada

    def mover_borracho(self, borracho):
        delta_x, delta_y = borracho.camina()
        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_coordenadas(self, borracho):
        return self.coordenadas_de_borrachos[borracho]

Clase Coordenada:

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 # raiz cuadrada del cuadrado de los catetos

Camino aleatorio:

from borracho import BorrachoTradicional
from campo import Campo
from coordenada import Coordenada
from bokeh.plotting import figure, show

def caminata(campo, borracho, pasos):
    inicio = campo.obtener_coordenadas(borracho)

    for _ in range(pasos):
        campo.mover_borracho(borracho)

    return inicio.distancia(campo.obtener_coordenadas(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.anadir_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_label='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__} tuvo una caminata aleatoria de {pasos} pasos')
        print(f'Media = {distancia_media}')
        print(f'Máxima = {distancia_maxima}')
        print(f'Mínima = {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)

Gráfica inicial sin modificación en Borracho:

Gráfica inicial con modificación en Borracho:

el mio es un borracho que va hacia el norte jajaja

def camina(self):
        return random.choice([(0, 1), (0, -1), (1.5, 0), (-1, 0)])```

depronto la gráfica se desenreda

Alfin después de ver tantas veces esto, y leer varios comentarios:

con 1000000 pasos

1 millón de pasos:


from borracho import BorrachoTradicional
from campo import Campo
from coordenada import Coordenada
from bokeh.plotting import figure, show

def caminata(campo, borracho, pasos):    
    
    inicio = campo.obtener_coordenada(borracho)
    x1=[]
    y1=[]    

    for _ in range(pasos):
        campo.mover_borracho(borracho)
        x1.append(campo.obtener_coordenada(borracho).x)
        y1.append(campo.obtener_coordenada(borracho).y)
    
    graficar(x1,y1)
    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.anadir_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='X',y_axis_label='Y')
    grafica.line(x, y, legend_label='Caminata')

    show(grafica)

def main(distancias_de_caminata,numero_de_intentos,tipo_de_borracho):

    distancia_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)
        distancia_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,distancia_media_por_caminata)

if __name__== '__main__':
    
    distancias_de_caminata = [1000000]
    numero_de_intentos = 1

    main(distancias_de_caminata,numero_de_intentos, BorrachoTradicional)


![](

Hice mi propia version!! added some new features.
Si gustan pueden ir a clonares el repo y probarlo. Me haria ilusion. acepto pull requeest.

repo en github

Hola amigos,

Les comparto mi resultado obtenido para un recorrido de 10 millones de pasos así como la implementación de mi código en Python (la cual no varía mucho de varias soluciones aquí descritas). En rojo muestro el punto inicial (0,0) así como el punto final. El archivo PNG escrito con bokeh pesaba 96MB así que decidí tomarle un screenshot para poder subirlo sin problemas aquí 😅

Código implementado:

from borracho import BorrachoTradicional, BorrachoDoble
from campo import Campo
from coordenada import Coordenada
from bokeh.plotting import figure, show
from bokeh.io import export_png

def graficar(x, y):
    grafica = figure(title = 'Camino aleatorio', x_axis_label='pasos', y_axis_label='distancia media')
    grafica.line(x,y, legend_label='camino recorrido')
    
    inicio_final_x = [x[0], x[-1]]
    inicio_final_y = [y[0], y[-1]]
    grafica.circle(inicio_final_x, inicio_final_y, size=7, fill_color="red")
    export_png(grafica, filename="camino_borracho_10M.png")

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, tipo_de_borracho):
    borracho = tipo_de_borracho(nombre = 'David')
    origen = Coordenada(0,0)
    campo = Campo()
    campo.anadir_borracho(borracho, origen)
    arreglo_coords_x = []
    arreglo_coords_y = []

    arreglo_coords_x.append(campo.obtener_coordenada(borracho).x)
    arreglo_coords_y.append(campo.obtener_coordenada(borracho).y)

    for _ in range(pasos):
        campo.mover_borracho(borracho)
        arreglo_coords_x.append(campo.obtener_coordenada(borracho).x)
        arreglo_coords_y.append(campo.obtener_coordenada(borracho).y)
    
    return (arreglo_coords_x, arreglo_coords_y)

def main(numero_de_pasos, tipo_de_borracho):
    arreglo_x, arreglo_y = simular_caminata(numero_de_pasos, tipo_de_borracho)
    graficar(arreglo_x, arreglo_y)

if __name__ == '__main__':
    numero_de_pasos = 10000000
    main(numero_de_pasos, BorrachoTradicional)```

![](

con 1000000

 random.choice([(0,2), (0,-2), (0,1), (0,-1), (2,0), (-2,0), (1,0), (-1,0)])

Yo el único cambio que hice es darle al borracho intención de irse derechito. Asi que e duplique el peso o probabilidad de irse en (0,1), el arreglo queda:

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

La gráfica de los pasos que dio refleja su intención de irse derecho:

Aumente las opciones del random choice para que el Boraacho pudiera moverse en 8 direcciones y sea más libre. Al parecer o vario mucho a cuando solo podía moverse en 4.

class BorrachoTodasDirecciones(Borracho):

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

    
    def camina(self):
        return random.choice([(0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1), (-1, 0), (-1, 1)])

Generé otra subclase Borracho que avanza más de lo que retrocede. El crecimiento de la distancia media fue más líneal sin tener que romperse un crecimiento más acelerado.

class BorrachoAvanzaMasRetrocedeMenos(Borracho):

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


    def camina(self):
        return random.choice([(0, 4), (0, -2), (4, 0), (-2, 0)])

Buenas.
Yo implementé un borracho con más probabilidad de ir hacia el norte:
class BorrachoNorte(Borracho):

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

def camina(self):
    return random.choice([(0, 1), (0, -1), (1, 0), (1, 0), (-1, 0)]) #Con más probabilidad de ir hacia el norte

y cambia mucho los valores medios:
BorrachoNorte caminata aleatoria de 10 pasos
Media = 3.435
Max = 8.2
Min = 0.0
BorrachoNorte caminata aleatoria de 100 pasos
Media = 20.688
Max = 47.0
Min = 3.2
BorrachoNorte caminata aleatoria de 1000 pasos
Media = 201.308
Max = 272.4
Min = 143.2
BorrachoNorte caminata aleatoria de 10000 pasos
Media = 1998.083
Max = 2205.8
Min = 1809.3

Slds

mi borracho se murió apenas ejecuté el código

Se genera el ambiente virtual.

Codigo base de la clase:

import random
from bokeh.plotting import figure, show


class Borracho:

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

class BorrachoTradicional(Borracho):

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

    def camina(self):
        return random.choice([(0, 1), (0, -1), (1, 0), (-1, 0)])

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

    def anadir_borracho(self, borracho, coordenada):
        self.coordenadas_de_borrachos[borracho] = coordenada
    
    def mover_borracho(self, borracho):
        delta_x, delta_y = borracho.camina()
        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_coordenadas(self, borracho):
        return self.coordenadas_de_borrachos[borracho]

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


def caminata(campo, borracho, pasos):
    inicio = campo.obtener_coordenadas(borracho)

    for _ in range(pasos):
        campo.mover_borracho(borracho)

    return inicio.distancia(campo.obtener_coordenadas(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.anadir_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}')
        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)```

Problema:
Suponga que ud. sale de su casa a comprar un medicamento, para una persona a la cual quiere mucho,y desea volver en el m ́ınimo tiempo posible. Si no lo compra, la persona sufrir ́a un perjuicio en su salud,cosa que por supuesto ud. no desea. En la ciudad hayNfarmacias.El tiempo de viaje para ir desde la farmaciaia la farmaciajestij[s]i, j∈0, . . . , N, donde la “farmacia0” representa su casa.El medicamento que ud. desea comprar es muy escaso y es posible que no se encuentre en todas lasfarmacias. Existe una probabilidad a prioripique el medicamento est ́e en la farmaciai. La presencia delmedicamento en 2 farmacias cualesquiera son v.a. dependientes en probabilidad: si ud. visita algunasfarmacias y encuentra que el medicamento no est ́a disponible en ellas, esa informaci ́on modifica lasprobabilidades de encontrarlo en las restantes (en general disminuye la probabilidad de encontrarlo enlas que est ́an cerca de una farmacia donde no est ́a el medicamento). Vale decir, la probabilidad deencontrarlo en la farmaciaies funci ́on del itinerario previo.Entrar a una farmacia a preguntar por el medicamento toma un tiempo deτ[s].

  1. ¿Cuantas farmacias visitar ud a lo sumo?
  2. Formule un modelo de programaci on dinamica que permita decidir su itinerario de manera deminimizar el valor esperado del tiempo hasta volver a su casa con el medicamento.

Todavia no estoy muy seguro del 2, pense tomar los recorridos que menos tomaron viajes y ver cual era la primeras droguerias que era necesario visitar, cualquier aporte o mejora seria de gran ayuda 😄

# -*- coding: utf-8 -*-
"""
Created on Sat May 30 09:03:24 2020

@author: Daniel Galvis
"""
import pandas as pd
import os
from random import randint,random,uniform
from bokeh.plotting import figure,show

class Farmacia:
    def __init__(self,nombre,proba,cordx,cordy):
        self.nombre=nombre
        self.proba=proba
        self.cordx=cordx
        self.cordy=cordy
        self.visit=0
        
    def distancia(self,cordx_m,cordy_m):
        dist=((self.cordx-cordx_m)**2+(self.cordy-cordy_m)**2)**(1/2)
        return dist

class Persona:
    def __init__(self,nombre,cordx,cordy):
        self.nombre=nombre
        self.cordx=cordx
        self.cordy=cordy
        self.distancia=0
        self.farmacia='Casa'
        
    def mover(self,nombre,cordx_m,cordy_m):
        recorrido=((self.cordx-cordx_m)**2+(self.cordy-cordy_m)**2)**(1/2)
        self.distancia=self.distancia+recorrido
        self.farmacia=nombre
        self.cordx=cordx_m
        self.cordy=cordy_m
        
    def escoger(self,randomico,listafarmacias):
        listafarmacias[randomico-1].visit=1
        farmacia_pivote=listafarmacias[randomico-1]
        return farmacia_pivote.nombre,farmacia_pivote.cordx,farmacia_pivote.cordy
    
#Extraer los datos del dataframe y convertirlos en una lista con la variable clase
def leerdatos(direccion,documento):
    os.chdir(direccion)
    dataframe_farmacias=pd.read_csv(documento,sep=';',header=None)
    fil,col=dataframe_farmacias.shape
    listafarmacias=[]
    for i in range(fil):
        farmaciapiv=Farmacia(dataframe_farmacias.iloc[i,0],dataframe_farmacias.iloc[i,3],dataframe_farmacias.iloc[i,1],dataframe_farmacias.iloc[i,2])
        listafarmacias.append(farmaciapiv)
    return listafarmacias,fil


def reduccionproba(listafarmacias,probabilidad,cordx,cordy):
    for i in listafarmacias:
        if i.visit==1:
            continue
        else:
            distancia=i.distancia(cordx,cordy)
            if distancia > 10:
                continue
            else:
                i.proba=i.proba*probabilidad
    pass
        
#Función main
def main(nombre,cordinicialx,cordinicialy,listafarmacias,fil):
    contador=0
    listamovx=[]
    listamovy=[]
    persona=Persona(nombre,cordinicialx,cordinicialy)
    listamovx.append(cordinicialx)
    listamovy.append(cordinicialy)
    condicion=False
    while condicion==False:
        farmacia,cordx,cordy=persona.escoger(randint(0,fil),listafarmacias)
        if farmacia==False:
            if contador>len(listafarmaciasi):
                return None,listamov
            continue
        else:
            listamovx.append(cordx)
            listamovy.append(cordy)
            persona.mover(farmacia,cordx,cordy)
            probabilidad=listafarmacias[int(persona.farmacia[9:])-1].proba
            if random()>probabilidad:
                reduccionproba(listafarmacias,probabilidad,cordx,cordy)
                continue
            else:
                condicion=True
        contador+=1        
    listamovx.append(cordinicialx)
    listamovy.append(cordinicialy)
    persona.mover("Casa", cordinicialx, cordinicialy)
    return persona.distancia,listamovx,listamovy


def graficar(x,y,nombre,intento):
    nombre_grafica=f'Recorrido de {nombre} intento {intento}'
    grafica=figure(title=nombre_grafica,x_axis_label='Coordenada x', y_axis_label='Coordenada y')
    grafica.step(x,y, legend="Distancia media")
    show(grafica)
    
    
if __name__=='__main__':
    #__________DATOS INICIALES_______________
    nombre="Daniel"
    cordenada_inicial_x=0
    cordenada_inicia_y=0
    intentos=10000
    distancias=[]
    listamovtotalesx=[]
    listamovtotalesy=[]
    direccion="D:/Documentos/Cursos_Daniel_2020/Cursos_Python/4_Platzi_Course_dinamicayestocastica"
    documento='Farmaciasexcel.csv'
    listafarmacias,fil=leerdatos(direccion,documento)
    listafarmacias_inicial=listafarmacias
    #________________________________________
    i=0
    while i<intentos:
        listafarmacias=listafarmacias_inicial
        dist,listamovx,listamovy=main(nombre,cordenada_inicial_x,cordenada_inicia_y,listafarmacias,fil)
        distancias.append(dist)
        listamovtotalesx.append(listamovx)
        listamovtotalesy.append(listamovy)
        i+=1
        
    distanciatotal=sum(distancias)/len(distancias)
    print(f'La persona {nombre} recorrio una distancia promedio de {distanciatotal} km para encontrar su medicina')
    
    intento_a_graficar=63
    graficar(listamovtotalesx[intento_a_graficar],listamovtotalesy[intento_a_graficar],nombre,intento_a_graficar)
    
    media_de_viajes=0
    for i in listamovtotalesx:
        media_de_viajes=media_de_viajes+len(i)
        
    media_de_viajes=int(media_de_viajes/len(listamovtotalesx))
    print(f'La media de viajes es {media_de_viajes}')

Mi gráfica para los movimientos del borracho con 10mm de pasos

Dejo código del reto con comentarios:

![](

'''
Este es el programa principal, desde este punto se crearan los borracho
Se indicacara cuando se debe mover
Y se actualizaran las posiciones en el campo
Para esta aplicación es necesario crear un ambiente virtual
python3 -m venv env
Abrirlo en Windows
source env/Scripts/activate
Si usa linux Scripts se remplaza por bin
Instalar bokeh para la libreria grafica
pip instal bokeh
'''
#Para esto primero importamos nuestras clases (los programas que creamos en la calse anterior)
#Primiero se indica el nombre del archivo y luego la clase
from bokeh.plotting import figure, show, output_file
from borracho import BorrachoTradicional, BorrachoNortenio, BorrachoSurenio
from coordenada import Coordenada
from campo import Campo

#Funcion que hace que el borracho se desplace
def caminata(campo, borracho, pasos):
    #Obtenemos las coordenadas del borracho
    inicio = campo.obtenerCoordenada(borracho)
    for _ in range(pasos):
        #Esta función es la que hace que el borracho se mueva
        campo.mover_borracho(borracho)
    #Retornamos la ultima posición del borracho
    return inicio.distancia(campo.obtenerCoordenada(borracho))
    
#Inicialiazamos nuestras variables
def simular_caminata(pasos, numeroDeIntentos, tipoBorracho):
    #Crea un nuevo borracho
    borracho = tipoBorracho(nombre = 'David')
    #Creamos el origen del borracho en el campo
    origen = Coordenada(0,0)
    #Variable para guardar las distancias
    distancias = []
    #Con este ciclo simulamos una caminata en varias ocasiones un total de 100
    for _ in range(numeroDeIntentos):
        #Creamos un campo para desplazar el borracho, un campp por 
        #cada intento
        campo = Campo()
        #Añadimos el borracho al campo e indicamos cual es su posicion
        campo.anadir_borracho(borracho, origen)
        #caminata retorna un valor para cada cantidad de pasos
        #que deseamos desplazar el borracho
        simulacion_caminata = caminata(campo, borracho, pasos)
        #Cada distancia se guarda en un arreglo de distancias
        distancias.append(round(simulacion_caminata, 1))

    return distancias
    
#Función para graficar un borracho a la vez, esta comentada 
#si la activas se genera una grafica cada llamada
''' def graficar(x,y,tipo_borracho):
    #Para graficar en una ventana html
    output_file('CaminoAleatrio.html')
    #Crea la ventana de tipo grafica
    grafica = figure(title='Camino aleatorio', x_axis_label='Pasos', y_axis_label='Distancia')
    #Hace una grafica de tipo linea con los puntos X e Y
    leyenda = 'Distancia Media ' + tipo_borracho.__name__
    grafica.line(x, y, legend=leyenda)
    #Pinta la grafica en la ventana
    show(grafica) '''

#Función para graficar
def graficaSimultanea(datos1, datos2, datos3):
    x1 = list(datos1.keys())
    y1 = list(datos1.values())
    x2 = list(datos2.keys())
    y2 = list(datos2.values())
    x3 = list(datos3.keys())
    y3 = list(datos3.values())
    #Para graficar en una ventana html
    output_file('CaminoAleatrio.html')
    #Crea la ventana de tipo grafica
    grafica = figure(title='Camino aleatorio', x_axis_label='Pasos', y_axis_label='Distancia')
    #Hace una grafica de tipo linea con los puntos X e Y
    grafica.line(x1, y1, legend='Distancia Borracho Tradicional', color= 'red')
    grafica.line(x2, y2, legend='Distancia Borracho Surenio', color= 'blue')
    grafica.line(x3, y3, legend='Distancia Borracho Nortenio', color= 'orange')
    #Pinta la grafica en la ventana
    show(grafica)


#Main
def main(distancia_De_Caminata, numeroDeIntentos, tipo_borracho):
    #Este array guardara las diferentes distancias que se ejecuten
    distancia_media_por_caminata = []
    #Retorno un diccionario con los pasos y distancias recorridas
    movimientos = {}
    #Este ciclo me ayuda a hacer que el borracho se mueva
    #por las 4 distancias que deseamos se mueva
    for pasos in distancia_De_Caminata:
        #Retorna un arreglo de distancias por cada simulacion
        distancia = simular_caminata(pasos, numeroDeIntentos, tipo_borracho)
        #Medida de la media de distancia recorrida
        distanciaMedia = round(sum(distancia) / len(distancia), 4)
        distancia_media_por_caminata.append(distanciaMedia)
        #Maxima distancia
        distanciaMaxima = max(distancia)
        #Minima distancia
        distanciaMinima = min(distancia)
        #Imprimimos los resultados para nuestro borracho
        print(f'{tipo_borracho.__name__} caminata aleatoria de {pasos} pasos")')
        print(f'Media  = {distanciaMedia}')
        print(f'Maxima = {distanciaMaxima}')
        print(f'Minima = {distanciaMinima}')
        #Lleno el diccionario para graficarlo
        movimientos[pasos] = distanciaMedia
    #Si activas las funcion para graficar de una en una en esta linea se llama la función
    #graficar(distancia_De_Caminata, distancia_media_por_caminata,tipo_borracho)
    return movimientos
    

#Primero generamos las lineas de comando para que el programa
#se ejecute en la linea de comandos (la terminal)
if __name__ == '__main__':
    #Tendremos cuatro caminatas que simular
    #Cada numero del array representa el numero de pasos
    #que caminara el borracho
    distancia_De_Caminata = [10, 100, 1000, 10000]
    #Para ver una aproximacion del resultado se corre varias
    #veces esta simulacion 100
    numeroDeIntentos = 100
    #La funcion main es la que corre el programa. Has de cuenta que
    #es el play del programa, por eso recibe todos los datos necesarios
    #para crear un borracho, medir su primer posicion, actualizar al mover
    #y ubicarlo en el campo. Veamos la simulacion para 3 borrachos
    borracho1 = main(distancia_De_Caminata, numeroDeIntentos, BorrachoTradicional)
    borracho2 = main(distancia_De_Caminata, numeroDeIntentos, BorrachoSurenio)
    borracho3 = main(distancia_De_Caminata, numeroDeIntentos, BorrachoNortenio)
    #Llamo la funcion para obtener los resultados de las tres simulaciones simultaneas
    graficaSimultanea(borracho1, borracho2, borracho3)

  • Tipos de borracho
#Clase para inicializar un borracho
import random
class Borracho:
    def __init__(self, nombre):
        self.nombre = nombre
#Es la extensión que hace que un borracho se desplace entr cuatro coordenadas
class BorrachoTradicional(Borracho):
    def __init__(self, nombre):
        super().__init__(nombre)
    
    def camina(self):
        #random.choice() escoge aleatoriamente entre una estructura 
        #iterable en este caso un arreglo con cuatro coordenadas
        return random.choice([(1,0),(0,1),(-1,0),(0,-1)])

#Creo un borracho que tenga probabilidad de ir hacia el sur
class BorrachoSurenio(Borracho):
    def __init__(self, nombre):
        super().__init__(nombre)
    
    def camina(self):
        #Tiene 2/5 de posibilidad de ir hacia el sur
        return random.choice([(1,0),(0,1),(-1,0),(0,-1),(0,-1)])

#Creo un borracho que tenga probabilidad de ir hacia el norte
#Creo un borracho que tenga probabilidad de ir hacia el sur
class BorrachoNortenio(Borracho):
    def __init__(self, nombre):
        super().__init__(nombre)
    
    def camina(self):
        #Tiene 2/5 de posibilidad de ir hacia el norte
        return random.choice([(1,0),(0,1),(-1,0),(0,-1),(0,1)])

Después de analizar un rato las clases puede entender bien como funciona, recomiendo empezar a revisar las clases antes del archivo que tiene el método principal, y después la clase principal donde se encuentra el main.

Hola, Alguien me podría indicar cual es la acción equivalente de ($ cd camino_aleatorio/) de Linux. En windows 8, no he podido avanzar. Si alguien me puede ayudar estoy profundamente agradecido.

Les comparto mi ejemplo:

Resultado:

from borracho import BorrachoTradiccional
from campo import Campo
from coordenada 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)), campo.obtener_coordenada(borracho)


def simular_caminata(pasos, numero_de_intentos, tipo_de_borracho):
    
    borracho = tipo_de_borracho(nombre='Yerson')
    origen = Coordenada(0, 0)
    distancia = []
    coordenada = []

    # _ en el for le estamos diciendo que no vamos a usar la variable, solo nos interesa la iteración
    for _ in range(numero_de_intentos):
        campo = Campo()
        campo.anadir_borracho(borracho, origen)
        simulacion_caminata, simulacion_coordenadas = caminata(campo, borracho, pasos)
        distancia.append(round(simulacion_caminata, 1)) # que no tenga decimal
        coordenada.append((simulacion_coordenadas.x, simulacion_coordenadas.y))

    
    return distancia, coordenada


def graficar(x, y):
    grafica = figure(title='Camino de Borrachos', x_axis_label='Pasos', y_axis_label='Distancia')
    grafica.line(x, y, legend='Distanacia media')

    show(grafica)


def main(distancia_de_caminata, numero_de_intentos, tipo_de_borracho):

    # distancias_media_por_caminata = []
    coordenada_x = []
    coordenada_y = []
    
    for pasos in distancia_de_caminata:
        distancias, coordenada = simular_caminata(pasos, numero_de_intentos, tipo_de_borracho)
        distancia_media = round(sum(distancias) / len(distancias), 4) # obtener solo 3 decimales
        distancia_maxima = max(distancias)
        distancia_minima = min(distancias)

        # distancias_media_por_caminata.append(distancia_media)
        # coordenada_x.append(coordenada.x)
        # coordenada_x.append(coordenada.y)

        for x in coordenada:
            coordenada_x.append(x[0])
            coordenada_y.append(x[1])

        print(f'{tipo_de_borracho.__name__} caminata aleotoria de {pasos} pasos') #.__name__ devuelve el nombre de la clase
        print(f'Media \t=\t {distancia_media}')
        print(f'Max \t=\t {distancia_maxima}')
        print(f'Min \t=\t {distancia_minima}')

    #graficar(distancia_de_caminata, distancias_media_por_caminata)
    graficar(coordenada_x, coordenada_y)


if __name__ == '__main__':
    distancia_de_caminata = [200000] # [10, 100, 1000, 10000]
    numero_de_intentos = 1000

    main(distancia_de_caminata, numero_de_intentos, BorrachoTradiccional)

Unas clases bastante pesadas realmente, costó mucho seguir el flujo. Recuerden que hay maneras diferentes de acercarse al problema.

https://www.youtube.com/watch?v=BfS2H1y6tzQ En este enlace hay una explicación al problema de los random walks con un enfoque diferente.

Adjunto mis gráficas con 10, 100, 1000, 10000 y 100000 pasos:

BorrachoCojo: ![](https://static.platzi.com/media/user_upload/Captura%20desde%202024-03-09%2017-00-23-c8a76e15-8ffa-4758-9285-0eb6bdf41660.jpg)

No me corre alguien que me ayude por favor:
C:\Users\Personal>C:/Python27/ArcGIS10.5/python.exe c:/Users/Personal/camino_aleatorio.py
Traceback (most recent call last):
File “c:/Users/Personal/camino_aleatorio.py”, line 41, in <module>
main(distancias_de_caminata, numero_de_intentos, BorrachoTradicional)
File “c:/Users/Personal/camino_aleatorio.py”, line 27, in main
distancias = simular_caminata(pasos, numero_de_intentos, tipo_de_borracho)
File “c:/Users/Personal/camino_aleatorio.py”, line 14, in simular_caminata
borracho = tipo_de_borracho(nombre = ‘Fercho’)
File “c:\Users\Personal\borracho.py”, line 11, in init
super().init(nombre)
TypeError: super() takes at least 1 argument (0 given)

Como dato off-topic, siendo hoy 4 de Diciembre del 2023, años después de este curso haber sido hecho, cuando uso CodeWhisperer cd AWS, y sin nunca haber escrito el código aquí presentado por el profesor, con solo crear, por ejemplo, el archivo Campo.py, y luego de definir la clase Campo, la extensión me empieza a sugerir el autocompletado con el mismo código expuesto acá. Suena divertido, pero habrá escenarios en donde no lo es.

el borracho con 1 millón de pasos

Si al ejecutar el programa les aparece el error:

KeyError: 'legend'

En el archivo camino_aleatorio.py en la función graficar(x, y), en su cuerpo cambien el parámetro legend:

grafica.line(x, y, legend='distancia media')

Por legend_label:

grafica.line(x, y, legend_label='distancia media')

Les presento al Caballo de Ajedrez, esta variente se mueve en “L”. Aquí les muestro 50,000 pasos, el inicio y el final estan unidos con una linea roja

Tambien les muestro el resultado del borracho tradicional para 50,000 pasos.

Borracho alocado

import random

class Borracho:

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


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

class BorrachoAlocado(Borracho):
   def __init__(self, name):
      super().__init__(name)
   
   def camina(self):
      return random.choice([(0,1*random.random()),(0,-1*random.random()),(1*random.random(),0),(-1*random.random(),0)])

100,000 pasos

Les quería compartir esto por dos razones: la primera que me costó mucho trabajo hacer la gráfica y, aunque es el mismo borracho, me siento orgulloso de que me halla quedado. Segundo, porque aún no puedo terminar mi carrera y este curso me está inspirando a seguir adelante. Bueno, dicho eso. Ahí va mi código que llevo dos días haciendo esa gráfica.

from borracho import BorrachoTradicional
from campo import Campo
from coordenada import Coordenada
from bokeh.plotting import figure, output_file, show  #graficar
#from bokeh.models import Circle, Plot

#funciones de simulación
def caminata(campo, borracho, pasos):
    inicio = campo.obtener_coordenada(borracho)
    
    x_vals = []
    y_vals = []
    x_vals.append(inicio.x)
    y_vals.append(inicio.y)

    for _ in range(pasos):
        x_act = x_vals[_]
        y_act = y_vals[_]

        campo.mover_borracho(borracho)

        p = campo.obtener_coordenada(borracho)
        x_vals.append(p.x)
        y_vals.append(p.y)

    #print(f'valores x {x_vals}')
    #print(f'valores y {y_vals}')

    return inicio.distancia(campo.obtener_coordenada(borracho)), x_vals, y_vals

def simular_caminata(pasos, numero_de_intentos, tipo_de_borracho):
    borracho = tipo_de_borracho(nombre ='David')
    origen = Coordenada(0, 0)
    distancias = []
    x_intento = [[0] * 1 for i in range(numero_de_intentos)]
    y_intento = [[0] * 1 for i in range(numero_de_intentos)]
    for _ in range(numero_de_intentos):
        campo = Campo()
        campo.anadir_borracho(borracho, origen)
        simulacion_caminata, x_intento[_], y_intento[_] = caminata(campo, borracho, pasos)
        #print(x_intento)
        distancias.append(round(simulacion_caminata, 1))

    return distancias, x_intento, y_intento

def graficar_media(x, y):       #graficar media
    output_file(filename="graficar_media", title="Grafica de incremento en distancia media")
    grafica = figure (title='Camino aleatorio', x_axis_label='pasos', y_axis_label='pasos recorridos')
    grafica.line(x, y, legend_label='distancia media')

    show(grafica)

def graficar_pasos(x_pasos, y_pasos, distancias_de_caminata, distancias_media_por_caminata, numero_de_intentos, i):
    output_file(filename = f'grafica_de_{distancias_de_caminata[i]}_pasos.html', title=f'Gráfica de recorridos de {distancias_de_caminata[i]} pasos')
    fig = figure()
    for j in range(numero_de_intentos):
        fig.line(x_pasos[i][j], y_pasos[i][j], line_width = 2)
    # plot = Plot()
    # glyph = Circle(x=0, y=0, radius=distancias_media_por_caminata[i], line_color="red", line_width=2)
    # plot.add_glyph(source, glyph)
    show(fig)
    #show(plot)

def main(distancias_de_caminata, numero_de_intentos, tipo_de_borracho):
    
    distancias_media_por_caminata = [] #grafica
    x_pasos = [[0] * 1 for i in range(len(distancias_de_caminata))]
    y_pasos = [[0] * 1 for i in range(len(distancias_de_caminata))]

    for pasos in distancias_de_caminata:
        distancias, x_pasos[distancias_de_caminata.index(pasos)], y_pasos[distancias_de_caminata.index(pasos)] = 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) #graficar
        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_media(distancias_de_caminata, distancias_media_por_caminata)
    for i in range(len(distancias_de_caminata)):
        graficar_pasos(x_pasos, y_pasos, distancias_de_caminata, distancias_media_por_caminata, numero_de_intentos, i)
    
    #print("Esta es la data de pasos")
    #print(x_pasos)
    print("Fin de la data de pasos")

if __name__ == '__main__':
    distancias_de_caminata = [10, 100, 1000, 10000]  #En este arreglo se pueden colocar las distancias de las caminatas
    numero_de_intentos = 100            #En esta variable el número de intentos

    main(distancias_de_caminata, numero_de_intentos, BorrachoTradicional)

Borracho con 10000 pasos, los 100 intentos

A continuacion les dejo mi repositorio de github, donde subi el ejercicio completo en un notebook de jupiter, grafique con plotly, por si a alguien le interesa interactuar con las graficas.
Link:
https://github.com/AAZG/random-walks.git

La verdad no he entendido mucho las ultimas dos clases jjj

Hice que el código corriera mil veces, en interesante como el grafico es mas preciso

Cada vez que hacemos una simulación obtenemos números diferentes porque tenemos un elemento de aleatoriedad incorporado dentro de nuestro código.

Con ayuda de los aportes y del profesor, les comparto mi resultado. Es el camino de un borracho tradicional con una distancia de 10000000 (10 millones) de pasos. La profundidad es increíble y el odio de mi CPU y Ram al curso también.❤

Este es el borracho de derecha (sí, doble sentido político). Está sesgado hacia x positivo.

class BorrachoDeDerecha(Borracho):

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

    def camina(self):
        return random.choice([
                (0,0.2),
                (0,-0.2),
                (0.4,0),
                (-0.2,0)
        ])

![](

No encontré como poner las manchas, pero hice las lineas; aquí mi reto:

![](

Si alguién esta trabajando en Deepnote le paso este código que encontré para que puedan graficar.

# Este código es para ejecutarse en deepnote
from bokeh.plotting import figure, output_file, save
from IPython.display import IFrame
from IPython.core.display import display, HTML
import tempfile

def bokeh_deepnote_show(plot):
    tmp_output_filename = tempfile.NamedTemporaryFile(suffix='.html').name
    output_file(tmp_output_filename)
    save(plot)

    f = open(tmp_output_filename, "r")
    display(HTML(f.read()))

# Ejemplo de uso
#p = figure(title="Bokeh test", plot_width=300, plot_height=300)
#p.line(x_vals, y_vals, line_width=2)
#bokeh_deepnote_show(p)

Es una buena práctica, lograr calcular el promedio, mínimo y máximo.

Espero este bien. Cuencano por Ecuador/Cuenca.

from bokeh.plotting import figure, show
from borracho import BorrachoTradicional, BorrachoCuencano
from campo import Campo
from coordenada import Coordenada
    

def graficar(x,y):
    grafica = figure(title='Camino de borracho', x_axis_label='pasos', y_axis_label='distancia')    
    grafica.line(x, y, legend_label='Distancia media' )
    show(grafica)  


def caminata(campo, pasos, borracho):
    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, borracho):
    coordenada_x_y = []
    borracho = borracho(nombre="William")
    origen = Coordenada(0,0)
    for _ in range(numero_de_intentos):
        campo = Campo()
        campo.agregar_borracho(borracho, origen)
        generador_coordenadas = caminata(campo, pasos, borracho)
        coordenada_x_y.append(generador_coordenadas)
    return coordenada_x_y
   

def main(distancias_de_caminata,numero_de_intentos,borracho):
    coordenadas_x= []
    coordenadas_y = []
    for pasos in distancias_de_caminata:
        coordenadas_aleatorias = simular_caminata(pasos, numero_de_intentos, borracho)
        distancia_maxima = max(coordenadas_aleatorias)
        distancia_minima = min(coordenadas_aleatorias)
        distancia_media = round(sum(coordenadas_aleatorias) / len(coordenadas_aleatorias), 4)
        print(f'{borracho.__name__} caminata aleatoria de {pasos}')
        print(f'La distancia media = {distancia_media}')
        print(f'La distancia maxima = {distancia_maxima}')
        print(f'La distancia minima = {distancia_minima}')
    for i in range(1, len(coordenadas_aleatorias), 2):
        coordenadas_y.append(coordenadas_aleatorias[i])
    for i in range(0, len(coordenadas_aleatorias), 2):
        coordenadas_x.append(coordenadas_aleatorias[i]) 
    graficar(coordenadas_x, coordenadas_y)


if __name__ == '__main__':
    distancias_de_caminata = [10, 100, 1000, 10000]
    numero_de_intentos = 1000
    print(""" 
    
            BORRACHO TRADICIONAL    
            
     """)
    main(distancias_de_caminata, numero_de_intentos, BorrachoTradicional)
    print(""" 
    
            BORRACHO CUENCANO      
            
        """)
    main(distancias_de_caminata, numero_de_intentos, BorrachoCuencano)
class BorrachoCojoDerecho(Borracho):

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

    def camina(self):
        return random.choice([(0, 1), (0, -1), (0.5, 0), (-1, 0)])

Les comparto mis simulaciones a 2000, 20000, 200000 y 2000000 de pasos. 😬

Les comparto un código un poco más corto y para mi más facil de entender, fue creado en jupyter notebook.

import numpy as no #librerias
import matplotlib.pyplot as plt

def generate_neighborhood(x,y):
    return [
        (x+1,y),#derecha
        (x-1,y),#izquierda
        (x,y+1),#arriba
        (x,y-1)#abajo
    ]
def random_walk_2D(origin=(0,0),steps = 10):
    walk = [origin] #Iniciamos el camino en el origen (0,0)
    si = 0 
    while True:
        candidates = generate_neighborhood(*walk[-1]) #Calculamos los 4 posibles candidatos de las siguientes coordenadas en el camino
        selected = candidates[no.random.choice(len(candidates))] # Seleccionamos una coordenada aleatoriamente con el random.choice
        walk+=[selected] # Aumentamos la coordenada anterior al camino
        si += 1 #Aumentamos los pasos
        if si == steps: break
    return walk #Devolvemos el camino

walk= random_walk_2D(steps = 100)
w = no.array(walk).T    #Transponemos las coordenadas para graficar más fáciñ
plt.figure(figsize=(5,5))
plt.plot(w[0],w[1],':.')

yo igual quiero, subí mi script, aunque no estoy seguro si está del todo correcto, pero igual me tinca que si

Mi gráfica con 10,000 y con la línea con la distancia final

Amigos, en youtube encontre un corto tutorail de graficos con bokeh, me parecio interesante.

https://www.youtube.com/watch?v=2TR_6VaVSOs&t=447s

def graficar(x, y, distancia):
    figura = figure(title = 'Random Walks', 
                    x_axis_label = 'x axis',
                    y_axis_label = 'y axis')
    figura.line(x, y, legend = 'Route')

    figura.circle(x[0], y[0], color='black', size = 10, legend = 'Init')
    figura.circle(x[-1], y[-1], color='green', size = 10, legend = 'Final')
    figura.line([x[0],x[-1]], [y[0], y[-1]], color='red', line_width = 1, legend = 'Distance')
    show(figura)```

Aqui mi grafico, un compilado de distintos aportes, hay muchos muy buenos en los comentarios.

from borrachos import SnoopDog, BorrachoTradicional
from coordenada import Coordenada
from campo import Campo
import random

from bokeh.plotting import figure, show

def main(distancia, inicio, borracho):
    campo = Campo()
    campo.anadir_borracho(borracho, inicio) #poner un borracho en origen
    ejecutar_caminata(campo, borracho, distancia)

def ejecutar_caminata(campo, borracho, distancia):
    x_arreglo = []
    y_arreglo = []
    x_arreglo.append(campo.obtener_coordenada(borracho).x)
    y_arreglo.append(campo.obtener_coordenada(borracho).y)
    for _ in range(distancia):
        campo.mover_borracho(borracho) #se actualiza las coordenadas del borracho
        x_arreglo.append(campo.obtener_coordenada(borracho).x)
        y_arreglo.append(campo.obtener_coordenada(borracho).y)

    graficar(x_arreglo, y_arreglo)

def graficar(x, y):
    figura = figure(title=f'Simulacion de borracho con {distancia} pasos', x_axis_label='Axis X', y_axis_label='Axis Y', plot_width=700, plot_height=700)
    x_plot = [x[0], x[-1]] # x_inicial, x_final
    y_plot = [y[0], y[-1]]    #  y_inicial, y_final
    names = ['Start', 'End']

    colors = '#%06X' % random.randint(0, 0xFFFFFF)
    figura.line(x, y, color=colors)
    figura.circle(x_plot,y_plot,alpha=0.8, size=20)
    figura.line(x_plot, y_plot, color="firebrick")
    # labels = LabelSet(x='x', y='y', text='names', level='glyph', x_offset=5, y_offset=5, render_mode='canvas')
    # colors = '#%06X' % random.randint(0, 0xFFFFFF)
    # figura.circle(x[len(x)-1],y[len(y)-1], color=colors,line_width=10)
    print(x[len(x)-1])
    print(y[len(y)-1])
    show(figura)

if __name__ == '__main__':
    distancia = 10000
    inicio = Coordenada(0,0)
    borracho1 = SnoopDog('Enzo')
    main(distancia, inicio, borracho1)

Les dejo este código si quieren crear un borracho vaya mas hacia arriba o mas hacia abajo, mas a la isquierda o la derecha. Solo tienen que cambiar los pesos “weights”

def camina(self):
        sig_paso = [(0,1), (0,-1), (1,0),(-1,0)]
        d = random.choices(range(len(sig_paso)),weights=(40,20,20,10),k=1)
        dint = int(d[0])
        return sig_paso[dint]

No estoy entendiendo bien la variable intentos (en el caso del programa=100). Cuando hacemos caminar al borracho usamos el for:
for _ in range(numero_de_intentos): campo = Campo() campo.anadir_borracho(borracho, origen) simulacion_caminata = caminata(campo, borracho, pasos) distancias.append(round(simulacion_caminata, 1))

Cuando en realidad eso deberia estar definido por la cantidad de pasos que nos arroja la lista de distancias o no? Acaso no deberia quedar asi?
for _ in range(pasos): campo = Campo() campo.anadir_borracho(borracho, origen) simulacion_caminata = caminata(campo, borracho, pasos) distancias.append(round(simulacion_caminata,1)) print(borracho.camina()) return distancias

Si alguien me saca la duda les agredecere mucho!

No me corrió y ni idea de donde tengo el error

La conclusión de esta clase es sencilla: “Uno no sabe cómo pero siempre regresa al punto de origen aún borracho” :smil

adjunto la modificación a borracho, para obtener distintos “pesos” de probabilidad en cada opción:

# Creamos la clase BorrachoModificado que extiende de Borracho.
class BorrachoModificado(Borracho):

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

    
    def camina(self):
        
        a=random.choices([(0, 1), (0, -1), (1, 0), (-1, 0)],weights=[0.1, 0.1, 0.1, 0.7])
        return(a[0])```

Agregué una coordenada extra al programa, como no se graficar en 3D con bokeh, decidí representarlo con tres graficas.
Código: https://github.com/Angrub/Caminos_aleatorios.git

Por si alguien tenía curiosidad de cómo se veian las coordenadas del borracho tradicional.

Logre modificar el código para agregar 2 borrachines, pero ambas presentan un patrón bastante parecido. Por el momento solo dejare el código de la grafica

![]()

from bokeh.models import ColumnDataSource

def graficar(x, y):
    grafica = figure(title='Camino aleatorio', x_axis_label='pasos', y_axis_label='distancia')
    source= ColumnDataSource(data=dict(x=x, y1=y[0], y2=y[1]))
    grafica.line(x, y, legend='distancia media')
    grafica.vline_stack(['y1', 'y2'], x='x', color=['blue', 'red'],  source=source)

Pues el cambio más sobresaliente que le hice al segundo objeto de la clase Borracho fue triplicar su distancia y si hay cambio en las estadísticas.

Mi borracho fue invisible