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 uma média e um desvio padrão de minha escolha?
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 uma média e um desvio padrão de minha escolha?
Respostas:
O algoritmo Zigurate é bastante eficiente para isso, embora a transformação Box-Muller seja mais fácil de implementar do zero (e não muito lenta).
Existem muitos métodos:
Alterar a distribuição de qualquer função para outra envolve o uso do inverso da função desejada.
Em outras palavras, se você almeja uma função de probabilidade específica p (x), você obtém a distribuição integrando-a -> d (x) = integral (p (x)) e usa 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 por meio da função Inv (d (x)). Você deve obter valores aleatórios lançados com distribuição de acordo com a função escolhida.
Esta é a abordagem matemática genérica - ao usá-la agora você pode escolher qualquer probabilidade ou função de distribuição que você tenha, desde que tenha uma aproximação inversa ou boa.
Espero que tenha ajudado e obrigado pelo pequeno comentário sobre o uso da distribuição e não a probabilidade em si.
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 */
}
Use o teorema do limite central, entrada da Wikipédia, entrada mathworld a seu favor.
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))
(ver wikipedia sobre distribuições uniformes para esse último)
n = 10 dá a você algo meio decente rápido. Se você quiser algo mais da metade decente, vá para a solução Tylers (conforme observado no entrada Wikipédia em distribuições normais )
Eu usaria o Box-Muller. Duas coisas sobre isso:
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 há necessidade de fazer todos aqueles loops lentos!
Parece incrível que eu pudesse adicionar algo a isso depois de oito anos, mas para o caso de Java, gostaria de apontar aos leitores o método Random.nextGaussian () , que gera uma distribuição gaussiana com média 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.
O módulo de biblioteca Python padrão aleatório tem o que você deseja:
normalvariate (mu, sigma)
Distribuição normal. mu é a média e sigma é o desvio padrão.
Para o algoritmo em si, dê uma olhada na função em random.py na biblioteca Python.
Esta é minha implementação JavaScript do Algoritmo P ( Método Polar para desvios normais ) da Seção 3.4.1 do livro de 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))
}
Acho que você deve tentar isso no EXCEL: =norminv(rand();0;1)
. Isso produzirá os números aleatórios que devem ser normalmente distribuídos com a média zero e a variância unitária. "0" pode ser fornecido com qualquer valor, de modo que os números tenham a média desejada e, mudando "1", você obterá a variância igual ao quadrado de sua entrada.
Por exemplo: =norminv(rand();50;3)
resultará em números normalmente distribuídos com MEAN = 50 VARIANCE = 9.
P 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?
Para implementação de software, conheço alguns nomes de geradores aleatórios que fornecem uma sequência aleatória pseudo-uniforme em [0,1] (Mersenne Twister, Linear Congruate Generator). Vamos chamá-lo de U (x)
Existe uma área matemática que se denomina teoria da probabilidade. Primeira coisa: se você quiser modelar RV com distribuição integral F, então você pode tentar avaliar apenas F ^ -1 (U (x)). Na teoria pr. Foi provado que tal va terá distribuição integral F.
A etapa 2 pode ser aplicada para gerar rv ~ F sem o uso de quaisquer métodos de contagem quando F ^ -1 pode ser derivado analiticamente sem problemas. (por exemplo, exp.distribution)
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 uma média e um desvio padrão de minha escolha?
Você pode calcular sigma * N (0,1) + m.
Pode ser mostrado que tal mudança e escala levam a N (m, sigma)
Esta é uma implementação 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 invocar histfit(randn_box_muller(10000000),100);
este é o resultado:
Obviamente, é realmente ineficiente em comparação com o randn integrado do Matlab .
Tenho o seguinte código que talvez possa 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)]
Também é mais fácil usar a função implementada rnorm (), pois é mais rápida do que escrever um gerador de números aleatórios para a distribuição normal. Veja o código a seguir como 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;
}