web-dev-qa-db-fra.com

Générateur de nombres pseudo-aléatoires - Distribution exponentielle

Je voudrais générer des nombres pseudo-aléatoires et jusqu'à présent, je me suis contenté de la fonction Random.Next(int min, int max) de la bibliothèque .Net. Les PRNG de cette variété sont supposés utiliser un distribution uniforme , mais j'aimerais beaucoup générer des nombres en utilisant un Distribution exponentielle .

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

Des suggestions/extraits de code/algorithmes/réflexions?

57
Charlie Salts

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

Donc, générez un nombre aléatoire uniforme, u, dans [0,1), Puis calculez x par:

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

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

98
Alok Singhal

Le théorème fondamental de l'échantillonnage soutient que si vous pouvez normaliser, intégrer et inverser la distribution souhaitée, vous êtes chez vous.

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

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

inverser cela pour obtenir C^{-1}, lancer z uniformément sur [0,1) et trouver

x_i = C^{-1}(z_i)

qui aura la distribution souhaitée.


Dans votre cas: F(x) = ke^{-kx} et je suppose que vous voulez [0,infinity]. On a :

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, l'utilisation d'une bibliothèque bien déboguée est plus intelligente, sauf si vous le faites pour votre propre édification.

13
dmckee

Si vous voulez de bons nombres aléatoires, envisagez de créer un lien vers les routines gsl: http://www.gnu.org/software/gsl/ . Ils ont la routine gsl_ran_exponential. Si vous souhaitez générer des nombres aléatoires à l'aide d'un générateur intégré avec une distribution uniforme sur [0, 1) (par exemple u = Random.Next (0, N-1)/N, pour certains N importants), utilisez simplement:

-mu * log (1-u)

Voir randist/exponential.c dans la source gsl.

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

6
Ramashalanka

Une propriété intéressante de la distribution exponentielle: Considérons un processus d'arrivée avec des temps interarrivaux exponentiels. Prenez n'importe quelle période de temps (t1, t2) et les arrivées de cette période. Ces arrivées sont UNIFORMEMENT réparties 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 journaux), vous ne voulez pas faire la transformation ci-dessus, mais voulez un r.v. exponentiel. avec une moyenne de 1,0.

Vous pouvez :

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

2) Trier dans l'ordre

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

4) Ces différences sont des RV exponentielles avec une distribution avec une moyenne = 1,0.

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

4
Grembo

L'open source bibliothèque Uncommons Maths de Dan Dyer fournit des générateurs de nombres aléatoires, des distributions de probabilités, des combinatoires et des statistiques pour Java.

Parmi d'autres classes précieuses, ExponentialGenerator a essentiellement implémenté l'idée expliquée par @Alok Singhal. Dans son blog de 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), il vous suffit de définir final long oneMinute = 1000.

En approfondissant le code source de la méthode nextValue() de ExponentialGenerator, vous trouverez ce que l'on appelle échantillonnage par transformée inverse 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 Uncommons Maths. Merci Dan Dyer.

1
hengxin

Si je comprends votre problème et que vous pouvez accepter un nombre fini de PRNG, vous pouvez suivre une approche comme:

  • Créez un tableau où chaque élément est dans votre distribution exponentielle
  • Générez un PRNG qui est un index entier dans le tableau. Renvoyez l'élément dans le tableau à cet index.
0
GreenMatt