Come posso convertire una distribuzione uniforme (come produce la maggior parte dei generatori di numeri casuali, ad esempio tra 0,0 e 1,0) in una distribuzione normale? E se desidero una deviazione media e standard di mia scelta?
Come posso convertire una distribuzione uniforme (come produce la maggior parte dei generatori di numeri casuali, ad esempio tra 0,0 e 1,0) in una distribuzione normale? E se desidero una deviazione media e standard di mia scelta?
Risposte:
L' algoritmo Ziggurat è abbastanza efficiente per questo, sebbene la trasformazione Box-Muller sia più facile da implementare da zero (e non lenta folle).
Esistono molti metodi:
Modificare la distribuzione di qualsiasi funzione in un'altra implica l'utilizzo dell'inverso della funzione desiderata.
In altre parole, se miri a una specifica funzione di probabilità p (x) ottieni la distribuzione integrando su di essa -> d (x) = integrale (p (x)) e usa il suo inverso: Inv (d (x)) . Ora usa la funzione di probabilità casuale (che ha una distribuzione uniforme) e lancia il valore del risultato attraverso la funzione Inv (d (x)). Dovresti ottenere valori casuali espressi con distribuzione in base alla funzione che hai scelto.
Questo è l'approccio matematico generico: utilizzandolo ora puoi scegliere qualsiasi funzione di probabilità o di distribuzione a condizione che abbia un'approssimazione inversa o inversa buona.
Spero che questo sia stato d'aiuto e grazie per la piccola osservazione sull'uso della distribuzione e non della probabilità stessa.
Ecco un'implementazione javascript che utilizza la forma polare della trasformazione Box-Muller.
/*
* Returns member of set with a given mean and standard deviation
* mean: mean
* standard deviation: std_dev
*/
function createMemberInNormalDistribution(mean,std_dev){
return mean + (gaussRandom()*std_dev);
}
/*
* Returns random number in normal distribution centering on 0.
* ~95% of numbers returned should fall between -2 and 2
* ie within two standard deviations
*/
function gaussRandom() {
var u = 2*Math.random()-1;
var v = 2*Math.random()-1;
var r = u*u + v*v;
/*if outside interval [0,1] start over*/
if(r == 0 || r >= 1) return gaussRandom();
var c = Math.sqrt(-2*Math.log(r)/r);
return u*c;
/* todo: optimize this algorithm by caching (v*c)
* and returning next time gaussRandom() is called.
* left out for simplicity */
}
Usa la voce mathworld del teorema del limite centrale a tuo vantaggio.
Genera n dei numeri uniformemente distribuiti, sommali, sottrai n * 0,5 e hai l'output di una distribuzione approssimativamente normale con media uguale a 0 e varianza uguale a (1/12) * (1/sqrt(N))
(vedi wikipedia sulle distribuzioni uniformi per quest'ultima)
n = 10 ti dà qualcosa di abbastanza decente velocemente. Se vuoi qualcosa di più della metà decente, scegli la soluzione tylers (come indicato nella voce di wikipedia sulle distribuzioni normali )
Userei Box-Muller. Due cose su questo:
Dove R1, R2 sono numeri uniformi casuali:
DISTRIBUZIONE NORMALE, con SD di 1: sqrt (-2 * log (R1)) * cos (2 * pi * R2)
Questo è esatto ... non c'è bisogno di fare tutti quei cicli lenti!
Sembra incredibile che io possa aggiungere qualcosa a questo dopo otto anni, ma per il caso di Java vorrei indirizzare i lettori al metodo Random.nextGaussian () , che genera una distribuzione gaussiana con media 0.0 e deviazione standard 1.0 per te.
Una semplice aggiunta e / o moltiplicazione cambierà la media e la deviazione standard in base alle tue esigenze.
Il modulo della libreria Python standard random ha quello che vuoi:
normalvariate (mu, sigma)
Distribuzione normale. mu è la media e sigma è la deviazione standard.
Per l'algoritmo stesso, dai un'occhiata alla funzione in random.py nella libreria Python.
Questa è la mia implementazione JavaScript dell'algoritmo P ( metodo Polar per deviazioni normali ) dalla sezione 3.4.1 del libro di Donald Knuth The Art of Computer Programming :
function normal_random(mean,stddev)
{
var V1
var V2
var S
do{
var U1 = Math.random() // return uniform distributed in [0,1[
var U2 = Math.random()
V1 = 2*U1-1
V2 = 2*U2-1
S = V1*V1+V2*V2
}while(S >= 1)
if(S===0) return 0
return mean+stddev*(V1*Math.sqrt(-2*Math.log(S)/S))
}
Penso che dovresti provare questo in EXCEL: =norminv(rand();0;1)
. Questo produrrà i numeri casuali che dovrebbero essere normalmente distribuiti con la media zero e unirà la varianza. "0" può essere fornito con qualsiasi valore, in modo che i numeri avranno la media desiderata, e cambiando "1", otterrai la varianza uguale al quadrato del tuo input.
Ad esempio: =norminv(rand();50;3)
restituirà i numeri normalmente distribuiti con MEAN = 50 VARIANCE = 9.
D Come posso convertire una distribuzione uniforme (come produce la maggior parte dei generatori di numeri casuali, ad esempio tra 0,0 e 1,0) in una distribuzione normale?
Per l'implementazione del software conosco un paio di nomi di generatori casuali che danno una sequenza casuale pseudo uniforme in [0,1] (Mersenne Twister, Linear Congruate Generator). Chiamiamolo U (x)
Esiste un'area matematica chiamata teoria della probabilità. Prima cosa: se vuoi modellare rv con distribuzione integrale F, puoi provare a valutare F ^ -1 (U (x)). Nella teoria pr. È stato dimostrato che tale rv avrà distribuzione integrale F.
Il passaggio 2 può essere applicabile per generare rv ~ F senza l'utilizzo di alcun metodo di conteggio quando F ^ -1 può essere derivato analiticamente senza problemi. (ad es. distribuzione exp)
Per modellare la distribuzione normale si può cacculare y1 * cos (y2), dove y1 ~ è uniforme in [0,2pi]. e y2 è la distribuzione relei.
D: Cosa succede se desidero una media e una deviazione standard di mia scelta?
Puoi calcolare sigma * N (0,1) + m.
Si può dimostrare che tale spostamento e ridimensionamento portano a N (m, sigma)
Questa è un'implementazione Matlab che utilizza la forma polare del Box-Muller trasformazione :
Funzione randn_box_muller.m
:
function [values] = randn_box_muller(n, mean, std_dev)
if nargin == 1
mean = 0;
std_dev = 1;
end
r = gaussRandomN(n);
values = r.*std_dev - mean;
end
function [values] = gaussRandomN(n)
[u, v, r] = gaussRandomNValid(n);
c = sqrt(-2*log(r)./r);
values = u.*c;
end
function [u, v, r] = gaussRandomNValid(n)
r = zeros(n, 1);
u = zeros(n, 1);
v = zeros(n, 1);
filter = r==0 | r>=1;
% if outside interval [0,1] start over
while n ~= 0
u(filter) = 2*rand(n, 1)-1;
v(filter) = 2*rand(n, 1)-1;
r(filter) = u(filter).*u(filter) + v(filter).*v(filter);
filter = r==0 | r>=1;
n = size(r(filter),1);
end
end
E invocare histfit(randn_box_muller(10000000),100);
questo è il risultato:
Ovviamente è davvero inefficiente rispetto al randn integrato di Matlab .
Ho il seguente codice che forse potrebbe aiutare:
set.seed(123)
n <- 1000
u <- runif(n) #creates U
x <- -log(u)
y <- runif(n, max=u*sqrt((2*exp(1))/pi)) #create Y
z <- ifelse (y < dnorm(x)/2, -x, NA)
z <- ifelse ((y > dnorm(x)/2) & (y < dnorm(x)), x, z)
z <- z[!is.na(z)]
È anche più facile usare la funzione implementata rnorm () poiché è più veloce che scrivere un generatore di numeri casuali per la distribuzione normale. Vedere il codice seguente come prova
n <- length(z)
t0 <- Sys.time()
z <- rnorm(n)
t1 <- Sys.time()
t1-t0
function distRandom(){
do{
x=random(DISTRIBUTION_DOMAIN);
}while(random(DISTRIBUTION_RANGE)>=distributionFunction(x));
return x;
}