Python
Tutorial Python: Módulo random
Aprende a usar el módulo random de Python para generar números aleatorios, seleccionar elementos y simular distribuciones probabilísticas.
Aprende Python y certifícateGeneración de números aleatorios
El módulo random
de Python proporciona una serie de funciones para generar números aleatorios, lo que resulta extremadamente útil en diversas aplicaciones como simulaciones, juegos, pruebas y análisis estadístico. Este módulo utiliza el algoritmo Mersenne Twister, un generador de números pseudoaleatorios de alta calidad que produce secuencias de números que parecen aleatorios pero son deterministas si se conoce la semilla inicial.
Para empezar a utilizar el módulo random
, primero debemos importarlo:
import random
Funciones básicas para generar números aleatorios
La función más simple del módulo es random()
, que devuelve un número flotante aleatorio entre 0.0 (inclusive) y 1.0 (exclusive):
# Genera un número flotante aleatorio entre 0.0 y 1.0
valor = random.random()
print(valor) # Ejemplo de salida: 0.7364926331879848
Si necesitamos un número flotante dentro de un rango específico, podemos usar la función uniform()
:
# Genera un número flotante aleatorio entre 10.5 y 20.5
valor = random.uniform(10.5, 20.5)
print(valor) # Ejemplo de salida: 15.273876587431254
Para generar números enteros aleatorios, disponemos de la función randint()
, que incluye ambos extremos del rango especificado:
# Genera un número entero aleatorio entre 1 y 6 (ambos inclusive)
dado = random.randint(1, 6)
print(f"Resultado del dado: {dado}") # Ejemplo: Resultado del dado: 4
También existe la función randrange()
, que permite especificar un rango con inicio, fin y paso, similar a la función range()
:
# Genera un número entero aleatorio entre 0 y 100, pero solo múltiplos de 5
valor = random.randrange(0, 101, 5)
print(valor) # Posibles resultados: 0, 5, 10, 15, ..., 95, 100
Control de la aleatoriedad con semillas
A veces necesitamos que nuestros números "aleatorios" sean reproducibles, por ejemplo, para pruebas o depuración. Para esto, podemos establecer una semilla (seed) que inicializa el generador de números aleatorios:
# Establecer una semilla fija
random.seed(42)
# Ahora los números generados serán siempre los mismos
print(random.random()) # Siempre será: 0.6394267984578837
print(random.randint(1, 10)) # Siempre será: 1
# Si establecemos la misma semilla de nuevo, obtendremos la misma secuencia
random.seed(42)
print(random.random()) # De nuevo: 0.6394267984578837
Aplicaciones prácticas
Simulación de un juego de dados
Podemos simular fácilmente el lanzamiento de dados utilizando randint()
:
def lanzar_dados(cantidad=2, caras=6):
"""Simula el lanzamiento de varios dados."""
return [random.randint(1, caras) for _ in range(cantidad)]
# Lanzar dos dados de 6 caras
resultado = lanzar_dados()
print(f"Resultado de los dados: {resultado}")
print(f"Suma total: {sum(resultado)}")
# Lanzar un dado de 20 caras (común en juegos de rol)
d20 = lanzar_dados(1, 20)[0]
print(f"Resultado del D20: {d20}")
Generación de contraseñas aleatorias
Podemos crear una función simple para generar contraseñas aleatorias:
def generar_password(longitud=12):
"""Genera una contraseña aleatoria con letras, números y símbolos."""
caracteres = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+"
password = ''.join(random.choice(caracteres) for _ in range(longitud))
return password
# Generar una contraseña de 16 caracteres
nueva_password = generar_password(16)
print(f"Contraseña generada: {nueva_password}")
Simulación de probabilidades
Podemos usar números aleatorios para simular eventos con diferentes probabilidades:
def evento_con_probabilidad(probabilidad):
"""Retorna True con la probabilidad especificada (0-1)."""
return random.random() < probabilidad
# Simular un evento con 30% de probabilidad
if evento_con_probabilidad(0.3):
print("¡El evento ocurrió!")
else:
print("El evento no ocurrió.")
# Contar cuántas veces ocurre un evento en múltiples intentos
exitos = sum(evento_con_probabilidad(0.3) for _ in range(1000))
print(f"El evento ocurrió {exitos} veces en 1000 intentos (~30% esperado)")
Generación de números aleatorios con distribuciones específicas
Además de la distribución uniforme (todos los valores tienen la misma probabilidad), el módulo random
ofrece funciones para generar números con otras distribuciones:
# Distribución normal (gaussiana) con media 0 y desviación estándar 1
valor_normal = random.normalvariate(0, 1)
print(f"Valor de distribución normal: {valor_normal}")
# Distribución triangular entre min, max con moda
valor_triangular = random.triangular(0, 10, 5)
print(f"Valor de distribución triangular: {valor_triangular}")
Generación de bytes aleatorios
Para aplicaciones criptográficas o que requieran datos binarios aleatorios, podemos usar:
# Generar 8 bytes aleatorios
bytes_aleatorios = random.randbytes(8)
print(f"Bytes aleatorios: {bytes_aleatorios}")
print(f"Representación hexadecimal: {bytes_aleatorios.hex()}")
Consideraciones de seguridad
Es importante destacar que el módulo random
no es adecuado para aplicaciones criptográficas o de seguridad. Para estos casos, Python proporciona el módulo secrets
que ofrece una fuente de aleatoriedad criptográficamente segura:
import secrets
# Generar un token seguro de 16 bytes
token = secrets.token_hex(16)
print(f"Token seguro: {token}")
# Generar un número entero seguro en un rango
num_seguro = secrets.randbelow(1000) # Número entre 0 y 999
print(f"Número aleatorio seguro: {num_seguro}")
El módulo random
es perfecto para simulaciones, juegos y aplicaciones donde la predictibilidad no es un problema de seguridad, mientras que secrets
debe usarse cuando la seguridad es crítica, como en la generación de tokens, contraseñas o claves.
Selección aleatoria de elementos
El módulo random
de Python no solo permite generar números aleatorios, sino que también ofrece funciones especializadas para seleccionar elementos de forma aleatoria a partir de secuencias como listas, tuplas o cadenas. Esta funcionalidad es extremadamente útil en aplicaciones como juegos, simulaciones, muestreo estadístico o cualquier situación donde necesitemos introducir aleatoriedad en la selección de datos.
Selección de un único elemento
La función más básica para seleccionar elementos aleatorios es choice()
, que selecciona un elemento al azar de una secuencia:
import random
# Seleccionar una carta aleatoria de una baraja
palos = ["♠", "♥", "♦", "♣"]
valores = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]
palo = random.choice(palos)
valor = random.choice(valores)
print(f"Carta seleccionada: {valor}{palo}") # Ejemplo: 7♥
Esta función es perfecta para simular situaciones donde se extrae un elemento al azar, como sacar una carta de una baraja o elegir un ganador de una rifa.
Selección de múltiples elementos
Cuando necesitamos seleccionar varios elementos de una secuencia, podemos usar la función choices()
, que permite seleccionar elementos con reemplazo (el mismo elemento puede ser seleccionado múltiples veces):
# Simular 5 lanzamientos de un dado
resultados = random.choices([1, 2, 3, 4, 5, 6], k=5)
print(f"Resultados de los dados: {resultados}") # Ejemplo: [3, 6, 2, 6, 1]
# Seleccionar 3 cartas con reemplazo (como sacar y volver a meter)
cartas = [f"{v}{p}" for v in valores for p in palos]
mano = random.choices(cartas, k=3)
print(f"Cartas seleccionadas: {mano}") # Ejemplo: ['4♠', '10♥', '4♠']
La función choices()
también permite especificar pesos para cada elemento, lo que resulta útil cuando queremos que algunos elementos tengan mayor probabilidad de ser seleccionados:
# Simular una ruleta con diferentes probabilidades
opciones = ["rojo", "negro", "verde"]
# El verde (0) tiene menos probabilidad que rojo y negro
pesos = [18, 18, 2] # Corresponde a una ruleta con 38 casillas
resultados = random.choices(opciones, weights=pesos, k=10)
print(f"Resultados de la ruleta: {resultados}")
# Contar frecuencias
frecuencias = {opcion: resultados.count(opcion) for opcion in opciones}
print(f"Frecuencias: {frecuencias}")
Muestreo sin reemplazo
Para seleccionar elementos sin repetición (como sacar cartas de una baraja sin devolverlas), usamos la función sample()
:
# Crear una baraja de cartas
baraja = [f"{v}{p}" for v in valores for p in palos]
# Repartir una mano de póker (5 cartas sin repetición)
mano_poker = random.sample(baraja, k=5)
print(f"Mano de póker: {mano_poker}") # Ejemplo: ['A♠', '10♦', '3♣', 'K♥', '7♠']
# Seleccionar 3 ganadores de un sorteo
participantes = ["Ana", "Carlos", "Elena", "David", "Beatriz", "Fernando"]
ganadores = random.sample(participantes, k=3)
print(f"Los ganadores son: {ganadores}") # Ejemplo: ['Elena', 'Fernando', 'Ana']
Es importante recordar que k
no puede ser mayor que la longitud de la secuencia cuando usamos sample()
, ya que no podemos seleccionar más elementos únicos de los que existen.
Selección aleatoria de caracteres
Podemos aplicar estas funciones a cadenas de texto para seleccionar caracteres aleatorios:
# Seleccionar un carácter aleatorio de una cadena
texto = "Python es divertido"
caracter = random.choice(texto)
print(f"Carácter aleatorio: '{caracter}'") # Ejemplo: 'o'
# Seleccionar 5 caracteres aleatorios sin repetición
caracteres = random.sample(texto, k=5)
print(f"Caracteres seleccionados: {caracteres}") # Ejemplo: ['P', 'e', 'i', 'd', 'n']
Aplicaciones prácticas
Generador de contraseñas mejorado
Podemos crear un generador de contraseñas más sofisticado que asegure la presencia de diferentes tipos de caracteres:
def generar_password_segura(longitud=12):
"""Genera una contraseña aleatoria con al menos un carácter de cada tipo."""
minusculas = "abcdefghijklmnopqrstuvwxyz"
mayusculas = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
numeros = "0123456789"
simbolos = "!@#$%^&*()-_=+[]{}|;:,.<>?"
# Asegurar al menos un carácter de cada tipo
password = [
random.choice(minusculas),
random.choice(mayusculas),
random.choice(numeros),
random.choice(simbolos)
]
# Completar con caracteres aleatorios
todos_caracteres = minusculas + mayusculas + numeros + simbolos
password.extend(random.choices(todos_caracteres, k=longitud-4))
# Mezclar la contraseña para que no siga un patrón predecible
random.shuffle(password)
return ''.join(password)
# Generar una contraseña segura
password = generar_password_segura(16)
print(f"Contraseña segura: {password}")
Selección de preguntas para un examen
Podemos simular la selección aleatoria de preguntas para un examen:
def crear_examen(banco_preguntas, num_preguntas=10, num_faciles=6, num_dificiles=4):
"""Crea un examen con preguntas fáciles y difíciles seleccionadas aleatoriamente."""
preguntas_faciles = random.sample(banco_preguntas['faciles'], k=num_faciles)
preguntas_dificiles = random.sample(banco_preguntas['dificiles'], k=num_dificiles)
examen = preguntas_faciles + preguntas_dificiles
# Mezclar las preguntas para que no estén agrupadas por dificultad
random.shuffle(examen)
return examen
# Banco de preguntas ejemplo
banco = {
'faciles': [f"Pregunta fácil #{i}" for i in range(1, 21)],
'dificiles': [f"Pregunta difícil #{i}" for i in range(1, 16)]
}
# Crear un examen
examen = crear_examen(banco)
print("Examen generado:")
for i, pregunta in enumerate(examen, 1):
print(f"{i}. {pregunta}")
Simulación de una lotería
Podemos simular un sorteo de lotería donde se extraen bolas numeradas sin reemplazo:
def sorteo_loteria(rango_numeros=(1, 49), bolas_extraidas=6):
"""Simula un sorteo de lotería extrayendo bolas numeradas sin reemplazo."""
numeros_disponibles = list(range(rango_numeros[0], rango_numeros[1] + 1))
return sorted(random.sample(numeros_disponibles, k=bolas_extraidas))
# Simular un sorteo de lotería
combinacion_ganadora = sorteo_loteria()
print(f"Combinación ganadora: {combinacion_ganadora}")
# Simular múltiples apuestas
num_apuestas = 5
print("\nTus apuestas:")
for i in range(num_apuestas):
apuesta = sorteo_loteria()
print(f"Apuesta {i+1}: {apuesta}")
Selección aleatoria ponderada personalizada
A veces necesitamos implementar selecciones aleatorias con probabilidades específicas que no se ajustan directamente a choices()
. Por ejemplo, para simular un sistema de "gacha" en un juego:
def seleccion_gacha():
"""Simula un sistema de gacha con diferentes raridades de objetos."""
raridades = {
"común": 0.70, # 70% de probabilidad
"raro": 0.25, # 25% de probabilidad
"épico": 0.04, # 4% de probabilidad
"legendario": 0.01 # 1% de probabilidad
}
# Objetos por rareza
objetos = {
"común": ["Poción", "Piedra", "Madera", "Cuero", "Hierro"],
"raro": ["Espada", "Escudo", "Arco", "Armadura", "Amuleto"],
"épico": ["Varita mágica", "Grimorio", "Cetro", "Corona", "Capa"],
"legendario": ["Excalibur", "Santo Grial", "Anillo único", "Mjölnir"]
}
# Seleccionar rareza según probabilidades
rareza = random.choices(
list(raridades.keys()),
weights=list(raridades.values()),
k=1
)[0]
# Seleccionar objeto de esa rareza
objeto = random.choice(objetos[rareza])
return rareza, objeto
# Simular 10 tiradas
print("Resultados del gacha:")
for i in range(10):
rareza, objeto = seleccion_gacha()
print(f"Tirada {i+1}: {objeto} ({rareza})")
Las funciones de selección aleatoria del módulo random
son herramientas versátiles que pueden aplicarse en numerosos contextos, desde juegos y simulaciones hasta aplicaciones educativas y científicas. La capacidad de seleccionar elementos con o sin reemplazo, y con diferentes probabilidades, nos permite modelar una amplia variedad de situaciones del mundo real donde la aleatoriedad juega un papel importante.
Mezcla de secuencias
El módulo random
de Python ofrece una funcionalidad especialmente útil para reordenar elementos de forma aleatoria mediante la función shuffle()
. Esta capacidad resulta fundamental en numerosas aplicaciones donde necesitamos alterar el orden de los elementos de una colección de manera impredecible.
A diferencia de las funciones de selección que hemos visto anteriormente, shuffle()
modifica la secuencia original in-place, es decir, altera directamente la colección sin crear una nueva:
import random
# Crear una lista ordenada
cartas = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]
# Mezclar la lista
random.shuffle(cartas)
print(f"Cartas mezcladas: {cartas}") # Ejemplo: ['7', 'A', 'J', '4', '2', '10', 'K', '5', '8', 'Q', '3', '9', '6']
Es importante destacar que shuffle()
solo funciona con objetos mutables como listas. No podemos aplicarla directamente a tuplas, cadenas u otros objetos inmutables:
# Esto generará un error
palabra = "Python"
# random.shuffle(palabra) # TypeError: 'str' object does not support item assignment
# Para mezclar una cadena, primero convertimos a lista, mezclamos y luego volvemos a unir
lista_caracteres = list(palabra)
random.shuffle(lista_caracteres)
palabra_mezclada = ''.join(lista_caracteres)
print(f"Palabra mezclada: {palabra_mezclada}") # Ejemplo: "yPntho"
Implementación de barajas de cartas
Una de las aplicaciones más intuitivas de shuffle()
es la simulación de barajas de cartas:
def crear_baraja():
"""Crea una baraja de cartas ordenada."""
palos = ["♠", "♥", "♦", "♣"]
valores = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]
return [f"{v}{p}" for p in palos for v in valores]
def barajar(mazo):
"""Mezcla un mazo de cartas."""
random.shuffle(mazo)
return mazo # Aunque no es necesario retornar el mazo, puede ser útil para encadenar operaciones
# Crear y barajar una baraja
baraja = crear_baraja()
print(f"Baraja ordenada (primeras 5 cartas): {baraja[:5]}")
barajar(baraja)
print(f"Baraja mezclada (primeras 5 cartas): {baraja[:5]}")
Juegos de mesa y simulaciones
La mezcla aleatoria es esencial en la simulación de juegos de mesa donde el orden de los elementos debe ser impredecible:
def juego_memoria():
"""Simula la preparación de un juego de memoria con parejas de cartas."""
# Crear parejas de símbolos
simbolos = ["🍎", "🍌", "🍒", "🍓", "🍇", "🍉", "🍋", "🍍"]
parejas = simbolos * 2
# Mezclar las cartas
random.shuffle(parejas)
# Disponer en un tablero 4x4
tablero = []
for i in range(0, 16, 4):
fila = parejas[i:i+4]
tablero.append(fila)
return tablero
# Crear un juego de memoria
tablero = juego_memoria()
print("Tablero de memoria (cartas boca abajo):")
for fila in tablero:
print(fila)
Generación de cuestionarios aleatorios
En aplicaciones educativas, podemos usar shuffle()
para crear cuestionarios donde tanto las preguntas como las opciones de respuesta aparecen en orden aleatorio:
def crear_cuestionario(preguntas, mezclar_opciones=True):
"""Crea un cuestionario con preguntas en orden aleatorio."""
# Hacer una copia para no modificar el original
cuestionario = preguntas.copy()
# Mezclar el orden de las preguntas
random.shuffle(cuestionario)
# Opcionalmente mezclar las opciones de cada pregunta
if mezclar_opciones:
for pregunta in cuestionario:
# Guardar la respuesta correcta
respuesta_correcta = pregunta["opciones"][pregunta["correcta"]]
# Mezclar las opciones
random.shuffle(pregunta["opciones"])
# Actualizar el índice de la respuesta correcta
pregunta["correcta"] = pregunta["opciones"].index(respuesta_correcta)
return cuestionario
# Ejemplo de preguntas
preguntas = [
{
"texto": "¿Cuál es la capital de Francia?",
"opciones": ["París", "Londres", "Madrid", "Berlín"],
"correcta": 0
},
{
"texto": "¿En qué año se fundó Python?",
"opciones": ["1991", "1989", "2000", "1995"],
"correcta": 0
},
{
"texto": "¿Quién escribió 'Don Quijote de la Mancha'?",
"opciones": ["Miguel de Cervantes", "Federico García Lorca", "Gabriel García Márquez", "Pablo Neruda"],
"correcta": 0
}
]
# Crear un cuestionario aleatorio
cuestionario = crear_cuestionario(preguntas)
# Mostrar el cuestionario
print("CUESTIONARIO:")
for i, pregunta in enumerate(cuestionario, 1):
print(f"\nPregunta {i}: {pregunta['texto']}")
for j, opcion in enumerate(pregunta["opciones"]):
print(f" {j+1}. {opcion}")
print(f" (Respuesta correcta: {pregunta['correcta']+1})")
Algoritmo de Fisher-Yates
Internamente, shuffle()
utiliza una variante del algoritmo de Fisher-Yates (también conocido como algoritmo de Knuth), que es un método eficiente para generar una permutación aleatoria de una secuencia finita. Podemos implementar nuestra propia versión de este algoritmo para entender cómo funciona:
def fisher_yates_shuffle(lista):
"""Implementación del algoritmo de Fisher-Yates para mezclar una lista."""
n = len(lista)
for i in range(n-1, 0, -1):
# Elegir un índice aleatorio entre 0 e i (inclusive)
j = random.randint(0, i)
# Intercambiar elementos
lista[i], lista[j] = lista[j], lista[i]
return lista
# Probar nuestro algoritmo
numeros = list(range(1, 11))
print(f"Lista original: {numeros}")
fisher_yates_shuffle(numeros)
print(f"Lista mezclada: {numeros}")
Este algoritmo tiene una complejidad de tiempo O(n), lo que lo hace muy eficiente incluso para secuencias grandes.
Generación de datos aleatorios para pruebas
La mezcla de secuencias es especialmente útil para generar datos de prueba en desarrollo de software:
def generar_datos_prueba(n=100):
"""Genera un conjunto de datos de prueba con valores mezclados."""
# Crear datos de ejemplo
nombres = ["Ana", "Carlos", "Elena", "David", "Beatriz", "Fernando", "Gabriela", "Héctor"]
apellidos = ["García", "López", "Martínez", "Rodríguez", "Fernández", "González", "Pérez", "Sánchez"]
ciudades = ["Madrid", "Barcelona", "Valencia", "Sevilla", "Bilbao", "Málaga", "Zaragoza", "Murcia"]
# Generar datos
datos = []
for _ in range(n):
# Mezclar las listas para seleccionar elementos aleatorios
random.shuffle(nombres)
random.shuffle(apellidos)
random.shuffle(ciudades)
# Crear un registro
registro = {
"id": _ + 1,
"nombre": nombres[0],
"apellido": apellidos[0],
"edad": random.randint(18, 80),
"ciudad": ciudades[0]
}
datos.append(registro)
return datos
# Generar 5 registros de prueba
datos_prueba = generar_datos_prueba(5)
for registro in datos_prueba:
print(registro)
Simulación de reparto de cartas
Podemos combinar shuffle()
con otras operaciones para simular juegos de cartas más complejos:
def repartir_cartas(num_jugadores, cartas_por_jugador):
"""Reparte cartas a varios jugadores."""
# Crear y mezclar la baraja
baraja = crear_baraja()
random.shuffle(baraja)
# Repartir cartas
manos = []
for i in range(num_jugadores):
inicio = i * cartas_por_jugador
fin = inicio + cartas_por_jugador
mano = baraja[inicio:fin]
manos.append(mano)
# Devolver las manos y el resto de la baraja
resto_baraja = baraja[num_jugadores * cartas_por_jugador:]
return manos, resto_baraja
# Simular una partida de póker (5 jugadores, 2 cartas cada uno)
manos_jugadores, resto = repartir_cartas(5, 2)
print("Reparto de cartas para póker Texas Hold'em:")
for i, mano in enumerate(manos_jugadores, 1):
print(f"Jugador {i}: {mano}")
# Mostrar las cartas comunitarias (flop, turn y river)
print(f"\nFlop: {resto[:3]}")
print(f"Turn: {resto[3]}")
print(f"River: {resto[4]}")
Generación de playlists aleatorias
Otra aplicación común es la creación de listas de reproducción aleatorias:
def crear_playlist_aleatoria(canciones, duracion_maxima=60):
"""Crea una playlist aleatoria con una duración máxima en minutos."""
# Hacer una copia y mezclar
playlist = canciones.copy()
random.shuffle(playlist)
# Seleccionar canciones hasta alcanzar la duración máxima
seleccionadas = []
duracion_total = 0
for cancion in playlist:
if duracion_total + cancion["duracion"] <= duracion_maxima:
seleccionadas.append(cancion)
duracion_total += cancion["duracion"]
return seleccionadas, duracion_total
# Lista de canciones de ejemplo
canciones = [
{"titulo": "Bohemian Rhapsody", "artista": "Queen", "duracion": 5.9},
{"titulo": "Imagine", "artista": "John Lennon", "duracion": 3.1},
{"titulo": "Hotel California", "artista": "Eagles", "duracion": 6.5},
{"titulo": "Sweet Child O' Mine", "artista": "Guns N' Roses", "duracion": 5.6},
{"titulo": "Billie Jean", "artista": "Michael Jackson", "duracion": 4.9},
{"titulo": "Stairway to Heaven", "artista": "Led Zeppelin", "duracion": 8.0},
{"titulo": "Yesterday", "artista": "The Beatles", "duracion": 2.1},
{"titulo": "Smells Like Teen Spirit", "artista": "Nirvana", "duracion": 5.0},
{"titulo": "Nothing Else Matters", "artista": "Metallica", "duracion": 6.3},
{"titulo": "Wonderwall", "artista": "Oasis", "duracion": 4.2}
]
# Crear una playlist de 30 minutos
playlist, duracion = crear_playlist_aleatoria(canciones, 30)
print(f"Playlist aleatoria (Duración total: {duracion:.1f} minutos):")
for i, cancion in enumerate(playlist, 1):
print(f"{i}. {cancion['titulo']} - {cancion['artista']} ({cancion['duracion']} min)")
Consideraciones sobre la aleatoriedad
Es importante recordar que la calidad de la mezcla depende del generador de números aleatorios subyacente. Para aplicaciones críticas donde la imprevisibilidad es esencial (como juegos de azar en línea), puede ser necesario utilizar fuentes de aleatoriedad más robustas:
import secrets
def shuffle_seguro(lista):
"""Versión más segura de shuffle usando el módulo secrets."""
n = len(lista)
for i in range(n-1, 0, -1):
# Usar secrets para mayor seguridad criptográfica
j = secrets.randbelow(i + 1)
lista[i], lista[j] = lista[j], lista[i]
return lista
# Ejemplo de uso para una aplicación de seguridad crítica
claves_cifrado = ["clave1", "clave2", "clave3", "clave4", "clave5"]
shuffle_seguro(claves_cifrado)
print(f"Claves mezcladas de forma segura: {claves_cifrado}")
La función shuffle()
del módulo random
es una herramienta versátil que simplifica enormemente la tarea de reordenar elementos de forma aleatoria, siendo fundamental en juegos, simulaciones y generación de datos de prueba. Su implementación eficiente y fácil uso la convierten en un componente esencial del arsenal de cualquier programador Python.
Distribuciones probabilísticas
El módulo random
de Python no solo permite generar números aleatorios uniformes o seleccionar elementos al azar, sino que también ofrece funciones para generar valores aleatorios que siguen diferentes distribuciones probabilísticas. Estas distribuciones son fundamentales en estadística, simulaciones científicas, análisis de datos y modelado de fenómenos naturales.
A diferencia de la distribución uniforme (donde todos los valores tienen la misma probabilidad de ocurrir), las distribuciones probabilísticas permiten modelar situaciones donde ciertos valores son más probables que otros, siguiendo patrones matemáticos específicos.
import random
import matplotlib.pyplot as plt
Distribución normal (gaussiana)
La distribución normal o gaussiana es quizás la más utilizada en estadística y ciencias. Describe fenómenos naturales como la altura de una población, errores de medición o fluctuaciones aleatorias.
# Generar un valor aleatorio con distribución normal
# Parámetros: media (mu) y desviación estándar (sigma)
valor = random.normalvariate(mu=0, sigma=1)
print(f"Valor aleatorio con distribución normal: {valor:.4f}")
# Generar una muestra de 1000 valores
muestra_normal = [random.normalvariate(mu=100, sigma=15) for _ in range(1000)]
La función normalvariate()
genera valores aleatorios donde la mayoría se concentran cerca de la media (parámetro mu
), y la desviación estándar (parámetro sigma
) controla qué tan dispersos están los valores.
También existe la variante gauss()
, que implementa el mismo concepto pero con un algoritmo ligeramente diferente:
# Alternativa a normalvariate
valor_gauss = random.gauss(mu=0, sigma=1)
Distribución triangular
La distribución triangular es útil cuando conocemos los valores mínimo, máximo y más probable (moda). Es común en análisis de riesgos y estimaciones de proyectos.
# Generar un valor con distribución triangular
# Parámetros: mínimo, máximo y moda (valor más probable)
valor = random.triangular(low=0, high=10, mode=5)
print(f"Valor aleatorio con distribución triangular: {valor:.4f}")
# Generar una muestra de 1000 valores
muestra_triangular = [random.triangular(0, 100, 60) for _ in range(1000)]
Esta distribución es particularmente útil cuando tenemos estimaciones de "mejor caso", "peor caso" y "caso más probable", como en la planificación de proyectos.
Distribución exponencial
La distribución exponencial modela el tiempo entre eventos que ocurren a una tasa constante e independiente, como las llegadas de clientes a un establecimiento o el tiempo hasta que un componente falla.
# Generar un valor con distribución exponencial
# El parámetro lambda controla la tasa (mayor lambda = menor media)
valor = random.expovariate(lambd=0.5)
print(f"Valor aleatorio con distribución exponencial: {valor:.4f}")
# Generar una muestra de 1000 valores
muestra_exponencial = [random.expovariate(0.1) for _ in range(1000)]
El parámetro lambd
es la tasa de ocurrencia (lambda). La media de esta distribución es 1/lambda.
Distribución beta
La distribución beta es versátil para modelar proporciones o probabilidades, ya que genera valores entre 0 y 1. Es útil en análisis bayesiano y para representar incertidumbre sobre probabilidades.
# Generar un valor con distribución beta
# Parámetros alpha y beta controlan la forma
valor = random.betavariate(alpha=2, beta=5)
print(f"Valor aleatorio con distribución beta: {valor:.4f}")
# Generar una muestra de 1000 valores
muestra_beta = [random.betavariate(2, 5) for _ in range(1000)]
Los parámetros alpha
y beta
controlan la forma de la distribución. Cuando ambos son mayores que 1, la distribución tiene forma de campana; cuando ambos son menores que 1, tiene forma de U.
Distribución gamma
La distribución gamma modela el tiempo hasta que ocurren k
eventos en un proceso de Poisson. Es útil en teoría de colas, análisis de supervivencia y modelado de precipitaciones.
# Generar un valor con distribución gamma
# Parámetros: alpha (forma) y beta (escala)
valor = random.gammavariate(alpha=2, beta=1)
print(f"Valor aleatorio con distribución gamma: {valor:.4f}")
# Generar una muestra de 1000 valores
muestra_gamma = [random.gammavariate(2, 1) for _ in range(1000)]
El parámetro alpha
controla la forma y beta
controla la escala de la distribución.
Distribución log-normal
La distribución log-normal aparece cuando el logaritmo de una variable sigue una distribución normal. Es común en fenómenos como ingresos, precios de acciones o tamaños de partículas.
# Generar un valor con distribución log-normal
# Parámetros: media y desviación estándar del logaritmo
valor = random.lognormvariate(mu=0, sigma=0.5)
print(f"Valor aleatorio con distribución log-normal: {valor:.4f}")
# Generar una muestra de 1000 valores
muestra_lognormal = [random.lognormvariate(0, 0.5) for _ in range(1000)]
Esta distribución genera valores positivos con una cola larga hacia la derecha, lo que la hace adecuada para modelar variables que no pueden ser negativas y tienen valores extremos ocasionales.
Distribución de Pareto
La distribución de Pareto sigue el principio de "los pocos vitales y los muchos triviales" (regla 80/20). Es útil para modelar distribuciones de riqueza, tamaños de ciudades o frecuencia de palabras.
# Generar un valor con distribución de Pareto
# El parámetro alpha controla la forma (menor alpha = cola más larga)
valor = random.paretovariate(alpha=1.5)
print(f"Valor aleatorio con distribución de Pareto: {valor:.4f}")
# Generar una muestra de 1000 valores
muestra_pareto = [random.paretovariate(1.5) for _ in range(1000)]
Un valor menor de alpha
produce una distribución con una cola más larga, lo que significa mayor probabilidad de valores extremos.
Distribución de von Mises
La distribución de von Mises es la equivalente circular de la distribución normal. Es útil para ángulos, direcciones o datos cíclicos como horas del día o estaciones.
# Generar un valor con distribución de von Mises
# Parámetros: mu (dirección media) y kappa (concentración)
valor = random.vonmisesvariate(mu=0, kappa=4)
print(f"Valor aleatorio con distribución de von Mises: {valor:.4f}")
# Generar una muestra de 1000 valores
muestra_vonmises = [random.vonmisesvariate(0, 4) for _ in range(1000)]
El parámetro mu
indica la dirección media (en radianes) y kappa
controla la concentración alrededor de esa dirección (mayor kappa = distribución más concentrada).
Distribución de Weibull
La distribución de Weibull es ampliamente utilizada en análisis de fiabilidad y supervivencia. Modela el tiempo hasta el fallo de componentes y sistemas.
# Generar un valor con distribución de Weibull
# Parámetros: alpha (escala) y beta (forma)
valor = random.weibullvariate(alpha=1, beta=1.5)
print(f"Valor aleatorio con distribución de Weibull: {valor:.4f}")
# Generar una muestra de 1000 valores
muestra_weibull = [random.weibullvariate(1, 1.5) for _ in range(1000)]
El parámetro alpha
controla la escala y beta
la forma. Cuando beta = 1
, la distribución de Weibull se reduce a la distribución exponencial.
Aplicaciones prácticas
Simulación de tiempos de espera
Podemos simular los tiempos de espera entre llegadas de clientes a un establecimiento:
def simular_llegadas_clientes(tiempo_total=60, tasa_media=5):
"""
Simula llegadas de clientes durante un período de tiempo.
Args:
tiempo_total: Duración de la simulación en minutos
tasa_media: Número medio de clientes por minuto
Returns:
Lista de tiempos de llegada
"""
tiempos = []
tiempo_actual = 0
while tiempo_actual < tiempo_total:
# Tiempo hasta la próxima llegada (distribución exponencial)
tiempo_hasta_siguiente = random.expovariate(tasa_media)
tiempo_actual += tiempo_hasta_siguiente
if tiempo_actual < tiempo_total:
tiempos.append(tiempo_actual)
return tiempos
# Simular llegadas durante una hora
llegadas = simular_llegadas_clientes(60, 2)
print(f"Número de clientes en una hora: {len(llegadas)}")
print(f"Primeras 5 llegadas (minutos): {[round(t, 2) for t in llegadas[:5]]}")
Simulación de alturas de una población
Podemos generar una muestra de alturas de una población utilizando la distribución normal:
def generar_alturas(n=1000, genero="mixto"):
"""
Genera alturas aleatorias para una población.
Args:
n: Número de personas
genero: "hombre", "mujer" o "mixto"
Returns:
Lista de alturas en centímetros
"""
if genero == "hombre":
# Media y desviación estándar para hombres (en cm)
return [random.normalvariate(176, 7) for _ in range(n)]
elif genero == "mujer":
# Media y desviación estándar para mujeres (en cm)
return [random.normalvariate(163, 6) for _ in range(n)]
else:
# Población mixta (50% hombres, 50% mujeres)
hombres = generar_alturas(n // 2, "hombre")
mujeres = generar_alturas(n - (n // 2), "mujer")
return hombres + mujeres
# Generar alturas para 1000 personas
alturas = generar_alturas(1000)
print(f"Altura media de la muestra: {sum(alturas)/len(alturas):.2f} cm")
print(f"Altura mínima: {min(alturas):.2f} cm, Altura máxima: {max(alturas):.2f} cm")
Simulación de duración de baterías
Podemos usar la distribución de Weibull para simular la vida útil de baterías:
def simular_duracion_baterias(n=100, vida_media=1000, forma=2.5):
"""
Simula la duración de baterías en horas.
Args:
n: Número de baterías
vida_media: Vida media esperada en horas
forma: Parámetro de forma (beta) de Weibull
Returns:
Lista de duraciones en horas
"""
# Calcular el parámetro de escala a partir de la vida media y forma
# Para Weibull: E[X] = alpha * Gamma(1 + 1/beta)
# Para beta=2.5, Gamma(1+1/2.5) ≈ 0.8873
escala = vida_media / 0.8873
return [random.weibullvariate(escala, forma) for _ in range(n)]
# Simular la duración de 100 baterías
duraciones = simular_duracion_baterias(100)
print(f"Duración media: {sum(duraciones)/len(duraciones):.2f} horas")
print(f"Porcentaje de baterías que duran menos de 800 horas: {sum(1 for d in duraciones if d < 800)/len(duraciones)*100:.1f}%")
Simulación de rendimientos financieros
La distribución log-normal es adecuada para simular rendimientos de inversiones:
def simular_rendimiento_inversion(capital_inicial=1000, rendimiento_anual=0.08,
volatilidad=0.15, años=10, simulaciones=100):
"""
Simula el rendimiento de una inversión utilizando un modelo log-normal.
Args:
capital_inicial: Capital inicial en euros
rendimiento_anual: Rendimiento anual esperado (0.08 = 8%)
volatilidad: Volatilidad anual (0.15 = 15%)
años: Número de años
simulaciones: Número de simulaciones
Returns:
Lista de capitales finales
"""
resultados = []
for _ in range(simulaciones):
capital = capital_inicial
for _ in range(años):
# Rendimiento anual con distribución log-normal
# Ajustamos mu para que la media sea el rendimiento esperado
mu = rendimiento_anual - (volatilidad**2) / 2
rendimiento = random.lognormvariate(mu, volatilidad)
capital *= (1 + rendimiento)
resultados.append(capital)
return resultados
# Simular 100 escenarios de inversión
resultados = simular_rendimiento_inversion()
print(f"Capital medio después de 10 años: {sum(resultados)/len(resultados):.2f} €")
print(f"Mejor escenario: {max(resultados):.2f} €")
print(f"Peor escenario: {min(resultados):.2f} €")
Simulación de lluvia diaria
Podemos usar la distribución gamma para simular precipitaciones:
def simular_lluvia_mensual(dias=30, probabilidad_lluvia=0.3,
forma=2, escala=5):
"""
Simula la lluvia diaria durante un mes.
Args:
dias: Número de días
probabilidad_lluvia: Probabilidad de que llueva en un día
forma: Parámetro de forma de la distribución gamma
escala: Parámetro de escala de la distribución gamma
Returns:
Lista de precipitaciones diarias en mm
"""
precipitaciones = []
for _ in range(dias):
# Determinar si llueve ese día
if random.random() < probabilidad_lluvia:
# Cantidad de lluvia (distribución gamma)
cantidad = random.gammavariate(forma, escala)
precipitaciones.append(cantidad)
else:
precipitaciones.append(0)
return precipitaciones
# Simular la lluvia de un mes
lluvia = simular_lluvia_mensual()
print(f"Días con lluvia: {sum(1 for p in lluvia if p > 0)}")
print(f"Precipitación total: {sum(lluvia):.1f} mm")
print(f"Día más lluvioso: {max(lluvia):.1f} mm")
Las distribuciones probabilísticas del módulo random
nos permiten modelar una amplia variedad de fenómenos naturales y procesos estocásticos. Al elegir la distribución adecuada para cada situación, podemos crear simulaciones más realistas y obtener resultados que reflejen mejor el comportamiento del mundo real.
Ejercicios de esta lección Módulo random
Evalúa tus conocimientos de esta lección Módulo random con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Módulo math
Reto herencia
Excepciones
Introducción a Python
Reto variables
Funciones Python
Reto funciones
Módulo datetime
Reto acumulación
Reto estructuras condicionales
Polimorfismo
Módulo os
Reto métodos dunder
Diccionarios
Reto clases y objetos
Reto operadores
Operadores
Estructuras de control
Funciones lambda
Reto diccionarios
Reto función lambda
Encapsulación
Reto coleciones
Reto funciones auxiliares
Crear módulos y paquetes
Módulo datetime
Excepciones
Operadores
Diccionarios
Reto map, filter
Reto tuplas
Proyecto gestor de tareas CRUD
Tuplas
Variables
Tipos de datos
Conjuntos
Reto mixins
Módulo csv
Módulo json
Herencia
Análisis de datos de ventas con Pandas
Reto fechas y tiempo
Reto estructuras de iteración
Funciones
Reto comprehensions
Variables
Reto serialización
Módulo csv
Reto polimorfismo
Polimorfismo
Clases y objetos
Reto encapsulación
Estructuras de control
Importar módulos y paquetes
Módulo math
Funciones lambda
Reto excepciones
Listas
Reto archivos
Encapsulación
Reto conjuntos
Clases y objetos
Instalación de Python y creación de proyecto
Reto listas
Tipos de datos
Crear módulos y paquetes
Tuplas
Herencia
Reto acceso a sistema
Proyecto sintaxis calculadora
Importar módulos y paquetes
Clases y objetos
Módulo os
Listas
Conjuntos
Reto tipos de datos
Reto matemáticas
Módulo json
Todas las lecciones de Python
Accede a todas las lecciones de Python y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Python
Introducción
Instalación Y Creación De Proyecto
Introducción
Tema 2: Tipos De Datos, Variables Y Operadores
Introducción
Instalación De Python
Introducción
Tipos De Datos
Sintaxis
Variables
Sintaxis
Operadores
Sintaxis
Estructuras De Control
Sintaxis
Funciones
Sintaxis
Estructuras Control Iterativo
Sintaxis
Estructuras Control Condicional
Sintaxis
Testing Con Pytest
Sintaxis
Listas
Estructuras De Datos
Tuplas
Estructuras De Datos
Diccionarios
Estructuras De Datos
Conjuntos
Estructuras De Datos
Comprehensions
Estructuras De Datos
Clases Y Objetos
Programación Orientada A Objetos
Excepciones
Programación Orientada A Objetos
Encapsulación
Programación Orientada A Objetos
Herencia
Programación Orientada A Objetos
Polimorfismo
Programación Orientada A Objetos
Mixins Y Herencia Múltiple
Programación Orientada A Objetos
Métodos Especiales (Dunder Methods)
Programación Orientada A Objetos
Composición De Clases
Programación Orientada A Objetos
Funciones Lambda
Programación Funcional
Aplicación Parcial
Programación Funcional
Entrada Y Salida, Manejo De Archivos
Programación Funcional
Decoradores
Programación Funcional
Generadores
Programación Funcional
Paradigma Funcional
Programación Funcional
Composición De Funciones
Programación Funcional
Funciones Orden Superior Map Y Filter
Programación Funcional
Funciones Auxiliares
Programación Funcional
Reducción Y Acumulación
Programación Funcional
Archivos Comprimidos
Entrada Y Salida Io
Entrada Y Salida Avanzada
Entrada Y Salida Io
Archivos Temporales
Entrada Y Salida Io
Contexto With
Entrada Y Salida Io
Módulo Csv
Biblioteca Estándar
Módulo Json
Biblioteca Estándar
Módulo Datetime
Biblioteca Estándar
Módulo Math
Biblioteca Estándar
Módulo Os
Biblioteca Estándar
Módulo Re
Biblioteca Estándar
Módulo Random
Biblioteca Estándar
Módulo Time
Biblioteca Estándar
Módulo Collections
Biblioteca Estándar
Módulo Sys
Biblioteca Estándar
Módulo Statistics
Biblioteca Estándar
Módulo Pickle
Biblioteca Estándar
Módulo Pathlib
Biblioteca Estándar
Importar Módulos Y Paquetes
Paquetes Y Módulos
Crear Módulos Y Paquetes
Paquetes Y Módulos
Entornos Virtuales (Virtualenv, Venv)
Entorno Y Dependencias
Gestión De Dependencias (Pip, Requirements.txt)
Entorno Y Dependencias
Python-dotenv Y Variables De Entorno
Entorno Y Dependencias
Acceso A Datos Con Mysql, Pymongo Y Pandas
Acceso A Bases De Datos
Acceso A Mongodb Con Pymongo
Acceso A Bases De Datos
Acceso A Mysql Con Mysql Connector
Acceso A Bases De Datos
Novedades Python 3.13
Características Modernas
Operador Walrus
Características Modernas
Pattern Matching
Características Modernas
Instalación Beautiful Soup
Web Scraping
Sintaxis General De Beautiful Soup
Web Scraping
Tipos De Selectores
Web Scraping
Web Scraping De Html
Web Scraping
Web Scraping Para Ciencia De Datos
Web Scraping
Autenticación Y Acceso A Recursos Protegidos
Web Scraping
Combinación De Selenium Con Beautiful Soup
Web Scraping
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender cómo generar números aleatorios básicos y controlar la aleatoriedad con semillas.
- Aprender a seleccionar elementos aleatorios de secuencias con y sin reemplazo, incluyendo selecciones ponderadas.
- Saber mezclar secuencias de forma aleatoria y aplicar esta técnica en simulaciones y juegos.
- Conocer las principales distribuciones probabilísticas disponibles en el módulo random y su aplicación práctica.
- Aplicar funciones del módulo random en ejemplos reales como simulaciones, generación de contraseñas y modelado estadístico.