random - Generatori Pseudocasuali di Numeri
Scopo: Implementa parecchi tipi di generatori pseudocasuali di numeri.
Il modulo random fornisce un veloce generatore pseudocasuale di numeri basato sull'algoritmo Mersenne Twister. Originariamente sviluppato per produrre input per le simulazioni Monte Carlo, tramite Mersenne Twister vengono generati numeri con distribuzione pressochè uniformi rendendoli adatti a un grande spettro di applicazioni.
Generare Numeri Casuali
La funzione random()
ritorna il prossimo valore a virgola mobile più vicino dalla sequenza generata. Tutti i valori di ritorno sono racchiusi tra 0 <= 1.0
.
# random_random.py
import random
for i in range(5):
print('%04.3f' % random.random(), end=' ')
print()
Eseguendo il programma ripetutamente, si ottengono sequenze di numeri differenti.
$ python3 random_random.py 0.038 0.115 0.705 0.902 0.907 $ python3 random_random.py 0.919 0.640 0.473 0.384 0.223
Per generare numeri compresi in un intervallo numerico definito si utilizza uniform()
.
# random_uniform.py
import random
for i in range(5):
print('{:04.3f}'.format(random.uniform(1, 100)), end=' ')
print()
Si passano i valori minimi e massimi ed uniform()
adatta i valori restituiti da random()
usando la formula min + (max - min) * random()
.
$ python3 random_uniform.py 79.664 89.742 89.874 1.878 34.278
Utilizzare un valore seme (Seeding)
random()
produce valori diversi ogni volta che viene invocato ed ha un periodo di tempo molto largo prima che un qualsiasi numero venga ripetuto. Questo è utile per produrre valori univoci o variazioni, ma ci sono volte nelle quali è utile avere lo stesso insieme di dati a disposizione per essere elaborato in diversi modi. Una tecnica è quella di utilizzare un programma per generare valori casuali e salvarli per essere elaborati come passo separato; il che potrebbe non essere praticabile per una vasta mole di dati, quindi random comprende la funzione seed()
per inizializzare un generatore pseudocasuale in modo da produrre un insieme di valori atteso.
# random_seed.py
import random
random.seed(1)
for i in range(5):
print('{:04.3f}'.format(random.random()), end=' ')
print()
Il valore seme controlla il primo valore prodotto dalla formula utilizzata per produrre i numeri pseudocasuali, e, visto che la formula è deterministica, imposta anche l'intera sequenza prodotta dopo che il seme è cambiato. L'argomento di seed()
può essere una qualsiasi oggetto hashable. La modalità predefinita è utilizzare una sorgente di casualità specifica alla piattaforma, se disponibile. Altrimenti viene utilizzato l'orario corrente.
$ 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
Salvare lo Stato
Lo stato interno dell'algoritmo di pseudocasualità utilizzato da random()
può essere salvato e utilizzato per controllare i numeri prodotti in esecuzioni successive. Ristabilire lo stato precedente prima di continuare riduce la possibilità di valori o sequenze di valori ripetute dall'input precedente. La funzione getstate()
ritorna dati che possono essere utilizzati per reinizializzare il generatore di numeri casuali successivamente con setstate()
.
# random_state.py
import random
import os
import pickle
if os.path.exists('state.dat'):
# Ripristino dello stato precedentemente salvato
print('Trovato state.dat, inizializzazione del modulo random')
with open('state.dat', 'rb') as f:
state = pickle.load(f)
random.setstate(state)
else:
# Si usa uno stato di partenza noto
print('state.dat non trovato, utilizzo un valore seme')
random.seed(1)
# Produce valori casuali
for i in range(3):
print('{:04.3f}'.format(random.random()), end=' ')
print()
# Salva lo state per la prossima volta
with open('state.dat', 'wb') as f:
pickle.dump(random.getstate(), f)
# Produce ulteriori valori casuali
print('\nDopo il salvataggio dello stato:')
for i in range(3):
print('{:04.3f}'.format(random.random()), end=' ')
print()
I dati restituiti da getstate()
sono un dettaglio di implementazione, quindi questo esempio salva i dati in un file con pickle
e li tratta come una scatola nera. Se il file esiste quando il programma parte, carica il vecchio stato e continua. Ogni esecuzione produce alcuni numeri prima e dopo il salvataggio dello stato, per mostrare che il ripristino dello stato fa sì che il generatore produca nuovamente gli stessi valori.
$ python3 random_state.py state.dat non trovato, utilizzo un valore seme 0.134 0.847 0.764 Dopo il salvataggio dello stato: 0.255 0.495 0.449 $ python3 random_state.py Trovato state.dat, inizializzazione del modulo random 0.255 0.495 0.449 Dopo il salvataggio dello stato: 0.652 0.789 0.094
Interi Casuali
random()
genera numeri a virgola mobile. E' possibile convertirli in interi, ma è più conveniente utilizzare randint()
per generare direttamente gli interi.
# random_randint.py
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()
Gli argomenti per randint()
sono i valori dell'intervallo compresi gli estremi. I numeri possono essere negativi o positivi, ma il primo valore dovrebbe essere inferiore al secondo.
$ python3 random_randint.py [1, 100]: 22 65 41 [-5, 5]: 2 1 -2
randrange()
è una forma più generica per selezionare valori da un intervallo.
# random_randrange.py
import random
for i in range(3):
print(random.randrange(0, 101, 5), end=' ')
print()
randrange()
supporta l'argomento step, oltre ai valori di inizio e fine, il che lo rende completamente equivalente alla selezione di un valore da range(start, stop, step)
. E' tuttavia più efficiente, in quanto l'intervallo non viene in realtà costruito.
$ python3 random_randrange.py 0 50 75
Scegliere Elementi Casuali
Un uso comune per i generatori di numeri casuali è per selezionare un elemento casuale da una sequenza di valori enumerati, anche se detti valori non sono numeri. random comprende la funzione choice()
per effettuare una scelta casuale da una sequenza. Questo esempio simula il lancio di una moneta per 10.000 volte per conteggiare il numero di testa e croce ottenuti.
# random_choice.py
import random
import itertools
outcomes = {
'heads': 0,
'tails': 0,
}
sides = list(outcomes.keys())
for i in range(10000):
outcomes[random.choice(sides)] += 1
print('Testa:', outcomes['heads'])
print('Croce:', outcomes['tails'])
Ci sono solo due risultati possibili, quindi invece che usare numeri e convertirli, vengono usati con choice()
le parole "testa" e "croce". I risultati sono disposti in un dizionario che utilizza i nomi dei risultati come chiave.
$ python3 random_choice.py Testa: 4965 Croce: 5035
Permutazioni
Per la simulazione di un gioco di carte è necessario mescolare il mazzo, quindi distribuire le carte ai giocatori, senza usare la stessa carta più di una volta. Se si utilizzasse choice()
si potrebbe viceversa distribuire la stessa carta due volte; al contrario il mazzo potrebbe essere "mescolato" con shuffle()
e le singole carte verranno rimosse non appena distribuite.
# random_shuffle.py
import random
import itertools
FACE_CARDS = ('J', 'Q', 'K', 'A')
SUITS = ('\u2665', '\u2666', '\u2663', '\u2660')
def new_deck():
return [
# Si utilizzano due caratteri per il valore in modo che le stringhe
# abbiano una lunghezza consistente.
'{:>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()
# Si crea un nuovo mazzo, con le carte ordinate
deck = new_deck()
print('Mazzo Iniziale :')
show_deck(deck)
# Si mescola il mazzo per rendere l'ordine casuale
random.shuffle(deck)
print('\nMazzo Mescolato:')
show_deck(deck)
# Si distribuiscono 4 mani di 5 carte ciascuna
hands = [[], [], [], []]
for i in range(5):
for h in hands:
h.append(deck.pop())
# Si mostrano le mani
print('\nMani:')
for n, h in enumerate(hands):
print('{}:'.format(n + 1), end=' ')
for c in h:
print(c, end=' ')
print()
# Si mostra la rimanenza del mazzo
print('\nRimaste nel mazzo:')
show_deck(deck)
Le carte sono rappresentate come stringhe con il valore e il simbolo unicode che indica il seme. Le mani distribuite sono create aggiungendo una carta alla volta a ognuna delle quattro liste e rimosse dal mazzo in modo che non possano essere distribuite nuovamente.
$ python3 random_shuffle.py Mazzo Iniziale 2♥ 2♦ 2♣ 2♠ 3♥ 3♦ 3♣ 3♠ 4♥ 4♦ 4♣ 4♠ 5♥ 5♦ 5♣ 5♠ 6♥ 6♦ 6♣ 6♠ 7♥ 7♦ 7♣ 7♠ 8♥ 8♦ 8♣ 8♠ 9♥ 9♦ 9♣ 9♠ 10♥ 10♦ 10♣ 10♠ J♥ J♦ J♣ J♠ Q♥ Q♦ Q♣ Q♠ K♥ K♦ K♣ K♠ A♥ A♦ A♣ A♠ Mazzo Mescolato: K♦ 5♦ 6♦ 9♦ 10♦ 4♦ 4♣ K♠ A♦ Q♠ 10♥ K♥ 10♣ A♥ 7♥ 8♦ 9♥ 7♦ A♠ 6♥ K♣ 3♥ 2♣ 4♠ 5♣ J♥ J♦ 3♠ 4♥ 7♣ 5♥ Q♦ J♠ 6♠ 2♠ 3♣ 8♣ 7♠ 8♥ 5♠ 3♦ 10♠ A♣ Q♣ 2♦ Q♥ J♣ 8♠ 9♠ 6♣ 9♣ 2♥ Mani: 1: 2♥ 8♠ Q♣ 5♠ 3♣ 2: 9♣ J♣ A♣ 8♥ 2♠ 3: 6♣ Q♥ 10♠ 7♠ 6♠ 4: 9♠ 2♦ 3♦ 8♣ J♠ Rimaste nel mazzo: K♦ 5♦ 6♦ 9♦ 10♦ 4♦ 4♣ K♠ A♦ Q♠ 10♥ K♥ 10♣ A♥ 7♥ 8♦ 9♥ 7♦ A♠ 6♥ K♣ 3♥ 2♣ 4♠ 5♣ J♥ J♦ 3♠ 4♥ 7♣ 5♥ Q♦
Campionamento
Molte simulazioni richiedono campioni casuali da una popolazione di valori in input. La funzione sample()
genera campioni senza ripetere i valori e senza modificare la sequenza in input. Questo esempio stampa un campione casuale di parole dal dizionario di sistema (solo sistemi Mac e Unix - N.d.T.).
# random_sample.py
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)
L'algoritmo per produrre l'insieme dei risultati prende in considerazione la dimensione dell'input e del campione richiesto per produrre il risultato nel modo più efficiente possibile.
$ python3 random_sample.py lure's Wendi's better's formalism's breaking $ python3 random_sample.py sissier forgo sanely chicaneries Shelia's
Generatori Multipli Simultanei
Oltre alle funzioni a livello di modulo, random comprende anche la classe Random
per gestire lo stato interno di parecchi generatori di numeri casuali. Tutte le funzioni descritte in precedenza sono disponibili come metodi delle istanze di Random
, e ciascuna istanza può essere inizializzata e utilizzata separatamente, senza interferire con i valori ritornati da altre istanze.
# random_random_class.py
import random
import time
print('Inizializzazione predefinita:\n')
r1 = random.Random()
r2 = random.Random()
for i in range(3):
print('{:04.3f} {:04.3f}'.format(r1.random(), r2.random()))
print('\nStesso seme:\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()))
Su di un sistema con un buon sistema di semi di valori casuali, le istanze partono in uno stato univoco. Tuttavia, se questa condizione non si manifesta, è probabile che il seme utilizzato per le istanze sia l'orario corrente, di conseguenza verranno prodotti gli stessi valori.
$ python3 random_random_class.py Inizializzazione predefinita: 0.377 0.857 0.286 0.152 0.758 0.057 Stesso seme: 0.933 0.933 0.958 0.958 0.225 0.225
SystemRandom
Alcuni sistemi operativi forniscono un generatore di numeri casuali che ha accesso a ulteriori fonti di entropia che possono essere inserite nel generatore. random espone questa caratteristica tramite la classe SystemRandom
, che ha la stessa API di Random
ma utilizza os.urandom()
per generare i valori che formano le basi di tutti gli altri algoritmi.
# random_system_random.py
import random
import time
print('Inizializzazione predefinita:\n')
r1 = random.SystemRandom()
r2 = random.SystemRandom()
for i in range(3):
print('{:04.3f} {:04.3f}'.format(r1.random(), r2.random()))
print('\nStesso seme:\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()))
Le sequenze prodotte da SystemRandom
non sono riproducibili dato che la casualità proviene dal sistema, invece che da uno stato software (in effetti seed()
e setstate()
non hanno alcun effetto).
$ python3 random_system_random.py Inizializzazione predefinita: 0.249 0.057 0.914 0.401 0.682 0.903 Stesso seme: 0.823 0.142 0.456 0.970 0.027 0.730
Distribuzioni Non Uniformi
Mentre la distribuzione uniforme dei valori prodotta da random()
è utile per molti scopi, ci sono altre distribuzioni che modellano più accuratamente specifiche situazioni. Il modulo random include funzioni per produrre valori anche per quelle distribuzioni. Sono elencati di seguito, ma non trattati dettagliatamente in quanto il loro uso tende a essere specialistico e richiede esempi più complessi.
Normale
La distribuzione normale viene comunemente utilizzata per valori ininterrotti non uniformi tipo voti, altezze, larghezze ecc. La curva prodotta dalla distribuzione ha una forma particolare che viene denominata "curva a campana". random include due funzioni per generare valori con una distribuzione normale, normalvariate()
e gauss()
(leggermente più veloce) - la distribuzione normale viene anche chiamata distribuzione Gaussiana.
La funzione collegata lognormvariate()
produce valori pseudocasuali dove il logaritmo dei valori è normalmente distribuito. Queste distribuzioni sono utili per valori che sono il prodotto di diverse variabili casuali che non interagiscono.
Approssimazione
La distribuzione triangolare viene usata come distribuzione approssimata per campioni di piccole dimensioni. La curva di una distribuzione triangolare ha i due punti bassi ai valore minimo e massimo noti, il punto alto è quello (la moda) che si stima sia basato sul risultato più probabile (rispecchiato dall'argomento mode per triangular()
).
Esponenziale
expovariate()
produce una distribuzione esponenziale utile per simulare valori di arrivo o intervalli temporali per processi omogenei di Poisson tipo l'intervallo del decadimento radioattivo oppure le richieste in arrivo su di un server web.
La distribuzione paretiana o legge di potenza, corrisponde a molti fenomeni osservabili e fu resa popolare dal libro La coda lunga di Chris Anderson. La funzione paretovariate()
è utile per simulare l'allocazione di risorse agli individui (beni alle persone, richieste ai musicisti, attenzione ai blog ecc.).
Angolare
La distribuzione di Von Mises, o circolare normale (prodotta da vonmisesvariate()
) viene utilizzata per calcolare le probabilità di valori ciclici tipo angoli, giorni di calendario, orari.
Dimensioni
betavariate()
genera valori con la distribuzione Beta, comunemente utilizzata in statistiche Bayesiane e applicazioni tipo la progettazione della durata di compiti.
La distribuzione Gamma prodotta da gammavariate()
viene utilizzata per la progettazione delle dimensioni di cose tipo i tempi di attesa, precipitazioni ed errori computazionali.
La distribuzione di Weibull calcolata da weibullvariate()
viene utilizzata in analisi di guasti, progettazione industriale e previsioni del tempo. Descrive la distribuzione di dimensioni di particelle o altri oggetti discreti.
Vedere anche:
- random
- La documentazione della libreria standard per questo modulo.
- Mersenne Twister: A 623-dimensionally equidistributed uniform pseudorandom number generator
- Articolo di M. Matsumoto e T. Nishimura