2010-09-19 7 views
3

Je tente de générer un nombre aléatoire avec une distribution logarithmique.Java: Générer un nombre aléatoire avec une distribution logarithmique

Où n = 1 se produit la moitié du temps, n = 2 se produit un quart du temps, n = 3 se produit un huitième du temps, etc.

int maxN = 5; 
    int t = 1 << (maxN); // 2^maxN 
    int n = maxN - 
      ((int) (Math.log((Math.random() * t)) 
      /Math.log(2))); // maxN - log2(1..maxN) 
    System.out.println("n=" + n); 

La plupart du temps, je suis obtenir le résultat dont j'ai besoin, mais une fois de temps en temps, j'ai une valeur de n qui est supérieure à maxN.

Pourquoi est-ce ainsi? La façon dont je le vois, la valeur maximale de Math.random() est 1,0; Par conséquent, la valeur maximale de (Math.random() * t)) est t
donc la valeur maximale de log2 (t) est maxN, puisque t = 2^maxN;

Où ma logique a-t-elle dérapé?

Merci

Répondre

6

logarithme des nombres inférieurs à 1,0 est négatif. Lorsque le nombre aléatoire généré est tel qu'il est inférieur à 1,0, l'expression ((int) (Math.log(Math.random() * t)/Math.log(2))) est un nombre négatif et donc maxN - (the negative number) est supérieur à maxN.

L'expression suivante doit donner une distribution correcte.

n = Math.floor(Math.log((Math.random() * t) + 1)/Math.log(2)) 

Notez que:

0.0 <= Math.random() <= 1.0 
0.0 <= Math.random() * t <= t 
1.0 <= (Math.random() * t) + 1 <= t + 1.0 
0.0 <= Math.log((Math.random() * t) + 1) <= Math.log(t + 1.0) 
0.0 <= Math.log((Math.random() * t) + 1)/Math.log(2) <= Math.log(t + 1.0)/Math.log(2) 

Since t = 2^maxN, 
Math.log(t + 1.0)/Math.log(2) is slightly larger than maxN. 

So do a Math.floor and you get the correct result: 
0.0 <= Math.floor(Math.log((Math.random() * t) + 1)/Math.log(2)) <= maxN 
+0

+1 @ abhin4v: Merci pour le commentaire! – bguiz

+0

J'ai eu le chèque, alors vous l'avez pris! –

+0

+ check @ abhin4v: Vous avez raison à propos de log (t)/log (2)> maxN L'erreur était "cachée" en raison de la conversion en 'int', mais c'est une façon plus appropriée d'un point de vue émathématique. – bguiz

2

Si Math.random()*t est inférieur à 1, alors vous obtiendrez une réponse négative lorsque vous prenez Math.log(Math.random()*t), par les règles de logarithmes. Cela signifie que vous obtiendrez une réponse négative lorsque vous divisez par Math.log(2) parce que c'est 0,69314718055994530941723212145818. C'est un nombre négatif divisé par un nombre positif. La réponse est négative. maxN - un nombre négatif = maxN + quelque chose de positif, donc n est supérieur à maxN. Pour résoudre ce casting Math.random() * t un int et ajouter 1:

int n = maxN - 
     ((int) (Math.log((int)((Math.random() * t)+1)) 
     /Math.log(2))); // maxN - log2(1..maxN) 

Notez la fonte à l'intérieur du journal, et l'ajout de 1.

Le but d'ajouter un serait Évitez le 0. Impossible de prendre un journal de 0. De plus, sans ajouter 1, vous ne pourrez jamais obtenir maxN dans le journal, car Math.random() ne produit jamais 1. De cette façon, au lieu d'obtenir 1 moitié, 2, un quatrième, 3, et huitième, il commence juste à 0. Cela donne 0, un demi, 1 un quatrième, 2 un huitième, etc.

+0

La conversion d'un nombre inférieur à 1,0 en 'int' produira 0 et la prise d'un journal produira' -Infinity'. Votre suggestion est donc incorrecte. –

+0

@ abhin4v: Comment travailleriez-vous autour de ça alors ... est-il possible de le faire sans utiliser une construction de type if-then-else? – bguiz

+0

Je viens de corriger cela. –

1

Le problème est dans l'autre extrémité de l'échelle. Pensez à ce qui se passerait si vous obtenez un petit nombre aléatoire.

Questions connexes