Générateur De Nombres Pseudorandom-Distribution Exponentielle

j'aimerais générer quelques numéros de pseudorandom et jusqu'à présent j'ai été très satisfait de la fonction Random.Next(int min, int max) de la bibliothèque.Net. PRNGs de cette variété sont supposé d'être d'utiliser un distribution uniforme , mais je voudrais très bien générer quelques nombres en utilisant un Distribution exponentielle .

Je programme en C#, bien que j'accepte le pseudocode ou C++, Java ou autre.

des suggestions / extraits de code / algorithmes / pensées?

53
demandé sur Charlie Salts 2010-01-21 05:23:50

7 réponses

puisque vous avez accès à un générateur de nombres aléatoires uniforme, générant un nombre aléatoire distribué avec une autre distribution dont CDF vous savez est facile en utilisant la méthode d'inversion .

ainsi, générer un nombre aléatoire uniforme, u , dans [0,1) , puis calculer x par:

x = log(1-u)/( - λ ) ,

Où λ est le paramètre de vitesse de l'exponentielle distribution. Maintenant, x est un nombre aléatoire avec une distribution exponentielle. Notez que log ci-dessus est ln , le logarithme naturel.

88
répondu Alok Singhal 2010-01-21 03:53:03

le théorème fondamental de L'échantillonnage soutient que si vous pouvez normaliser, intégrer et inverser la distribution désirée vous êtes à la maison libre.

si vous avez une distribution désirée F(x) normalisé sur [a,b] . Vous calculez

C(y) = \int_a^y F(x) dx

Inverser que pour obtenir C^{-1} , jeter z uniformément sur [0,1) et trouver

x_i = C^{-1}(z_i)

qui aura la distribution désirée.


dans votre cas: F(x) = ke^{-kx} et je supposerai que vous voulez [0,infinity] . Nous obtenons:

C(y) = 1 - e^{-ky}

qui est inversable pour donner

x = -1/k  ln(1 - z)

pour z lancé uniformément sur [0,1) .


mais, franchement, utiliser une bibliothèque bien déboguée est plus intelligent à moins que vous ne le faites pour votre propre édification.

13
répondu dmckee 2010-03-31 15:06:42

si vous voulez de bons nombres aléatoires, envisagez un lien vers les routines gsl: http://www.gnu.org/software/gsl / . Ils ont la routine gsl_ran_exponential . Si vous voulez générer des nombres aléatoires en utilisant un générateur intégré avec une distribution uniforme sur [0, 1) (par exemple u=aléatoire.Prochaine(0, N-1)/N, pour certains, un grand N), alors il suffit d'utiliser:

-mu * log (1-u)

voir randist / exponential.c dans la source gsl.

EDIT: juste pour comparaison avec quelques réponses ultérieures-ceci est équivalent avec mu = 1 / lambda. mu ici est la moyenne de la distribution, aussi appelé paramètre d'échelle sur la page wikipedia à laquelle L'OP lié, et lambda est le paramètre de vitesse.

6
répondu Ramashalanka 2010-01-21 02:52:58

une propriété intéressante de la distribution exponentielle: considérez un processus d'arrivée avec des temps interarrivaux exponentiels. Prendre une période de temps (t1, t2) et les arrivées dans cette période. Ces arrivées sont réparties uniformément entre t1 et t2. (Sheldon Ross, Processus Stochastiques).

si j'ai un générateur de nombres pseudo-aléatoires et, pour une raison quelconque (par exemple, mon logiciel ne peut pas calculer les logs) vous ne voulez pas faire la transformation ci-dessus, mais voulez un exponentielle R. v. avec une moyenne de 1,0.

, Vous pouvez :

1) Créer 1001 U(0,1) variables aléatoires.

2) trier dans l'ordre

3) soustraire le deuxième du premier, Le troisième du deuxième,... pour obtenir 1000 différences.

4) Ces différences sont des valeurs de référence exponentielles à partir d'une distribution moyenne = 1,0.

moins efficace, je pense, mais un moyen pour le même but.

4
répondu Grembo 2010-01-26 21:47:07

open-source Uncommons Mathématiques de la bibliothèque par Dan Dyer fournit des générateurs de nombres aléatoires, distributions de probabilités, combinatoire et statistiques pour Java.

parmi d'autres classes précieuses, ExponentialGenerator a essentiellement mis en œuvre l'idée expliquée par @Alok Singhal. Dans son blog tutoriel , un extrait de code est donné pour simuler un événement aléatoire qui s'est produit en moyenne 10 fois par minute:

final long oneMinute = 60000;
Random rng = new MersenneTwisterRNG();

// Generate events at an average rate of 10 per minute.
ExponentialGenerator gen = new ExponentialGenerator(10, rng);
boolean running = true;
while (true)
{
    long interval = Math.round(gen.nextValue() * oneMinute);
    Thread.sleep(interval);

    // Fire event here.
}

bien sûr, si vous préférez l'Unité de temps per second (au lieu de a minute ici), vous avez juste besoin de mettre final long oneMinute = 1000 .

aller plus loin dans le code source de la méthode nextValue() de ExponentialGenerator , vous trouverez le soi-disant inverse transform sampling décrit dans Generating_exponential_variates [wiki] :

public Double nextValue()
{
    double u;
    do
    {
        // Get a uniformly-distributed random double between
        // zero (inclusive) and 1 (exclusive)
        u = rng.nextDouble();
    } while (u == 0d); // Reject zero, u must be positive for this to work.
    return (-Math.log(u)) / rate.nextValue();
}  

P.S.: récemment, j'utilise la bibliothèque de mathématiques Uncommons. Merci Dan Dyer.

1
répondu hengxin 2014-06-27 01:37:55

si je comprends votre problème, et vous pouvez accepter un nombre limité de PRNG, vous pouvez suivre une approche comme:

  • créez un tableau où chaque élément est dans votre distribution exponentielle
  • génère un PRNG qui est un indice entier dans le tableau. Renvoie l'élément dans le tableau à cet index.
0
répondu GreenMatt 2010-01-22 15:15:32

C'est ce que j'ai utilisé face à des exigences similaires:

// sorry.. pseudocode, mine was in Tcl:

int weighted_random (int max) {
    float random_number = rand();
    return floor(max - ceil( max * random_number * random_number))
}

bien sûr, c'est la formule de la quadrature du nombre aléatoire donc vous générez un nombre aléatoire le long d'une courbe quadratique.

-2
répondu slebetman 2010-01-21 02:37:36