it-swarm-pt.tech

Convertendo uma distribuição uniforme em uma distribuição normal

Como posso converter uma distribuição uniforme (como a maioria dos geradores de números aleatórios produzem, por exemplo, entre 0,0 e 1,0) em uma distribuição normal? E se eu quiser um desvio médio e padrão da minha escolha?

91
Terhorst

O algoritmo Ziggurat é bastante eficiente para isso, embora a transformação Box-Muller seja mais fácil de implementar do zero (e não lenta).

46
Tyler

Existem muitos métodos:

  • Do not use o Box Muller. Especialmente se você desenhar muitos números gaussianos. Box Muller produz um resultado que é fixado entre -6 e 6 (assumindo dupla precisão. As coisas pioram com os floats). E é realmente menos eficiente que outros métodos disponíveis.
  • Ziggurat é bom, mas precisa de uma pesquisa de tabela (e alguns ajustes específicos da plataforma devido a problemas de tamanho de cache)
  • Razão-de-uniformes é o meu favorito, apenas algumas adições/multiplicações e um log 1/50th do tempo (por exemplo, olha lá ).
  • Invertendo o CDF é eficiente (e esquecido, por quê?), Você tem implementações rápidas dele disponíveis se você pesquisar no google. É obrigatório para números quase aleatórios.
38
Alexandre C.

Alterar a distribuição de qualquer função para outra envolve usar o inverso da função desejada.

Em outras palavras, se você apontar para uma função de probabilidade específica p(x), obterá a distribuição integrando-a -> d(x) = integral (p (x)) e usará seu inverso : Inv (d (x)). Agora use a função de probabilidade aleatória (que tem distribuição uniforme) e lance o valor do resultado através da função Inv (d (x)). Você deve obter valores aleatórios com a distribuição de acordo com a função escolhida.

Esta é a abordagem matemática genérica - usando-a você pode escolher qualquer probabilidade ou função de distribuição que você tenha, desde que tenha uma aproximação inversa ou boa inversa.

Espero que isso ajudou e obrigado pela pequena observação sobre o uso da distribuição e não a probabilidade em si.

25
Adi

Aqui está uma implementação de javascript usando a forma polar da transformação 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

Use o teorema do limite central entrada wikipediamathworld entry para sua vantagem.

Gere n dos números uniformemente distribuídos, some-os, subtraia n * 0,5 e você terá a saída de uma distribuição aproximadamente normal com média igual a 0 e variância igual a (1/12) * (1/sqrt(N)) (veja wikipedia em distribuições uniformes para essa última) 

n = 10 dá-lhe algo meio decente rápido. Se você quiser algo mais do que metade decente, vá para solução de tylers (como observado na entrada wikipedia em distribuições normais )

5
jilles de wit

Parece incrível que eu possa acrescentar algo a isso depois de oito anos, mas para o caso de Java eu ​​gostaria de apontar os leitores para o método Random.nextGaussian () , que gera uma distribuição gaussiana com média de 0,0 e desvio padrão 1,0 para você.

Uma simples adição e/ou multiplicação mudará a média e o desvio padrão de acordo com suas necessidades.

1
Pepijn Schmitz

Eu usaria o Box-Muller. Duas coisas sobre isso:

  1. Você acaba com dois valores por iteração
    Normalmente, você armazena em cache um valor e retorna o outro. Na próxima chamada para uma amostra, você retorna o valor em cache.
  2. Box-Muller dá um Z-score
    Você tem que escalar a pontuação Z pelo desvio padrão e adicionar a média para obter o valor total na distribuição normal.
1
hughdbrown

O módulo padrão da biblioteca Python random tem o que você deseja:

normalvariate (mu, sigma)
Distribuição normal. mu é a média e sigma é o desvio padrão.

Para o próprio algoritmo, dê uma olhada na função em random.py na biblioteca Python.

A entrada manual está aqui

1
Brent.Longborough

Onde R1, R2 são números uniformes aleatórios:

DISTRIBUIÇÃO NORMAL, com SD de 1: sqrt (-2 * log (R1)) * cos (2 * pi * R2)

Isso é exato ... não precisa fazer todos esses loops lentos!

1
Erik Aronesty

P Como posso converter uma distribuição uniforme (como a maioria dos geradores de números aleatórios produz, por exemplo, entre 0,0 e 1,0) em uma distribuição normal?

  1. Para a implementação de software, conheço alguns nomes aleatórios de geradores que fornecem uma sequência aleatória pseudo-uniforme em [0,1] (Mersenne Twister, Linear Congruate Generator). Vamos chamá-lo U (x)

  2. Existe uma área matemática que denominou teoria da probabilidad. Primeira coisa: Se você quer modelar r.v. com a distribuição integral F, você pode tentar avaliar F ^ -1 (U (x)). Em pr.theory foi provado que tal r.v. terá distribuição integral F.

  3. O passo 2 pode ser appliable para gerar r.v. ~ F sem uso de qualquer método de contagem quando F ^ -1 pode ser derivado analiticamente sem problemas. (por exemplo, exp.distribution)

  4. Para modelar a distribuição normal, você pode calcular y1 * cos (y2), onde y1 ~ é uniforme em [0,2pi]. e y2 é a distribuição relei.

P: E se eu quiser um desvio médio e padrão da minha escolha?

Você pode calcular sigma * N (0,1) + m.

Pode-se mostrar que tal deslocamento e dimensionamento levam a N (m, sigma)

0
bruziuz

Esta é uma implementação do Matlab usando a forma polar da transformação Box-Muller :

Função 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); este é o resultado: Box-Muller Matlab Histfit

Obviamente, é realmente ineficiente em comparação com o Matlab built-in randn .

0
madx

Eu tenho o seguinte código que talvez pudesse ajudar:

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)]
0
great_minds_think_alike

Também é mais fácil usar a função implementada rnorm (), já que é mais rápido do que escrever um gerador de números aleatórios para a distribuição normal. Veja o seguinte código como prova

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

Eu acho que você deveria tentar isso no Excel: =norminv(Rand();0;1). Isto irá produzir os números aleatórios que devem ser normalmente distribuídos com a média zero e unir a variância. "0" pode ser fornecido com qualquer valor, de modo que os números serão da média desejada, e alterando "1", você obterá a variância igual ao quadrado da sua entrada.

Por exemplo: =norminv(Rand();50;3) resultará nos números normalmente distribuídos com MEAN = 50 VARIANCE = 9.

0
Hippo