web-dev-qa-db-fra.com

Conversion d'une distribution uniforme en une distribution normale

Comment puis-je convertir une distribution uniforme (comme le produisent la plupart des générateurs de nombres aléatoires, par exemple entre 0,0 et 1,0) en une distribution normale? Et si je veux un écart moyen et standard de mon choix?

91
Terhorst

L'algorithme Ziggurat est assez efficace pour cela, bien que la transformation Box-Muller soit plus facile à implémenter à partir de zéro (et pas trop lente).

46
Tyler

Il y a beaucoup de méthodes:

  • Ne not utilise Box Muller. Surtout si vous dessinez beaucoup de nombres gaussiens. Box Muller donne un résultat serré entre -6 et 6 (en supposant une double précision. La situation s'aggrave avec les flotteurs.). Et c'est vraiment moins efficace que les autres méthodes disponibles.
  • La ziggourat convient, mais nécessite une consultation de table (et quelques ajustements spécifiques à la plate-forme en raison de problèmes de taille de cache).
  • Le rapport des uniformes est mon préféré, seulement quelques additions/multiplications et un log 1/50ème du temps (par exemple. regardez ici ).
  • Inverser le CDF est efficace (et négligé, pourquoi?), Vous en avez une mise en oeuvre rapide si vous effectuez une recherche sur Google. Il est obligatoire pour les nombres quasi-aléatoires.
38
Alexandre C.

Changer la distribution d'une fonction à une autre implique l'utilisation de l'inverse de la fonction souhaitée.

En d’autres termes, si vous visez une fonction de probabilité spécifique p(x), vous obtenez la distribution en intégrant dessus -> d(x) = intégrale (p (x)) et utilisez son inverse : Inv (d (x)). Maintenant, utilisez la fonction de probabilité aléatoire (qui a une distribution uniforme) et convertissez la valeur du résultat par la fonction Inv (d (x)). Vous devriez obtenir des valeurs aléatoires avec une distribution en fonction de la fonction que vous avez choisie.

C'est l'approche mathématique générique - en l'utilisant, vous pouvez maintenant choisir n'importe quelle probabilité ou fonction de distribution que vous avez tant qu'elle a une approximation inverse ou bonne.

J'espère que cela vous a aidé et merci pour la petite remarque sur l'utilisation de la distribution et non de la probabilité elle-même.

25
Adi

Voici une implémentation javascript utilisant la forme polaire de la transformation 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

Utilisez le théorème central limite entrée wikipediaentrée mathworld à votre avantage.

Générez n des nombres uniformément répartis, additionnez-les, soustrayez n * 0.5 et vous obtenez le résultat d’une distribution approximativement normale avec une moyenne égale à 0 et une variance égale à (1/12) * (1/sqrt(N)) (voir wikipedia sur les distributions uniformes pour ce dernier) 

n = 10 vous donne quelque chose de rapide moitié décent. Si vous voulez quelque chose de plus que la moitié décent, optez pour la solution tylers (comme indiqué dans l'entrée de wikipedia sur les distributions normales )

5
jilles de wit

Il semble incroyable que je puisse ajouter quelque chose à cela après huit ans, mais pour Java, je voudrais indiquer aux lecteurs la méthode Random.nextGaussian () , qui génère une distribution gaussienne avec une moyenne de 0.0 et un écart type 1,0 pour vous.

Une simple addition et/ou multiplication modifiera l'écart moyen et l'écart type en fonction de vos besoins.

1
Pepijn Schmitz

Je voudrais utiliser Box-Muller. Deux choses à ce sujet:

  1. Vous vous retrouvez avec deux valeurs par itération
    En général, vous mettez en cache une valeur et retournez l’autre. Lors du prochain appel à un échantillon, vous renvoyez la valeur mise en cache.
  2. Box-Muller donne un Z-score
    Vous devez ensuite mettre à l'échelle le score Z en fonction de l'écart type et ajouter la moyenne pour obtenir la valeur complète dans la distribution normale.
1
hughdbrown

Le module de bibliothèque Python standard random a ce que vous voulez:

variable normale (mu, sigma)
Distribution normale. mu est la moyenne et sigma est la déviation standard.

Pour l'algorithme lui-même, jetez un oeil à la fonction random.py dans la bibliothèque Python.

La entrée manuelle est ici

1
Brent.Longborough

Où R1, R2 sont des nombres uniformes aléatoires:

DISTRIBUTION NORMALE, avec écart type de 1: sqrt (-2 * log (R1)) * cos

C'est exact ... pas besoin de faire toutes ces boucles lentes!

1
Erik Aronesty

Q Comment puis-je convertir une distribution uniforme (comme le produisent la plupart des générateurs de nombres aléatoires, par exemple entre 0,0 et 1,0) en une distribution normale?

  1. Pour l’implémentation logicielle, je connais quelques noms de générateurs aléatoires qui vous donnent une séquence aléatoire pseudo uniforme dans [0,1] (Mersenne Twister, Générateur de congruence linéaire). Appelons cela U (x)

  2. Il existe un domaine mathématique appelé théorie de la probabilité. Première chose: Si vous voulez modéliser r.v. avec la distribution intégrale F, vous pouvez simplement évaluer F ^ -1 (U (x)). Dans la théorie, il a été prouvé que de telles lois aura une distribution intégrale F.

  3. L'étape 2 peut être applicable pour générer r.v. ~ F sans utiliser de méthodes de comptage lorsque F ^ -1 peut être dérivé analytiquement sans problèmes. (par exemple, exp.distribution)

  4. Pour modéliser la distribution normale, vous pouvez calculer y1 * cos (y2), où y1 ~ est uniforme dans [0,2pi]. et y2 est la distribution relei.

Q: Et si je veux un écart moyen et standard de mon choix?

Vous pouvez calculer sigma * N (0,1) + m.

On peut montrer que ce décalage et cette mise à l'échelle mènent à N (m, sigma)

0
bruziuz

Ceci est une implémentation de Matlab utilisant la forme polaire de Box-Muller transformation:

Fonction 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

Et en invoquant histfit(randn_box_muller(10000000),100);, voici le résultat:  Box-Muller Matlab Histfit

Évidemment, il est vraiment inefficace par rapport au Matlab intégré randn .

0
madx

J'ai le code suivant qui pourrait peut-être aider:

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

Il est également plus facile d’utiliser la fonction implémentée rnorm () car elle est plus rapide que l’écriture d’un générateur de nombres aléatoires pour la distribution normale. Voir le code suivant comme preuve

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

Je pense que vous devriez essayer ceci dans Excel: =norminv(Rand();0;1). Cela produira les nombres aléatoires qui devraient normalement être distribués avec la moyenne nulle et la variance unifiée. "0" peut être fourni avec n'importe quelle valeur, de sorte que les nombres soient de la moyenne souhaitée, et en modifiant "1", vous obtiendrez la variance égale au carré de votre entrée.

Par exemple: =norminv(Rand();50;3) donnera les nombres normalement distribués avec MEAN = 50 VARIANCE = 9.

0
Hippo