random —Generar números pseudoaleatorios

Código fuente: Lib/random.py


Este módulo implementa generadores de números pseudoaleatorios para varias distribuciones.

Para los enteros, existe una selección uniforme dentro de un rango. Para las secuencias, existe una selección uniforme de un elemento aleatorio, una función para generar una permutación aleatoria de una lista in-situ y una función para el muestreo aleatorio sin reemplazo.

Para números reales, existen funciones para calcular distribuciones uniformes, normales (Gaussianas), log-normales, exponenciales negativas, gamma y beta. Para generar distribuciones angulares está disponible la distribución de von Mises.

Casi todas las funciones del módulo dependen de la función básica random(), la cuál genera uniformemente un número flotante aleatorio en el rango semiabierto [0.0, 1.0). Python utiliza Mersenne Twister como generador principal. Éste produce números de coma flotante de 53 bits de precisión y tiene un periodo de 2**19937-1. La implementación subyacente en C es rápida y segura para subprocesos. Mersenne Twister es uno de los generadores de números aleatorios más testados que existen. Sin embargo, al ser completamente determinístico, no es adecuado para todos los propósitos y es completamente inadecuado para fines criptográficos.

Las funciones proporcionadas por este módulo en realidad son métodos enlazados a instancias ocultas a la clase random.Random. Puedes instanciar tus propias instancias de Random para obtener generadores que no compartan estado.

La clase Random puede ser también subclaseada si quieres usar un generador básico diferente para tu propio diseño: en este caso, invalida los métodos random(), seed(), getstate() y setstate(). Opcionalmente, se puede substituir un método getrandbits() por un nuevo generador — esto permite a randrange() producir selecciones sobre un rango arbitrariamente amplio.

El módulo random también proporciona la clase SystemRandom, la cuál usa la función del sistema os.urandom() para generar números aleatorios a partir de fuentes proporcionadas por el sistema operativo.

Advertencia

Los generadores pseudoaleatorios de este módulo no deben ser utilizados con fines de seguridad. Para usos de seguridad o criptográficos, consulta el módulo secrets.

Ver también

M. Matsumoto and T. Nishimura, «Mersenne Twister: A 623-dimensionally equidistributed uniform pseudorandom number generator», ACM Transactions on Modeling and Computer Simulation Vol. 8, No. 1, January pp.3–30 1998.

Complementary-Multiply-with-Carry recipe for a compatible alternative random number generator with a long period and comparatively simple update operations.

Bookkeeping functions

random.seed(a=None, version=2)

Inicializa el generador de números aleatorios.

Si a es omitida o None, se utilizará la hora actual del sistema. Si las fuentes de aleatoriedad vienen del sistema operativo, éstas se usarán en vez de la hora del sistema (ver la función os.urandom() para detalles sobre su disponibilidad).

Si a es un entero, se usará directamente.

Con la versión 2 (la versión por defecto), un objeto str, bytes, o bytearray se convierte en int y se usarán todos sus bits.

Con la versión 1 (proporcionada para reproducir secuencias aleatorias desde versiones anteriores de Python), el algoritmo para str y bytes genera un rango de semillas más estrecho.

Distinto en la versión 3.2: El esquema que usa todos los bits en una semilla de tipo cadena, se ha movido a la versión 2.

random.getstate()

Retorna un objeto capturando el estado interno del generador. Este objeto puede pasarse a setstate() para restaurar su estado.

random.setstate(state)

El state debería haberse obtenido de una llamada previa a getstate(), y setstate() reestablece el estado interno del generador al que tenia cuando se llamó a la función getstate().

random.getrandbits(k)

Returns a Python integer with k random bits. This method is supplied with the Mersenne Twister generator and some other generators may also provide it as an optional part of the API. When available, getrandbits() enables randrange() to handle arbitrarily large ranges.

Funciones para enteros

random.randrange(stop)
random.randrange(start, stop[, step])

Retorna un elemento de range(start, stop, step) seleccionado aleatoriamente. Esto es equivalente a choice(range(start, stop, step)), pero en realidad no crea un objeto rango.

El patrón de argumento posicional coincide con el de range(). Los argumentos no deben usarse porque la función puede usarlos de forma inesperada.

Distinto en la versión 3.2: randrange() es más sofisticado produciendo valores igualmente distribuidos. Anteriormente utilizaba un estilo como int(random()*n) el cual puede producir distribuciones ligeramente desiguales.

random.randint(a, b)

Retorna un entero aleatorio N tal que a <= N <= b. Alias de randrange(a, b+1).

Funciones para secuencias

random.choice(seq)

Retorna un elemento aleatorio de una secuencia seq no vacía. Si seq está vacía, lanza IndexError.

random.choices(population, weights=None, *, cum_weights=None, k=1)

Retorna una lista de elementos de tamaño k elegidos de la population con reemplazo. Si la population está vacía, lanza IndexError.

Si se especifica una secuencia weights, las selecciones se realizan de acuerdo con las ponderaciones relativas. Alternativamente, si se da una secuencia cum_weights, las selecciones se harán según los pesos cumulativos (posiblemente se calculen usando itertools.accumulate()). Por ejemplo, los pesos relativos [10, 5, 30, 5] son equivalentes a los pesos cumulativos [10, 15, 45, 50]. Internamente, los pesos relativos se convierten en pesos cumulativos antes de hacer selecciones, por lo cual suplir los pesos cumulativos ahorra trabajo.

Si ni weights ni cum_weights están especificadas, las selecciones se realizan con la misma probabilidad. Si se proporciona una secuencia de ponderaciones, debe tener la misma longitud que la secuencia population. Es un TypeError especificar ambas weights y cum_weights.

The weights or cum_weights can use any numeric type that interoperates with the float values returned by random() (that includes integers, floats, and fractions but excludes decimals). Weights are assumed to be non-negative.

Dada una semilla, la función choices() normalmente produce una secuencia diferente a las llamadas repetidas a choice() con la misma ponderación. El algoritmo usado por choices() emplea aritmética de coma flotante para la consistencia interna y velocidad. El algoritmo usado por choice() emplea por defecto aritmética de enteros con selecciones repetidas para evitar pequeños sesgos de errores de redondeo.

Nuevo en la versión 3.6.

random.shuffle(x[, random])

Shuffle the sequence x in place.

El argumento opcional random es una función de 0 argumentos que retorna un flotante random en [0.0, 1.0); por defecto esta es la función random().

Para bajar una secuencia inmutable y retornar una nueva lista barajada, utilice sample(x, k=len(x)) en su lugar.

Tenga en cuenta que incluso para pequeños len(x), el número total de permutaciones de x puede crecer rápidamente más que el periodo de muchos generadores de números aleatorios. Esto implica que la mayoría de las permutaciones de una secuencia larga nunca se pueden generar. Por ejemplo, una secuencia de longitud 2080 es la más grande que cabe dentro del período del generador de números aleatorios de Mersenne Twister.

random.sample(population, k)

Retorna una lista de longitud k de elementos únicos elegidos de la secuencia de población o conjunto. Se utiliza para el muestreo aleatorio sin reemplazo.

Retorna una nueva lista que contiene elementos de la población sin modificar la población original. La lista resultante está en orden de selección de forma que todos los subsectores también son muestras aleatorias válidas. Esto permite que los ganadores de la rifa (la muestra) se dividan en primer premio y ganadores del segundo lugar (los subsectores).

Los miembros de la población no tienen porqué ser hashable o únicos. Si la población incluye repeticiones, entonces cada ocurrencia es una posible selección en la muestra.

Para escoger una muestra de un rango de enteros, use un objeto range() como argumento. Esto es especialmente rápido y eficiente en espacio para el muestreo de poblaciones grandes: sample(range(10000000), k=60).

Si el tamaño de la muestra es mayor que el tamaño de la población, se lanzará un ValueError.

Distribuciones para los nombres reales

Las siguientes funciones generan distribuciones específicas para números reales. Los parámetros de la función reciben el nombre de las variables correspondientes en la ecuación de distribución, tal y como se utilizan en la práctica matemática común.; la mayoría de estas ecuaciones se pueden encontrar en cualquier texto estadístico.

random.random()

Retorna el siguiente número en coma flotante aleatorio dentro del rango [0.0, 1.0).

random.uniform(a, b)

Retorna un número en coma flotante aleatorio N tal que a <= N <= b para a <= b y b <= N <= a para b < a.

El valor final b puede o no estar incluido en el rango, dependiendo del redondeo de coma flotante en la ecuación a + (b-a) * random().

random.triangular(low, high, mode)

Retorna un número de coma flotante N tal que low <= N <= high y con el mode especificado entre esos límites. Los límites low (inferior) y high (superior) son por defecto cero y uno. El argumento mode tiene como valor por defecto el punto medio entre los límites, dando lugar a una distribución simétrica.

random.betavariate(alpha, beta)

Distribución beta. Las condiciones de los parámetros son alpha > 0 y beta > 0. Retorna valores dentro del rango entre 0 y 1.

random.expovariate(lambd)

Distribución exponencial. lambd es 1.0 dividido entre la media deseada. Debe ser distinto a cero (El parámetro debería llamarse lambda pero esa es una palabra reservada en Python). Retorna valores dentro del rango de 0 a infinito positivo si lambd es positivo, y de infinito negativo a 0 si lambd es negativo.

random.gammavariate(alpha, beta)

Distribución gamma. (¡No la función gamma!) Las condiciones en los parámetros son alpha > 0 y beta > 0.

La función de distribución de la probabilidad es:

          x ** (alpha - 1) * math.exp(-x / beta)
pdf(x) =  --------------------------------------
            math.gamma(alpha) * beta ** alpha
random.gauss(mu, sigma)

Distribución gaussiana. mu es la media y sigma es la desviación estándar. Es un poco más rápida que la función normalvariate() definida debajo.

random.lognormvariate(mu, sigma)

Logaritmo de la distribución normal. Si se usa un logaritmo natural de esta distribución, se obtendrá una distribución normal con media mu y desviación estándar sigma. mu puede tener cualquier valor, y sigma debe ser mayor que cero.

random.normalvariate(mu, sigma)

Distribución normal. mu es la media y sigma es la desviación estándar.

random.vonmisesvariate(mu, kappa)

mu es el ángulo medio, expresado en radiantes entre 0 y 2*pi, y kappa es el parámetro de concentración, que debe ser mayor o igual a cero. Si kappa es igual a cero, esta distribución se reduce a un ángulo aleatorio uniforme sobre el rango de 0 a 2*pi.

random.paretovariate(alpha)

Distribución de Pareto. alpha es el parámetro de forma.

random.weibullvariate(alpha, beta)

Distribución de Weibull. alpha es el parámetro de escala y beta es el parámetro de forma.

Generador alternativo

class random.Random([seed])

Esta clase implementa el generador de números pseudoaleatorios predeterminado que usa el módulo random .

class random.SystemRandom([seed])

Clase que utiliza la función os.urandom() para generar números aleatorios a partir de fuentes proporcionadas por el sistema operativo. No está disponible en todos los sistemas. No se basa en el estado del software y las secuencias no son reproducibles. En consecuencia, el método seed() no tiene efecto y es ignorado. Los métodos getstate() y setstate() lanzan NotImplementedError si se les llama.

Notas sobre la Reproducibilidad

A veces es necesario poder reproducir las secuencias dadas por un generador de números pseudoaleatorios. Al volver a usar el valor de una semilla, la misma secuencia debería ser reproducible en cada ejecución siempre que no se ejecuten múltiples subprocesos.

Muchos de los algoritmos y de las funciones de generación de semillas del módulo aleatorio pueden cambiar entre versiones de Python, pero se garantiza que dos aspectos no cambien:

  • Si se añade un nuevo método de generación de semilla, se ofrecerá un generador de semilla retrocompatible.

  • El método generador random() continuará produciendo la misma secuencia cuando se le da la misma semilla al generador de semilla compatible.

Ejemplos y Recetas

Ejemplos básicos:

>>> random()                             # Random float:  0.0 <= x < 1.0
0.37444887175646646

>>> uniform(2.5, 10.0)                   # Random float:  2.5 <= x < 10.0
3.1800146073117523

>>> expovariate(1 / 5)                   # Interval between arrivals averaging 5 seconds
5.148957571865031

>>> randrange(10)                        # Integer from 0 to 9 inclusive
7

>>> randrange(0, 101, 2)                 # Even integer from 0 to 100 inclusive
26

>>> choice(['win', 'lose', 'draw'])      # Single random element from a sequence
'draw'

>>> deck = 'ace two three four'.split()
>>> shuffle(deck)                        # Shuffle a list
>>> deck
['four', 'two', 'ace', 'three']

>>> sample([10, 20, 30, 40, 50], k=4)    # Four samples without replacement
[40, 10, 50, 30]

Simulaciones:

>>> # Six roulette wheel spins (weighted sampling with replacement)
>>> choices(['red', 'black', 'green'], [18, 18, 2], k=6)
['red', 'green', 'black', 'black', 'red', 'black']

>>> # Deal 20 cards without replacement from a deck of 52 playing cards
>>> # and determine the proportion of cards with a ten-value
>>> # (a ten, jack, queen, or king).
>>> deck = collections.Counter(tens=16, low_cards=36)
>>> seen = sample(list(deck.elements()), k=20)
>>> seen.count('tens') / 20
0.15

>>> # Estimate the probability of getting 5 or more heads from 7 spins
>>> # of a biased coin that settles on heads 60% of the time.
>>> def trial():
...     return choices('HT', cum_weights=(0.60, 1.00), k=7).count('H') >= 5
...
>>> sum(trial() for i in range(10_000)) / 10_000
0.4169

>>> # Probability of the median of 5 samples being in middle two quartiles
>>> def trial():
...     return 2_500 <= sorted(choices(range(10_000), k=5))[2] < 7_500
...
>>> sum(trial() for i in range(10_000)) / 10_000
0.7958

Example of statistical bootstrapping using resampling with replacement to estimate a confidence interval for the mean of a sample:

# http://statistics.about.com/od/Applications/a/Example-Of-Bootstrapping.htm
from statistics import fmean as mean
from random import choices

data = [41, 50, 29, 37, 81, 30, 73, 63, 20, 35, 68, 22, 60, 31, 95]
means = sorted(mean(choices(data, k=len(data))) for i in range(100))
print(f'The sample mean of {mean(data):.1f} has a 90% confidence '
      f'interval from {means[5]:.1f} to {means[94]:.1f}')

Ejemplo de un test de permutación en remuestreo (en) para determinar la significación estadística o p-valor de una diferencia observada entre los efectos de un fármaco y un placebo:

# Example from "Statistics is Easy" by Dennis Shasha and Manda Wilson
from statistics import fmean as mean
from random import shuffle

drug = [54, 73, 53, 70, 73, 68, 52, 65, 65]
placebo = [54, 51, 58, 44, 55, 52, 42, 47, 58, 46]
observed_diff = mean(drug) - mean(placebo)

n = 10_000
count = 0
combined = drug + placebo
for i in range(n):
    shuffle(combined)
    new_diff = mean(combined[:len(drug)]) - mean(combined[len(drug):])
    count += (new_diff >= observed_diff)

print(f'{n} label reshufflings produced only {count} instances with a difference')
print(f'at least as extreme as the observed difference of {observed_diff:.1f}.')
print(f'The one-sided p-value of {count / n:.4f} leads us to reject the null')
print(f'hypothesis that there is no difference between the drug and the placebo.')

Simulation of arrival times and service deliveries for a multiserver queue:

from heapq import heappush, heappop
from random import expovariate, gauss
from statistics import mean, median, stdev

average_arrival_interval = 5.6
average_service_time = 15.0
stdev_service_time = 3.5
num_servers = 3

waits = []
arrival_time = 0.0
servers = [0.0] * num_servers  # time when each server becomes available
for i in range(100_000):
    arrival_time += expovariate(1.0 / average_arrival_interval)
    next_server_available = heappop(servers)
    wait = max(0.0, next_server_available - arrival_time)
    waits.append(wait)
    service_duration = gauss(average_service_time, stdev_service_time)
    service_completed = arrival_time + wait + service_duration
    heappush(servers, service_completed)

print(f'Mean wait: {mean(waits):.1f}.  Stdev wait: {stdev(waits):.1f}.')
print(f'Median wait: {median(waits):.1f}.  Max wait: {max(waits):.1f}.')

Ver también

*Statistics for Hackers* un video tutorial de Jake Vanderplas sobre análisis estadístico usando sólo algunos conceptos fundamentales incluyendo simulación, muestreo, baraja y validación cruzada.

*Economics Simulation* <http://nbviewer.jupyter.org/url/norvig.com/ipython/Economics.ipynb>`_una simulación de un mercado por `Peter Norvig que muestra un uso efectivo de las herramientas y distribuciones proporcionadas por este modulo (gauss, uniform, sample, betavariate, choice, triangular, y randrange).

*A Concrete Introduction to Probability (using Python)* <http://nbviewer.jupyter.org/url/norvig.com/ipython/Probability.ipynb>`_un tutorial de `Peter Norvig cubriendo teoría básica de probabilidad, cómo escribir simulaciones y cómo realizar un análisis de datos usando Python.