random — Generadores de números pseudoaleatorios¶
Propósito: | Implementa varios tipos de generadores de números pseudoaleatorios. |
---|
El módulo random
proporciona un generador rápido de números
pseudoaleatorios basado en el algoritmo Mersenne Twister. Originalmente
desarrollado para producir entradas para simulaciones de Monte Carlo, Mersenne
Twister genera números con distribución casi uniforme y un período grande, por
lo que es adecuado para una amplia gama de aplicaciones.
Generando Números Aleatorios¶
La función random()
devuelve el siguiente valor de coma flotante aleatorio
de la secuencia generada. Todos los valores de retorno caen dentro del rango
0 <= n <1.0
.
import random
for i in range(5):
print('%04.3f' % random.random(), end=' ')
print()
Ejecutar el programa repetidamente produce diferentes secuencias de números.
$ python3 random_random.py
0.859 0.297 0.554 0.985 0.452
$ python3 random_random.py
0.797 0.658 0.170 0.297 0.593
Para generar números en un rango numérico específico, usa en lugar
uniform()
.
import random
for i in range(5):
print('{:04.3f}'.format(random.uniform(1, 100)), end=' ')
print()
Pasa los valores mínimo y máximo, y uniform()
ajusta los valores de retorno
de random()
usando la fórmula min + (max -min) * random ()
.
$ python3 random_uniform.py
12.428 93.766 95.359 39.649 88.983
Sembrando¶
random()
produce diferentes valores cada vez que se llama y tiene un
período muy grande antes de repetir cualquier número. Esto es útil para
producir valores únicos o variaciones, pero hay momentos en los que tener el
mismo conjunto de datos disponible para ser procesado de diferentes maneras es
útil. Una técnica es usar un programa para generar valores aleatorios y
guárdalos para ser procesados por un paso separado. Eso puede no ser práctico
para grandes cantidades de datos, así que random
incluye la función
seed()
para inicializar el generador pseudoaleatorio para que produzca un
conjunto esperado de valores.
import random
random.seed(1)
for i in range(5):
print('{:04.3f}'.format(random.random()), end=' ')
print()
El valor de semilla controla el primer valor producido por la fórmula utilizada
para producir números pseudoaleatorios, y dado que la fórmula es determinista,
también establece la secuencia completa producida después de la semilla sea
cambiada. El argumento para seed()
puede ser cualquier objeto hashable.
El valor predeterminado es usar una fuente de aleatoriedad específica de la
plataforma, si está disponible. De lo contrario, se usa la hora actual.
$ python3 random_seed.py
0.134 0.847 0.764 0.255 0.495
$ python3 random_seed.py
0.134 0.847 0.764 0.255 0.495
Guardando el estado¶
El estado interno del algoritmo pseudoaleatorio utilizado por random()
se
puede guardar y usar para controlar los números producidos en ejecuciones
posteriores. Restaurando el estado anterior antes de continuar reduce la
probabilidad de repetir valores o secuencias de valores de la entrada anterior.
La función getstate()
devuelve datos que se puede utilizar para
reinicializar el generador de números aleatorios más tarde con setstate()
.
import random
import os
import pickle
if os.path.exists('state.dat'):
# Restore the previously saved state
print('Found state.dat, initializing random module')
with open('state.dat', 'rb') as f:
state = pickle.load(f)
random.setstate(state)
else:
# Use a well-known start state
print('No state.dat, seeding')
random.seed(1)
# Produce random values
for i in range(3):
print('{:04.3f}'.format(random.random()), end=' ')
print()
# Save state for next time
with open('state.dat', 'wb') as f:
pickle.dump(random.getstate(), f)
# Produce more random values
print('\nAfter saving state:')
for i in range(3):
print('{:04.3f}'.format(random.random()), end=' ')
print()
Los datos devueltos por getstate()
son un detalle de implementación, por lo
que este ejemplo guarda los datos en un archivo con pickle
pero de lo
contrario lo trata como una caja negra. Si el archivo existe cuando el
programa comienza, carga el estado anterior y continúa. Cada ejecución produce
algunos números antes y después de guardar el estado, para mostrar que restaura
el estado hace que el generador produzca los mismos valores nuevamente.
$ python3 random_state.py
No state.dat, seeding
0.134 0.847 0.764
After saving state:
0.255 0.495 0.449
$ python3 random_state.py
Found state.dat, initializing random module
0.255 0.495 0.449
After saving state:
0.652 0.789 0.094
Número enteros aleatorios¶
random()
genera números de coma flotante. Es posible convertir los
resultados a enteros, pero usando randint()
para generar enteros
directamente es más conveniente.
import random
print('[1, 100]:', end=' ')
for i in range(3):
print(random.randint(1, 100), end=' ')
print('\n[-5, 5]:', end=' ')
for i in range(3):
print(random.randint(-5, 5), end=' ')
print()
Los argumentos para randint()
son los extremos del rango inclusivo para los
valores. Los números pueden ser positivos o negativos, pero el primer valor
debe ser menor que el segundo.
$ python3 random_randint.py
[1, 100]: 98 75 34
[-5, 5]: 4 0 5
randrange()
es una forma más general de seleccionar valores de un rango.
import random
for i in range(3):
print(random.randrange(0, 101, 5), end=' ')
print()
randrange()
admite un argumento paso
, además de los valores inicio y
fin, por lo que es totalmente equivalente a seleccionar un valor aleatorio
desde range(inicio, fin, paso)
. Es más eficiente, porque el rango no está
realmente construido.
$ python3 random_randrange.py
15 20 85
Eligiendo elementos aleatorios¶
Un uso común para generadores de números aleatorios es seleccionar un elemento
aleatorio a partir de una secuencia de valores enumerados, incluso si esos
valores no son números. random
incluye la función choice()
para hacer
una selección al azar de una secuencia. Este ejemplo simula lanzar una moneda
10,000 veces para contar cuántas veces sale cara y cuántas veces sale cruz.
import random
import itertools
outcomes = {
'heads': 0,
'tails': 0,
}
sides = list(outcomes.keys())
for i in range(10000):
outcomes[random.choice(sides)] += 1
print('Heads:', outcomes['heads'])
print('Tails:', outcomes['tails'])
Solo se permiten dos resultados, así que en lugar de usar números y
convertirlos las palabras «cabezas» y «cola» se utilizan con choice()
. Los
resultados se tabulan en un diccionario usando los nombres de resultados como
llaves.
$ python3 random_choice.py
Heads: 5091
Tails: 4909
Permutaciones¶
Una simulación de un juego de cartas necesita mezclar el mazo de cartas y luego
repartirlas a los jugadores, sin usar la misma tarjeta más de una vez. Usando
choice()
podría dar como resultado que se reparta la misma carta dos veces,
por lo tanto, en su lugar, el mazo se puede mezclar con shuffle()
y luego,
las cartas individuales se eliminan a medida que se reparten.
import random
import itertools
FACE_CARDS = ('J', 'Q', 'K', 'A')
SUITS = ('H', 'D', 'C', 'S')
def new_deck():
return [
# Always use 2 places for the value, so the strings
# are a consistent width.
'{:>2}{}'.format(*c)
for c in itertools.product(
itertools.chain(range(2, 11), FACE_CARDS),
SUITS,
)
]
def show_deck(deck):
p_deck = deck[:]
while p_deck:
row = p_deck[:13]
p_deck = p_deck[13:]
for j in row:
print(j, end=' ')
print()
# Make a new deck, with the cards in order
deck = new_deck()
print('Initial deck:')
show_deck(deck)
# Shuffle the deck to randomize the order
random.shuffle(deck)
print('\nShuffled deck:')
show_deck(deck)
# Deal 4 hands of 5 cards each
hands = [[], [], [], []]
for i in range(5):
for h in hands:
h.append(deck.pop())
# Show the hands
print('\nHands:')
for n, h in enumerate(hands):
print('{}:'.format(n + 1), end=' ')
for c in h:
print(c, end=' ')
print()
# Show the remaining deck
print('\nRemaining deck:')
show_deck(deck)
Las cartas se representan como cadenas con el valor nominal y una letra indicando el palo. Las «manos» repartidas se crean agregando una carta a la vez a cada una de las cuatro listas, y quitándola del mazo para que no se puede repartir de nuevo.
$ python3 random_shuffle.py
Initial deck:
2H 2D 2C 2S 3H 3D 3C 3S 4H 4D 4C 4S 5H
5D 5C 5S 6H 6D 6C 6S 7H 7D 7C 7S 8H 8D
8C 8S 9H 9D 9C 9S 10H 10D 10C 10S JH JD JC
JS QH QD QC QS KH KD KC KS AH AD AC AS
Shuffled deck:
QD 8C JD 2S AC 2C 6S 6D 6C 7H JC QS QC
KS 4D 10C KH 5S 9C 10S 5C 7C AS 6H 3C 9H
4S 7S 10H 2D 8S AH 9S 8H QH 5D 5H KD 8D
10D 4C 3S 3H 7D AD 4H 9D 3D 2H KC JH JS
Hands:
1: JS 3D 7D 10D 5D
2: JH 9D 3H 8D QH
3: KC 4H 3S KD 8H
4: 2H AD 4C 5H 9S
Remaining deck:
QD 8C JD 2S AC 2C 6S 6D 6C 7H JC QS QC
KS 4D 10C KH 5S 9C 10S 5C 7C AS 6H 3C 9H
4S 7S 10H 2D 8S AH
Muestreo¶
Muchas simulaciones necesitan muestras aleatorias de una población valores de
entrada. La función sample()
genera muestras sin repetir valores y sin
modificar la secuencia de entrada. Este ejemplo imprime una muestra aleatoria
de palabras del diccionario del sistema.
import random
with open('/usr/share/dict/words', 'rt') as f:
words = f.readlines()
words = [w.rstrip() for w in words]
for w in random.sample(words, 5):
print(w)
El algoritmo para producir el conjunto de resultados tiene en cuenta los tamaños de la entrada y la muestra solicitada para producir el resultado de la manera eficiente posible.
$ python3 random_sample.py
streamlet
impestation
violaquercitrin
mycetoid
plethoretical
$ python3 random_sample.py
nonseditious
empyemic
ultrasonic
Kyurinish
amphide
Múltiples generadores simultáneos¶
Además de las funciones a nivel de módulo, random
incluye una clase
Random
para gestionar el estado interno de varios generadores aleatorios de
números. Todas las funciones descritas anteriormente están disponibles como
métodos de las instancias de Random
, y cada instancia puede ser
inicializada y utilizada por separado, sin interferir con los valores devueltos
por otras instancias.
import random
import time
print('Default initializiation:\n')
r1 = random.Random()
r2 = random.Random()
for i in range(3):
print('{:04.3f} {:04.3f}'.format(r1.random(), r2.random()))
print('\nSame seed:\n')
seed = time.time()
r1 = random.Random(seed)
r2 = random.Random(seed)
for i in range(3):
print('{:04.3f} {:04.3f}'.format(r1.random(), r2.random()))
En un sistema con buena creación nativa de valores aleatorios, las instancias comienzan en estados únicos. Sin embargo, si no hay un buen generador aleatorio en la plataforma, es probable que las instancias se hayan sembrado con la hora actual, y por lo tanto producen los mismos valores.
$ python3 random_random_class.py
Default initializiation:
0.862 0.390
0.833 0.624
0.252 0.080
Same seed:
0.466 0.466
0.682 0.682
0.407 0.407
SystemRandom¶
Algunos sistemas operativos proporcionan un generador de números aleatorios que
tiene acceso a más fuentes de entropía que se pueden introducir en el
generador. random
expone esta característica a través de la clase
SystemRandom
, que tiene la misma interfaz que Random
pero usa
os.urandom()
para generar los valores que forman la base de todos los otros
algoritmos.
import random
import time
print('Default initializiation:\n')
r1 = random.SystemRandom()
r2 = random.SystemRandom()
for i in range(3):
print('{:04.3f} {:04.3f}'.format(r1.random(), r2.random()))
print('\nSame seed:\n')
seed = time.time()
r1 = random.SystemRandom(seed)
r2 = random.SystemRandom(seed)
for i in range(3):
print('{:04.3f} {:04.3f}'.format(r1.random(), r2.random()))
Las secuencias producidas por SystemRandom
no son reproducibles porque la
aleatoriedad proviene del sistema, no del estado del software (de hecho,
seed()
y setstate()
no tienen efecto del todo).
$ python3 random_system_random.py
Default initializiation:
0.110 0.481
0.624 0.350
0.378 0.056
Same seed:
0.634 0.731
0.893 0.843
0.065 0.177
Distribuciones no uniformes¶
Si bien la distribución uniforme de los valores producidos por random()
es
útil para muchos propósitos, otras distribuciones modelan situaciones
específicas con mayor precisión. El módulo random
incluye funciones para
producir valores en esas distribuciones también. Se enumeran aquí, pero no se
cubren en detalle porque sus usos tienden a ser especializados y requieren
ejemplos más complejos.
Normal¶
La distribución normal se usa comúnmente para valores continuos no uniformes
tales como grados, alturas, pesos, etc. La curva producida por la distribución
tiene una forma distintiva que la ha llevado a sera apodada una «curva de
campana». random
incluye dos funciones para generar valores con una
distribución normal, normalvariate()
y gauss()
, ligeramente más rápida
(la distribución normal también llamada distribución de gauss).
La función relacionada, lognormvariate()
produce valores pseudoaleatorios
donde el logaritmo de los valores está distribuido normalmente. Las
distribuciones log-normales son útiles para los valores que son el producto de
varias variables aleatorias que no interactúan.
Aproximada¶
La distribución triangular se usa como una distribución aproximada para
tamaños de muestra pequeños. La «curva» de una distribución triangular tiene
puntos bajos a valores mínimos y máximos conocidos, y un punto alto en el modo,
que se estima en función del resultado «más probable» (reflejado por el
argumento de modo a triangular()
).
Exponencial¶
expovariate()
produce una distribución exponencial útil para simular
valores de llegada o de tiempo de intervalo para en procesos homogéneos Poisson
tales como la tasa de descomposición radiactiva o solicitudes que vienen a un
servidor web.
La distribución de Pareto, o ley de poder, coincide con muchos fenómenos
observables y fue popularizada por The Long Tail, por Chris Anderson. La
función paretovariate()
es útil para simular la asignación de recursos para
individuos (riqueza para las personas, demanda de músicos, atención a los
blogs, etc.).
Angular¶
La distribución de von Mises, o circular normal, (producida por
vonmisesvariate()
) se usa para calcular las probabilidades de valores
cíclicos como ángulos, días calendario y horas.
Tamaños¶
betavariate()
genera valores con la distribución Beta, que se usa
comúnmente en estadísticas de Bayes y aplicaciones como modelado de duración de
tareas.
La distribución Gamma producida por gammavariate()
se usa para modelar los
tamaños de cosas tales como tiempos de espera, lluvia y errores
computacionales.
La distribución de Weibull calculada por weibullvariate()
se usa en el
análisis de fallas, ingeniería industrial y pronóstico del clima. Describe la
distribución de tamaños de partículas u otros objetos discretos.
Ver también
- Documentación de la biblioteca estándar para random
- «Mersenne Twister: Un uniforme generador equidistribudo uniforme de 623 dimensiones de números pseudoaleatorios» – Articulo por M. Matsumoto y T. Nishimura de ACM Transactions on Modeling and Computer Simulation Vol. 8, No. 1, Enero pp.3-30 1998.
- Wikipedia: Mersenne twister – Artículo sobre el generador de número pseudoaleatorios usado por Python.
- Wikipedia: Uniform distribution – Artículo sobre la distribución uniforme continua.