Tracks
/
Python
Python
/
Temario
/
Aleatoriedad
Al

Aleatoriedad en Python

{one: "1 ejercicio", many: "%{count} de ejercicios", other: "%{count} ejercicios"}

Acerca de Aleatoriedad

Muchos programas necesitan valores aleatorios (aparentemente) para simular eventos del mundo real.

Entre los ejemplos comunes y conocidos están:

  • Lanzar una moneda: un valor aleatorio de ('Heads', 'Tails').
  • Tirar un dado: un número entero aleatorio del 1 al 6.
  • Barajar una baraja de cartas: un orden aleatorio de una lista de cartas.

Generar valores verdaderamente aleatorios con una computadora es un desafío técnico sorprendentemente difícil, por lo que es posible que veas que estos resultados se denominan «pseudoaleatorios».

En la práctica, una biblioteca bien diseñada como el módulo random de la biblioteca estándar de Python es rápida, flexible y da resultados que son más que suficientes para la mayoría de las aplicaciones de modelado, simulación y juegos.

El resto de esta página enumera algunas de las funciones más comunes de random. Te animamos a explorar toda la documentación de random, ya que hay muchas más opciones de las que cubrimos aquí.

Caution

El módulo random NO debe usarse para aplicaciones de seguridad o criptográficas.

En su lugar, Python proporciona el módulo secrets. Está especialmente optimizado para la seguridad criptográfica. Algunos de los problemas previos y las razones para crear el módulo secrets se pueden encontrar en PEP 506.

Importar

Antes de poder usar las herramientas del módulo random, primero debes importarlo:

>>> import random

# Choose random integer from a range
>>> random.randrange(1000)
360

>>> random.randrange(-1, 500)
228

>>> random.randrange(-10, 11, 2)
-8

# Choose random integer between two values (inclusive)
>>> random.randint(5, 25)
22

Para evitar escribir el nombre del módulo, puedes importar funciones específicas por su nombre:

>>> from random import choice, choices

# Using choice() to pick Heads or Tails 10 times
>>> tosses = []
... for side in range(10):
...    tosses.append(choice(['H', 'T']))    

>>> print(tosses)
['H', 'H', 'H', 'H', 'H', 'H', 'H', 'T', 'T', 'H']


# Using choices() to pick Heads or Tails 8 times
>>> picks = []
... picks.extend(choices(['H', 'T'], k=8))
... print(picks)
['T', 'H', 'H', 'T', 'H', 'H', 'T', 'T']

Crear números enteros aleatorios

La función randrange() tiene tres formas de seleccionar un valor aleatorio de range(start, stop, step):

  1. randrange(stop) da un número entero n tal que 0 <= n < stop
  2. randrange(start, stop) da un número entero n tal que start <= n < stop
  3. randrange(start, stop, step) da un número entero n tal que start <= n < stop y n está en la secuencia start, start + step, start + 2*step...

Para el caso común en el que step == 1, la función randint(a, b) puede ser más cómoda y legible. Los posibles resultados de randint() incluyen el límite superior, así que randint(a, b) es lo mismo que usar randrange(a, b+1):

>>> import random

# Select one number at random from the range 0, 499
>>> random.randrange(500)
219

# Select 10 numbers at random between 0 and 9 two steps apart.
>>> numbers = []
... for integer in range(10):
...    numbers.append(random.randrange(0, 10, 2))

>>> print(numbers)
[2, 8, 4, 0, 4, 2, 6, 6, 8, 8]

# roll a die
>>> random.randint(1, 6)  
4

Trabajar con secuencias

Las funciones de esta sección suponen que partes de alguna secuencia u otro contenedor.

Normalmente será una list o, con algunas limitaciones, una tuple o un set (una tuple es inmutable y un set no tiene orden).

choice() y choices()

La función choice() devuelve un elemento elegido al azar de una secuencia dada. En su forma más simple, esto podría ser un lanzamiento de moneda:

# This will pick one of the two values in the list at random 5 separate times 
>>> [random.choice(['H', 'T']) for _ in range(5)]
['T', 'H', 'H', 'T', 'H']

Podríamos lograr esencialmente lo mismo con la función choices(), pasando un argumento de palabra clave con la longitud de la lista:

>>> random.choices(['H', 'T'], k=5)
['T', 'H', 'T', 'H', 'H']

En los ejemplos anteriores, supusimos una moneda justa con la misma probabilidad de cara o cruz, pero también se pueden especificar pesos. Por ejemplo, si una bolsa contiene 10 bolas rojas y 15 bolas verdes, y queremos sacar una al azar:

>>> random.choices(['red', 'green'], [10, 15])
['red']

sample()

El ejemplo de choices() anterior supone lo que los estadísticos llaman «muestreo con reemplazo». Cada elección o selección no tiene efecto en la probabilidad de las elecciones futuras, y la distribución de las posibles elecciones se mantiene igual de una elección a otra.

En el ejemplo de las bolas rojas y verdes: después de cada elección, devolvemos la bola a la bolsa y la agitamos bien antes de la siguiente elección. Esto contrasta con una situación en la que sacamos una bola roja y esta se queda fuera. No devolver la bola significa que ahora hay menos bolas rojas en la bolsa, y la siguiente elección tiene ahora menos probabilidad de ser roja.

Para simular este «muestreo sin reemplazo», el módulo random proporciona la función sample(). La sintaxis de sample() es similar a la de choices(), salvo que añade un parámetro de palabra clave counts:

>>> random.sample(['red', 'green'], counts=[10, 15], k=10)
['green', 'green', 'green', 'green', 'green', 'red', 'red', 'red', 'red', 'green']

Las muestras se devuelven en el orden en que se eligieron.

shuffle()

Tanto choices() como sample() devuelven listas nuevas cuando k > 1. En cambio, shuffle() aleatoriza el orden de una lista in situ, y el orden original se pierde:

>>> my_list = [1, 2, 3, 4, 5]
>>> random.shuffle(my_list)
>>> my_list
[4, 1, 5, 2, 3]

Trabajar con distribuciones

Hasta ahora nos hemos centrado en casos donde todos los resultados son igualmente probables. Por ejemplo, random.randrange(100) tiene la misma probabilidad de dar cualquier número entero del 0 al 99.

Muchas situaciones del mundo real son mucho menos simples que esta. Como resultado, los estadísticos han creado una amplia variedad de distributions para describir matemáticamente los resultados del «mundo real».

Distribuciones uniformes

Para números enteros, se usan randrange() y randint() cuando todas las probabilidades son iguales. Esto se llama distribución uniform.

Hay equivalentes de punto flotante para randrange() y randint().

random() da un valor float x tal que 0.0 <= x < 1.0.

uniform(a, b) da x tal que a <= x <= b.

>>> [round(random.random(), 3) for _ in range(5)]
[0.876, 0.084, 0.483, 0.22, 0.863]

>>> [round(random.uniform(2, 5), 3) for _ in range(5)]
[2.798, 2.539, 3.779, 3.363, 4.33]

Distribución gaussiana

También llamada distribución «normal» o curva «en forma de campana», es una forma muy común de describir la imprecisión en valores medidos.

Por ejemplo, supón que la fábrica donde trabajas acaba de comprar 10.000 tornillos que deberían ser idénticos. Quieres configurar el robot de la fábrica para que los manipule, así que pesas una muestra de 100 y descubres que tienen un peso promedio (o mean) de 4,731 g. Es muy poco probable que esto signifique que todos pesan exactamente 4,731 g. Quizá descubras que los valores van de 4,627 a 4,794 g, pero se agrupan alrededor de 4,731 g.

Esta es la Gaussian distribution, en la que las probabilidades alcanzan su máximo en la media y disminuyen de forma simétrica hacia ambos lados (de ahí «en forma de campana»). Para simular esto en software, necesitamos alguna forma de especificar el ancho de la curva (por lo general, ¡los tornillos caros se agrupan más estrechamente alrededor de la media que los tornillos baratos!).

Por convención, esto se hace con la standard deviation: valores pequeños para una curva pronunciada y estrecha, y grandes para una curva baja y ancha. A los matemáticos les encantan las letras griegas, así que usamos μ ('mu') para representar la media y σ ('sigma') para representar la desviación estándar. Así, si lees que «el 95% de los valores están dentro de 2σ de μ» o que «el bosón de Higgs se ha detectado con una confianza de 5 sigma», esos comentarios se relacionan con la desviación estándar.

>>> mu = 4.731
>>> sigma = 0.316
>>> [round(random.gauss(mu, sigma), 3) for _ in range(5)]
[4.72, 4.957, 4.64, 4.556, 4.968]
Editar en GitHub El enlace se abre en una ventana o pestaña nueva