Molti programmi hanno bisogno di valori (apparentemente) casuali per simulare eventi del mondo reale.
Esempi comuni e familiari includono:
('H', 'T').Generare valori veramente casuali con un computer è una sfida tecnica sorprendentemente difficile, quindi potresti vedere questi risultati indicati come «pseudocasuali».
Importante: questo concetto non copre i numeri casuali crittograficamente sicuri, che sono una sfida molto più difficile.
Tuttavia, librerie ben progettate come il modulo Random della libreria standard di Julia sono veloci, flessibili e danno risultati ampiamente sufficienti per la maggior parte delle applicazioni di modellazione, simulazione e giochi.
Julia suddivide la funzionalità casuale in più punti:
Base, che sono sempre disponibili.Random.Random fa parte della libreria standard ed è probabilmente già installato, ma dovrai aggiungere using Random all'inizio del programma per portare i suoi contenuti nel namespace.
rand()
Quello che fa questa funzione dipende dagli argomenti che le passi. Ci sono molte opzioni.
Senza argomenti, genera un numero in virgola mobile tra 0 (incluso) e 1.
Questa è una distribuzione uniform in cui tutti i valori sono ugualmente probabili, come discusso nella sezione Lavorare con le distribuzioni, più sotto.
Un singolo argomento intero genera un vettore di quella lunghezza.
julia> rand()
0.10261774967264703
julia> rand(5)
5-element Vector{Float64}:
0.24134501977563894
0.5664193284851202
0.9804412082089355
0.6229551330613335
0.47589221741904664
Per un intervallo diverso, basta spostare e riscalare il risultato in modo appropriato.
L'esempio sotto usa il broadcasting per la sottrazione, trattato nel concetto Operazioni sui vettori.
Il .- applica semplicemente questa aritmetica a ogni elemento del vettore.
# numbers between -1.0 and +1.0
julia> (rand(5) .- 0.5) * 2
5-element Vector{Float64}:
-0.5303906759076336
0.9635682226775855
-0.048823697086981754
0.465842804648374
0.9880834344780736
Con un tipo come unico argomento, rand userà typemin e typemax come limiti.
Probabilmente non è quello che vuoi!
Per numeri interi casuali, possiamo fornire un intervallo, più facoltativamente quanti valori generare.
julia> rand(Int64)
-9159538335234594326 # not very useful
julia> rand(1:10, 5)
5-element Vector{Int64}:
1
1
1
4
7
Nell'esempio rand(1:10, 5) sopra, nota che ci sono valori ripetuti (per coincidenza), perché ogni scelta è indipendente.
Questo è il «campionamento con reinserimento», discusso più in dettaglio sotto.
Per valori in virgola mobile in un intervallo, di solito dovrai specificare una dimensione del passo. Altrimenti il passo sarà 1.0 per impostazione predefinita, il che è raramente utile.
julia> rand(2.4:0.01:3.2, 4)
4-element Vector{Float64}:
3.19
2.53
3.14
3.13
In alternativa, fornisci un array o una tupla, e rand restituirà una voce casuale:
julia> rand([4, 9, 16, 25])
16
# coin flip
julia> rand(['H', 'T'])
'H': ASCII/Unicode U+0048 (category Lu: Letter, uppercase)
# mixed types in tuple
julia> rand( (1, 3.2, "name"), 2 )
2-element Vector{Any}:
1
"name"
Immagina di avere un sacchetto contenente 3 palline rosse e 4 palline verdi, e di estrarre a caso una pallina dal sacchetto. Per ottenere una seconda pallina, ci sono due possibilità:
Lo scenario 1 è con reinserimento, lo scenario 2 è senza, e danno risultati diversi.
Per simulare il campionamento senza reinserimento in Julia, ci sono un paio di opzioni.
Il più semplice (e su Exercism l'unica opzione) è usare Random.shuffle() per mettere le voci in ordine casuale, poi usare i primi n elementi.
Questo va bene per problemi piccoli ma potrebbe non scalare bene su collezioni grandi: shuffle deve generare l'array completo, anche se ne vuoi solo una piccola frazione.
Per fare il campionamento con reinserimento «come si deve», installa il pacchetto StatsBase.jl.
Questo fornisce la funzione sample() con una gamma completa di opzioni.
Possiamo ragionevolmente sperare che funzionalità simili vengano aggiunte a Random in una versione futura, per renderle parte della libreria standard (gli esempi di codice in questo documento sono stati testati con Julia 1.11).
Finora ci siamo concentrati su casi in cui tutti gli esiti sono ugualmente probabili.
Ad esempio, rand(1:100) ha la stessa probabilità di dare qualsiasi numero intero da 1 a 100.
Molte situazioni del mondo reale sono molto meno semplici di così.
Di conseguenza, gli statistici hanno creato un'ampia varietà di distributions per descrivere matematicamente i risultati del «mondo reale».
La funzione rand() descritta sopra si usa quando tutte le probabilità sono uguali.
Questa si chiama distribuzione [uniform][uniform-distribution].
Chiamata anche distribuzione «normale» o curva «a campana», è un modo molto comune per descrivere l'imprecisione nei valori misurati.
Ad esempio, supponi che la fabbrica in cui lavori abbia appena acquistato 10.000 bulloni che dovrebbero essere identici.
Vuoi configurare il robot della fabbrica per maneggiarli, quindi pesi un campione di 100 e scopri che hanno un peso medio (o mean) di 4,731 g.
È estremamente improbabile che questo significhi che pesano tutti esattamente 4,731 g.
Forse scopri che i valori vanno da 4,627 a 4,794 g ma si concentrano intorno a 4,731 g.
Questa è la Gaussian distribution, per la quale le probabilità raggiungono il picco in corrispondenza della media e diminuiscono simmetricamente su entrambi i lati (da qui «a campana»).
Per simularla nel software, abbiamo bisogno di un modo per specificare l'ampiezza della curva (tipicamente, i bulloni costosi si concentreranno più strettamente intorno alla media rispetto a quelli economici!).
Per convenzione, questo si fa con la standard deviation: valori piccoli per una curva stretta e appuntita, grandi per una curva bassa e larga.
I matematici adorano le lettere greche, quindi usiamo μ ('mu') per rappresentare la media e σ ('sigma') per rappresentare la deviazione standard.
Quindi, se leggi che «il 95% dei valori è entro 2σ da μ» o che «il bosone di Higgs è stato rilevato con una confidenza di 5 sigma», questi commenti riguardano la deviazione standard.
Ci sarà altro da dire su questo nel concetto Statistics.
randn()
Abbreviazione di «random normal», è simile alla variante in virgola mobile di rand() tranne per il fatto che i valori sono distribuiti come una gaussiana con media 0 e deviazione standard 1.
Anche in questo caso, potresti voler riscalare l'output grezzo di randn per la deviazione standard e spostarlo per la media.
L'esempio sotto converte a media 30 e deviazione standard 5.
julia> raw = randn(5)
5-element Vector{Float64}:
3.0762588867281475
1.5101100620253902
-0.5914858221637778
0.684175554069735
-0.8416433926114673
julia> raw * 5 .+ 30
5-element Vector{Float64}:
45.38129443364074
37.55055031012695
27.04257088918111
33.420877770348675
25.791783036942665
È difficile capire guardando l'output che l'output grezzo si concentra più vicino a zero rispetto a una distribuzione uniforme. Se ne dubiti, generane 1000 o più e tracciali per renderlo più evidente.
Random
Questo modulo contiene il livello successivo di funzionalità, omesso da Base per contribuire a ridurre al minimo le dimensioni della configurazione predefinita di Julia.
Random integra rand e randn in Base con versioni mutanti, rand! e randn!.
Un'aggiunta utile è randstring, che genera una stringa di lunghezza data.
Per impostazione predefinita, usa lettere maiuscole e minuscole più le cifre da 0 a 9, ma è possibile specificare altre scelte.
julia> using Random
julia> randstring(20)
"BoJnIxrS33pJiWggXZQV"
Inoltre, c'è una funzione bitrand per generare un BitArray casuale di lunghezza specificata.
julia> bitrand(5)
julia> bitrand(5)
5-element BitVector:
1
1
0
0
1
Per mescolare casualmente le voci in un Vector abbiamo shuffle; e anche shuffle! per mutare il vettore di input sul posto.
julia> v = ['A', '1', '2', 'J', 'Q', 'K'];
julia> shuffle(v)
6-element Vector{Char}:
'K': ASCII/Unicode U+004B (category Lu: Letter, uppercase)
'1': ASCII/Unicode U+0031 (category Nd: Number, decimal digit)
'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase)
'J': ASCII/Unicode U+004A (category Lu: Letter, uppercase)
'2': ASCII/Unicode U+0032 (category Nd: Number, decimal digit)
'Q': ASCII/Unicode U+0051 (category Lu: Letter, uppercase)
# shuffles are random:
julia> shuffle(v)
6-element Vector{Char}:
'2': ASCII/Unicode U+0032 (category Nd: Number, decimal digit)
'K': ASCII/Unicode U+004B (category Lu: Letter, uppercase)
'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase)
'Q': ASCII/Unicode U+0051 (category Lu: Letter, uppercase)
'J': ASCII/Unicode U+004A (category Lu: Letter, uppercase)
'1': ASCII/Unicode U+0031 (category Nd: Number, decimal digit)
A volte è utile avere invece gli indici mescolati.
Per questo, usa randperm(n) dove n è la lunghezza della sequenza.
julia> randperm(6)
6-element Vector{Int64}:
6
2
4
1
3
5
In effetti, l'esempio sopra dà gli stessi risultati di shuffle(1:6).
Funzioni correlate includono randsubseq per estrarre voci con probabilità fissa, e randcycle per permutazioni cicliche.
Richiedono una conoscenza specializzata, quindi controlla la documentazione se ti interessano.
Diversi algoritmi di generazione di numeri casuali (RNG) sono integrati in Random come standard, e chiunque abbia le competenze matematiche appropriate può aggiungerne altri.
Queste cose vanno ben oltre lo scopo di questo documento!
Una ragione più comune per lavorare con gli RNG è specificare un seed, che ha l'effetto di rendere riproducibile la sequenza di output «casuali» da un'esecuzione all'altra.
Tale riproducibilità non è appropriata nel codice di produzione, ma può aiutare con i test e il debug.
Al di fuori di Exercism, ci sono molti pacchetti installabili relativi a casualità, probabilità e statistica.
Per ulteriori informazioni, vedi il concetto Statistics.
StatsBase.jl
La maggior parte delle funzioni di StatsBase è piuttosto tecnica e non pertinente a questo documento.
L'eccezione è StatsBase.sample, che fornisce un'implementazione completa del campionamento con o senza reinserimento (vedi una sezione precedente, sopra).
Ci sono anche funzioni per il campionamento ponderato (non uniforme).
Distributions.jl
Le distribuzioni uniform e normal (o gaussiana) sono state descritte sopra.
Il modulo Random contiene anche randexp per campionare dalla distribuzione esponenziale, che è correlata alla (molto comune) distribuzione di Poisson.
Per una gamma molto più ampia di opzioni, c'è il pacchetto Distributions.jl per chi ha una preparazione adeguata in statistica.