it-swarm.it

Conversione di una distribuzione uniforme in una distribuzione normale

Come posso convertire una distribuzione uniforme (come la maggior parte dei generatori di numeri casuali producono, ad esempio tra 0.0 e 1.0) in una distribuzione normale? Cosa succede se voglio una deviazione media e standard della mia scelta?

91
Terhorst

L'algoritmo Ziggurat è abbastanza efficiente per questo, sebbene la trasformazione di Box-Muller sia più facile da implementare da zero (e non da pazzi lenti).

46
Tyler

Ci sono molti metodi:

  • Do not usa Box Muller. Soprattutto se disegni molti numeri gaussiani. Box Muller produce un risultato che viene bloccato tra -6 e 6 (assumendo una doppia precisione, le cose peggiorano con i galleggianti). Ed è davvero meno efficiente di altri metodi disponibili.
  • Ziggurat va bene, ma ha bisogno di una ricerca nella tabella (e qualche tweaking specifico della piattaforma a causa di problemi di dimensioni della cache)
  • Il rapporto di uniformi è il mio preferito, solo alcune aggiunte/moltiplicazioni e un log 1/50 ° del tempo (ad esempio guarda lì ).
  • Inverting the CDF è efficiente (e trascurato, perché?), Hai implementazioni veloci di esso disponibili se cerchi google. È obbligatorio per i numeri Quasi-Random.
38
Alexandre C.

Cambiare la distribuzione di una funzione in un'altra comporta l'utilizzo dell'inverso della funzione desiderata.

In altre parole, se si mira a una funzione di probabilità specifica p(x) si ottiene la distribuzione integrandola su di essa -> d(x) = integrale (p (x)) e si usa la sua inversa : Inv (d (x)). Ora usa la funzione di probabilità casuale (che ha distribuzione uniforme) e lancia il valore del risultato attraverso la funzione Inv (d (x)). Dovresti ottenere cast casuali di valori con distribuzione in base alla funzione che hai scelto.

Questo è l'approccio matematico generico. Usandolo ora puoi scegliere qualsiasi funzione di probabilità o distribuzione che hai purché abbia una inversione inversa o una buona approssimazione inversa.

Spero che questo sia stato d'aiuto e grazie per la piccola osservazione sull'utilizzo della distribuzione e non sulla probabilità stessa.

25
Adi

Ecco un'implementazione javascript usando 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 */
}
20
user5084

Usa il teorema del limite centrale voce wikipediavoce di mathworld a tuo vantaggio.

Genera n dei numeri uniformemente distribuiti, somma loro, 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 su distribuzioni uniformi per quest'ultimo uno) 

n = 10 ti dà qualcosa di mezzo decente veloce. Se vuoi qualcosa di più della metà decente vai per la soluzione Tylers (come indicato nella voce di Wikipedia sulle normali distribuzioni )

5
jilles de wit

Sembra incredibile che potrei aggiungere qualcosa a questo dopo otto anni, ma nel 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 proprie esigenze.

1
Pepijn Schmitz

Vorrei usare Box-Muller. Due cose su questo:

  1. Finisci con due valori per iterazione
    In genere, si memorizza un valore nella cache e si restituisce l'altro. Alla prossima chiamata per un campione, si restituisce il valore memorizzato nella cache.
  2. Box-Muller dà un punteggio Z.
    Devi quindi scalare il punteggio Z per la deviazione standard e aggiungere la media per ottenere il valore completo nella distribuzione normale.
1
hughdbrown

Il modulo di 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.

Il inserimento manuale è qui

1

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 loop lenti!

1
Erik Aronesty

Q Come posso convertire una distribuzione uniforme (come la maggior parte dei generatori di numeri casuali producono, ad esempio tra 0.0 e 1.0) in una distribuzione normale?

  1. Per l'implementazione del software conosco nomi di generatori casuali di coppie che forniscono una sequenza casuale pseudo-uniforme in [0,1] (Mersenne Twister, Linear Congruate Generator). Chiamiamolo U (x)

  2. Esiste un'area matematica che ha chiamato la teoria della probabilità. Per prima cosa: se vuoi modellare r.v. con la distribuzione integrale F, allora puoi provare solo a valutare F ^ -1 (U (x)). Nella pr.teoria fu provato che tale r.v. avrà distribuzione integrale F.

  3. Il passaggio 2 può essere appli cabile per generare r.V. ~ F senza l'uso di alcun metodo di conteggio quando F ^ -1 può essere derivato analiticamente senza problemi. (ad esempio exp.distribution)

  4. Per modellare la distribuzione normale è possibile effettuare il caccolamento di y1 * cos (y2), dove y1 ~ è uniforme in [0,2pi]. e y2 è la distribuzione relei.

Q: Cosa succede se voglio una deviazione media e standard della mia scelta?

È possibile calcolare sigma * N (0,1) + m.

Si può dimostrare che tale spostamento e ridimensionamento portano a N (m, sigma)

0
bruziuz

Questa è un'implementazione di Matlab che utilizza la forma polare di Box-Muller transformation:

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 invocando histfit(randn_box_muller(10000000),100); questo è il risultato:  Box-Muller Matlab Histfit

Ovviamente è davvero inefficiente rispetto a Matlab built-in randn .

0
madx

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 di scrivere un generatore di numeri casuali per la distribuzione normale. Vedi il seguente codice come prova

n <- length(z)
t0 <- Sys.time()
z <- rnorm(n)
t1 <- Sys.time()
t1-t0
0

Io dovrei provare questo in Excel: =norminv(Rand();0;1). Questo produrrà i numeri casuali che dovrebbero essere normalmente distribuiti con la media zero e la varianza unitaria. "0" può essere fornito con qualsiasi valore, in modo che i numeri siano della 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.

0
Hippo