Modulo random di Python
Impara il modulo random di Python: numeri casuali, scelte ponderate, mescolamento di sequenze, seed per riproducibilità e casualità crittografica.
Il modulo integrato random di Python genera numeri pseudo-casuali ed effettua selezioni casuali. È basato sull'algoritmo Mersenne Twister, che produce sequenze pseudo-casuali di alta qualità adatte a simulazioni, giochi, campionamento e test — ma non per scopi sensibili alla sicurezza come la generazione di password o chiavi crittografiche. Questo capitolo tratta:
- Generazione di float, interi e intervalli casuali
- Selezione di elementi casuali:
choice,choices,sample - Mescolamento di sequenze in-place con
shuffle - Utilizzo del seed per risultati riproducibili
- Distribuzioni continue:
uniform,gauss,triangular - Quando usare
secretsal posto dirandom
Non è necessaria alcuna installazione — import random è tutto ciò che serve.
Importare il modulo
import randomTutti gli esempi seguenti presuppongono che questa importazione sia nell'ambito. È anche possibile importare singole funzioni per evitare il prefisso random., sebbene il prefisso renda chiaro da dove proviene ogni funzione:
from random import randint, choice, shuffleGenerare float casuali
random()
random.random() restituisce un float in [0.0, 1.0) — incluso 0.0 ma mai uguale a 1.0.
import random
print(random.random()) # e.g. 0.6394267984578837
print(random.random()) # e.g. 0.025010755222666936Il valore cambia ad ogni chiamata perché il generatore avanza il suo stato interno.
uniform(a, b)
random.uniform(a, b) restituisce un float N tale che a <= N <= b:
import random
random.seed(42)
print(random.uniform(1.5, 10.5)) # 7.254841186120954
print(random.uniform(-1.0, 1.0)) # e.g. -0.82 (some float in [-1, 1])A differenza di random(), uniform permette di specificare entrambi gli estremi dell'intervallo, e il limite superiore può essere restituito.
Generare interi casuali
randint(a, b)
random.randint(a, b) restituisce un intero casuale N tale che a <= N <= b. Entrambi gli estremi sono inclusi:
import random
random.seed(42)
die = random.randint(1, 6)
print(die) # 6Questo è il modo più comune per simulare lanci di dado, estrazioni di carte o qualsiasi intervallo intero discreto.
randrange(start, stop[, step])
random.randrange rispecchia la range() di Python — stop è escluso ed è possibile specificare un passo:
import random
random.seed(42)
# Pick an even number from 0 to 98 (0, 2, 4, ..., 98)
print(random.randrange(0, 100, 2)) # e.g. a random even integer
# Pick a multiple of 5 from 0 to 95
random.seed(42)
print(random.randrange(0, 100, 5)) # 15Quando si hanno bisogno di interi casuali da un intervallo che non inizia da 0, o che richiede un passo, randrange è più espressivo di randint.
getrandbits(k)
random.getrandbits(k) restituisce un intero non negativo casuale con esattamente k bit casuali:
import random
random.seed(42)
print(random.getrandbits(8)) # 163 (an integer in 0..255)
print(random.getrandbits(16)) # an integer in 0..65535È utile quando si necessitano interi casuali più grandi di quelli ottenibili con randint, o quando si lavora a livello di bit.
Scegliere da sequenze
choice(seq)
random.choice(seq) seleziona casualmente un elemento da una sequenza non vuota. Funziona con liste, tuple e string:
import random
random.seed(42)
fruits = ['apple', 'banana', 'cherry']
print(random.choice(fruits)) # cherry
random.seed(42)
print(random.choice('ABCDE')) # A (picks one character)Se la sequenza è vuota, viene sollevato un IndexError.
choices(population, weights=None, k=1)
random.choices seleziona k elementi con reimmissione (lo stesso elemento può apparire più di una volta). È possibile influenzare la selezione con weights:
import random
colors = ['red', 'blue', 'green']
# Equal probability — each run may differ
print(random.choices(colors, k=3))
# e.g. ['blue', 'red', 'red']
# Weighted: green is 3× more likely than red, blue is 2×
random.seed(42)
print(random.choices(colors, weights=[1, 2, 3], k=4))
# ['green', 'red', 'blue', 'blue']choices restituisce sempre una lista, anche quando k=1. L'argomento weights è relativo — [1, 2, 3] produce la stessa distribuzione di [10, 20, 30].
sample(population, k)
random.sample seleziona k elementi senza reimmissione — ogni elemento può apparire solo una volta nel risultato. Non modifica la sequenza originale:
import random
random.seed(42)
# Pick 4 unique numbers from 0-9
print(random.sample(range(10), 4)) # [1, 0, 4, 9]
# Simulate a lottery: 6 unique numbers from 1-49
random.seed(7)
ticket = sorted(random.sample(range(1, 50), 6))
print(ticket) # [4, 5, 10, 21, 26, 42]Se k è maggiore della dimensione della popolazione, random.sample solleva un ValueError:
import random
try:
random.sample([1, 2, 3], 5)
except ValueError as e:
print(e) # Sample larger than population or is negativechoices vs sample a colpo d'occhio:
choices | sample | |
|---|---|---|
| Reimmissione | Sì | No |
| Risultati duplicati | Possibile | Mai |
| Modifica l'originale | No | No |
| Supporta i pesi | Sì | No (usa choices) |
Mescolare sequenze
random.shuffle(seq) mescola la lista in-place e restituisce None. La lista originale viene modificata:
import random
random.seed(42)
deck = [1, 2, 3, 4, 5]
random.shuffle(deck)
print(deck) # [4, 2, 3, 5, 1] (original list is modified)Se si ha bisogno di una copia mescolata senza modificare l'originale, usare random.sample:
import random
random.seed(42)
original = [1, 2, 3, 4, 5]
shuffled = random.sample(original, len(original))
print(original) # [1, 2, 3, 4, 5] (unchanged)
print(shuffled) # [4, 2, 3, 5, 1]Seed per risultati riproducibili
Per impostazione predefinita, il modulo random si inizializza dal tempo di sistema (o os.urandom()), quindi ogni esecuzione produce valori diversi. Chiamare random.seed() con un valore fisso blocca la sequenza in modo da ottenere output identici ogni volta — essenziale per il debugging, i test unitari e le simulazioni riproducibili:
import random
random.seed(42)
print(random.random()) # 0.6394267984578837
print(random.randint(1, 10)) # 1
print(random.choice(['a', 'b', 'c'])) # c
# Reset to the same seed — identical sequence
random.seed(42)
print(random.random()) # 0.6394267984578837 (same as before)
print(random.randint(1, 10)) # 1
print(random.choice(['a', 'b', 'c'])) # crandom.seed() accetta un intero, una string, un float, bytes o None (che re-inizializza dal sistema). Il seeding è un'operazione globale — influisce su tutte le chiamate successive nello stesso programma, quindi impostare il seed una sola volta all'inizio di un blocco riproducibile.
Generare dati di test riproducibili
import random
random.seed(999)
test_scores = [round(random.uniform(0, 100), 2) for _ in range(5)]
print(test_scores) # [78.13, 8.01, 87.25, 57.38, 49.06]Ogni esecuzione con seed(999) produce esattamente la stessa lista, rendendola utile per test unitari che verificano valori specifici.
Distribuzioni continue
Il modulo random include diverse distribuzioni di probabilità continue, utili nelle simulazioni e nella modellazione statistica.
uniform(a, b)
Già trattata in precedenza — una distribuzione piatta in cui ogni valore in [a, b] ha la stessa probabilità.
triangular(low, high, mode)
Restituisce un float da una distribuzione triangolare in cui mode è il valore più comune:
import random
random.seed(42)
# Most values cluster near 5, between 0 and 10
t = random.triangular(0, 10, 5)
print(t) # 5.753983033817951Utile quando si conoscono il valore minimo, massimo e più probabile ma non la distribuzione esatta — comune nella stima di progetti e nella modellazione del rischio.
gauss(mu, sigma)
Restituisce un float da una distribuzione gaussiana (normale) con media mu e deviazione standard sigma:
import random
random.seed(42)
# Heights in cm: mean 170, std 10
heights = [round(random.gauss(170, 10), 1) for _ in range(5)]
print(heights) # [168.6, 168.3, 168.9, 177.0, 168.7]random.normalvariate(mu, sigma) è equivalente ma thread-safe; usare normalvariate nei programmi multi-thread.
expovariate(lambd)
Restituisce un float da una distribuzione esponenziale, dove lambd è 1 / media. Utile per modellare il tempo tra eventi (ad esempio arrivi di clienti, pacchetti di rete):
import random
random.seed(42)
# Mean inter-arrival time = 5 minutes; lambd = 1/5
wait = random.expovariate(1 / 5)
print(round(wait, 2)) # 5.1 (minutes until next arrival)Esempi pratici
Simulare 60 lanci di dado
import random
random.seed(42)
counts = {face: 0 for face in range(1, 7)}
for _ in range(60):
counts[random.randint(1, 6)] += 1
print(counts)
# {1: 15, 2: 10, 3: 8, 4: 7, 5: 10, 6: 10}Con più lanci la distribuzione si avvicina alla probabilità teorica di 1/6 per ogni faccia.
Scegliere una squadra casuale
import random
random.seed(42)
players = ['Alice', 'Bob', 'Carol', 'Dave', 'Eve', 'Frank']
random.shuffle(players)
team_a = players[:3]
team_b = players[3:]
print('Team A:', team_a)
print('Team B:', team_b)Password casuale semplice
Per contesti non di sicurezza (demo, token temporanei), random.choices funziona bene:
import random
import string
random.seed(42)
chars = string.ascii_letters + string.digits
password = ''.join(random.choices(chars, k=12))
print(password) # NbrnTP3fAbnFPer password utente o token reali, usare invece il modulo secrets (vedi sotto).
Quando usare secrets invece di random
Il modulo random non è crittograficamente sicuro. Un attaccante che osserva abbastanza output può predire i valori futuri. Per qualsiasi elemento sensibile alla sicurezza, usare il modulo secrets (Python 3.6+), che attinge dalla sorgente casuale crittografica del sistema operativo:
import secrets
# Cryptographically secure random integer in [0, 100)
print(secrets.randbelow(100))
# Secure random token for a password-reset link (URL-safe base64)
token = secrets.token_urlsafe(32)
print(token) # e.g. 'gIg9XzExxFkHLAH9JCK8Zxb1aBEFZ...'
# Secure random hex string for an API key
api_key = secrets.token_hex(16)
print(api_key) # e.g. 'a3f1c2d4e5b6...'Regola pratica: se si generano token, ID di sessione, password o qualsiasi valore che un avversario non deve poter indovinare, usare secrets. Usare random per tutto il resto (giochi, simulazioni, fixture di test, campionamento).
Riferimento rapido
| Funzione | Descrizione | Restituisce |
|---|---|---|
random.random() | Float in [0.0, 1.0) | float |
random.uniform(a, b) | Float in [a, b] | float |
random.randint(a, b) | Intero in [a, b] (entrambi inclusi) | int |
random.randrange(start, stop[, step]) | Intero da range(start, stop, step) | int |
random.getrandbits(k) | Intero non negativo con k bit casuali | int |
random.choice(seq) | Un elemento casuale | tipo dell'elemento |
random.choices(pop, weights, k) | k elementi con reimmissione | list |
random.sample(pop, k) | k elementi senza reimmissione | list |
random.shuffle(seq) | Mescola la lista in-place | None |
random.seed(a) | Imposta il seed casuale | None |
random.gauss(mu, sigma) | Variato gaussiano (normale) | float |
random.normalvariate(mu, sigma) | Variato normale (thread-safe) | float |
random.triangular(low, high, mode) | Variato da distribuzione triangolare | float |
random.expovariate(lambd) | Variato da distribuzione esponenziale | float |
Capitoli correlati
- Python Math — funzioni matematiche e costanti
- Python Modules — come importare e organizzare i moduli
- Python Numbers — tipi intero, float e complesso
- Python Lists — lavorare con sequenze ordinate
- Python For Loops — iterare per generare dati casuali
- Python Try Except — gestire errori come
ValueErrordasample