2010-01-27 11 views
12

L'oggetto MATH di Javascript ha un metodo casuale che restituisce dall'insieme [0,1) 0 compreso, 1 esclusivo. C'è un modo per restituire un metodo veramente casuale che include 1.Scrivi un metodo casuale veramente inclusivo per javascript

ad es.

var rand = MATH.random()*2; 

if(rand > 1) 
{ 
    rand = MATH.floor(rand); 
} 

return rand; 

Mentre questo restituisce sempre un numero dall'insieme [0,1], non è veramente casuale.

+2

definire casuale :) –

+1

mi chiedo quale sia l'uso di includere specificamente 1? – bryantsai

+2

È casuale; solo non uniformemente casuale. –

risposta

8

Se l'inclusione dei confini non dovrebbe avere importanza; anzi, in senso stretto, quello che stai cercando di fare non ha molto senso. Ricorda che sotto una distribuzione di probabilità continua, la probabilità di ottenere un valore specifico è comunque infinitesimale, quindi matematicamente parlando, non vedrai mai il valore esatto di 1.

Naturalmente, nel mondo dei computer, la distribuzione di un RNG non è veramente continuo, quindi è "possibile" che incontrerai un valore specifico (qualunque cosa significhi), ma il fatto che stai facendo affidamento su una limitazione di come vengono memorizzati i numeri reali suggerisce problemi con il tuo approccio a qualunque problema tu stia cercando di risolvere.

+0

Grazie per la risposta. Penso che sia solo un esercizio poco formato che sto facendo. L'esercizio è quello di scrivere il proprio oggetto Math con un metodo casuale che ha come argomenti minimi, massimi e inclusi. L'argomento inclusivo è un booleano che dice se vuoi che il set sia inclusivo o esclusivo. Ora come ho sottolineato nella mia domanda iniziale che la funzione Math.random() non è né inclusiva né esclusiva perché include 0 ma esclude 1. Quindi scrivere un metodo casuale che sia veramente inclusivo o esclusivo e uniformemente distribuito non è possibile, giusto ? –

+0

Non è impossibile, poiché in realtà esiste solo un numero finito di valori che possono essere generati dall'RNG tra 0 e 1 (anziché un continuum). Tuttavia, è matematicamente privo di senso farlo. O l'esercizio si riferisce specificamente agli interi, o è una domanda sbagliata. Inoltre, l'estensione di una distribuzione uniforme discreta per formare un'altra distribuzione uniforme non è banale. Le soluzioni di Trevor e Brian McKenna sono molto vicine, ma non perfettamente uniformi. –

3

Vuoi includere 1?

return 1 - Math.random(); 

Tuttavia, penso che questa sia una di quelle domande che suggerisce altri problemi. Perché hai bisogno di includere 1? C'è probabilmente un modo migliore per farlo.

+5

Ma non includerà 0 – Trevor

+0

che sarebbe 0 esclusivo ora .. non è vero? –

+4

Sì, ma non ha detto che escludere 0 era un problema, solo che l'esclusione di 1 era. –

11

Ciò restituirà [0,1] inclusiva:

if(MATH.random() == 0) 
    return 1; 
else 
    return MATH.random(); 

Spiegazione: Se la prima chiamata a random() restituisce 0, ritorno 1. In caso contrario, chiamare di nuovo a caso, che sarà [0,1). Pertanto, restituirà [0,1] tutto compreso.

+0

perché casuale due volte. cosa succede se ottieni ancora 0 ... – bryantsai

+0

Questo non includerà neanche 0, come citato nel tuo commento al nickf –

+1

Se la prima chiamata casuale() non è 0, la seconda chiamata casuale() potrebbe essere. – Trevor

6

La funzione Math.random restituisce un numero casuale compreso tra 0 e 1, dove 0 è compreso e 1 è esclusivo. Ciò significa che l'unico modo per distribuire correttamente i numeri casuali come numeri interi in un intervallo consiste nell'utilizzare un limite superiore esclusivo.

Per specificare un limite superiore compreso, è sufficiente aggiungerne uno per renderlo esclusivo nel calcolo. Questo distribuirà i numeri casuali correttamente tra 7 e 12, inclusi:

var min = 7; 
var max = 12; 
var rnd = min + Math.floor(Math.random() * (max - min + 1)); 
+0

+1 per l'approccio classico alla definizione di un intervallo casuale arbitrario –

0

Addendum:

Dando uno sguardo al codice java.util.Random sorgente incluso con la distribuzione di Oracle JDK 7 ("Copyright (c) ..., 1995, 2010, Oracle e/o delle sue affiliate Tutti i diritti riservati ORACLE PROPRIETÀ/CONFIDENTIAL uso è soggetto ai termini di licenza") mostra questo semplice codice:

class Random { 

    public float nextFloat() { 
     return next(24)/((float)(1 << 24)); 
    } 

    protected int next(int bits) { 
     long oldseed, nextseed; 
     AtomicLong seed = this.seed; 
     do { 
      oldseed = seed.get(); 
      nextseed = (oldseed * multiplier + addend) & mask; 
     } while (!seed.compareAndSet(oldseed, nextseed)); 
     return (int)(nextseed >>> (48 - bits)); 
    } 
} 

Così, per 0.123.167,633293 millions:

  1. prendere un "valore intero casuale" tra 0 e 2^24-1 (o meglio, un casuale 24 bit bitpattern interpretato come un valore intero),
  2. Convertire a galleggiare (in Java "float" ha il mandato di essere un IEEE 724 a 32 bit in virgola mobile, che può rappresentare fino a 2^24 senza perdita di precisione, e quindi sarà un valore compreso tra 0 e 1.6777215E7)
  3. Quindi dividerlo per la rappresentazione float di 2^24, nuovamente appena rappresentabile senza perdita di precisione come 1.6777216E7. 2^24 + 1 = 16777217 scenderebbe a 1.6777216E7 quando forzato a essere float. Nel codice, questo dovrebbe essere davvero una costante. Hey Sun, i cicli non crescono sugli alberi !!
  4. La divisione risulta in un float in [0.0 .. 0.99999994] (il risultato della divisione corretta sarebbe intorno a 0.999999940395355224609375), con, penso, tutti i possibili valori in virgola mobile IEEE 724 tra "ugualmente possibile".

Vedere anche IEEE floating point e Floating-Point Arithmetic on the JVM.

commenti Javadoc per `prossimo() è:

/** 
* Generates the next pseudorandom number. Subclasses should 
* override this, as this is used by all other methods. 
* 
* <p>The general contract of {@code next} is that it returns an 
* {@code int} value and if the argument {@code bits} is between 
* {@code 1} and {@code 32} (inclusive), then that many low-order 
* bits of the returned value will be (approximately) independently 
* chosen bit values, each of which is (approximately) equally 
* likely to be {@code 0} or {@code 1}. The method {@code next} is 
* implemented by class {@code Random} by atomically updating the seed to 
* <pre>{@code (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)}</pre> 
* and returning 
* <pre>{@code (int)(seed >>> (48 - bits))}.</pre> 
* 
* This is a linear congruential pseudorandom number generator, as 
* defined by D. H. Lehmer and described by Donald E. Knuth in 
* <i>The Art of Computer Programming,</i> Volume 3: 
* <i>Seminumerical Algorithms</i>, section 3.2.1. 
* 
* @param bits random bits 
* @return the next pseudorandom value from this random number 
*   generator's sequence 
* @since 1.1 
*/ 

I commenti Javadoc per nextFloat() è:

/** 
* Returns the next pseudorandom, uniformly distributed {@code float} 
* value between {@code 0.0} and {@code 1.0} from this random 
* number generator's sequence. 
* 
* <p>The general contract of {@code nextFloat} is that one 
* {@code float} value, chosen (approximately) uniformly from the 
* range {@code 0.0f} (inclusive) to {@code 1.0f} (exclusive), is 
* pseudorandomly generated and returned. All 2<font 
* size="-1"><sup>24</sup></font> possible {@code float} values 
* of the form <i>m&nbsp;x&nbsp</i>2<font 
* size="-1"><sup>-24</sup></font>, where <i>m</i> is a positive 
* integer less than 2<font size="-1"><sup>24</sup> </font>, are 
* produced with (approximately) equal probability. 
* 
* <p>The method {@code nextFloat} is implemented by class {@code Random} 
* as if by: 
* <pre> {@code 
* public float nextFloat() { 
* return next(24)/((float)(1 << 24)); 
* }}</pre> 
* 
* <p>The hedge "approximately" is used in the foregoing description only 
* because the next method is only approximately an unbiased source of 
* independently chosen bits. If it were a perfect source of randomly 
* chosen bits, then the algorithm shown would choose {@code float} 
* values from the stated range with perfect uniformity.<p> 
* [In early versions of Java, the result was incorrectly calculated as: 
* <pre> {@code 
* return next(30)/((float)(1 << 30));}</pre> 
* This might seem to be equivalent, if not better, but in fact it 
* introduced a slight nonuniformity because of the bias in the rounding 
* of floating-point numbers: it was slightly more likely that the 
* low-order bit of the significand would be 0 than that it would be 1.] 
* 
* @return the next pseudorandom, uniformly distributed {@code float} 
*   value between {@code 0.0} and {@code 1.0} from this 
*   random number generator's sequence 
*/ 
0

Da quello che posso vedere dalla console JavaScript in Chrome, Math.random() genera un numero da 0 a 0,9999,99999999999. Tenendo conto di ciò, puoi ottenere quello che vuoi aggiungendo un modificatore. Ad esempio, ecco una funzione che vi darà galleggiante quasi-casuale tra 0 e 1, dove 1 è compreso:

function randFloat() { 
    // Assume random() returns 0 up to 0.9999999999999999 
    return Math.random()*(1+2.5e-16); 
} 

si può provare questo nella console per entrare 0.9999999999999999*(1+2.5e-16) - tornerà esattamente 1. È può prendere questo ulteriore e restituire un float compreso tra 0 e 1024 (compreso) con questa funzione:

function randFloat(nMax) { 
    // Assume random() returns 0 up to 0.9999999999999999 
    // nMax should be a float in the range 1-1024 
    var nMod; 
    if (nMax<4) nMod = 2.5e-16; 
    else if (nMax<16) nMod = 1e-15; 
    else if (nMax<32) nMod = 3.5e-15; 
    else if (nMax<128) nMod = 1e-14; 
    else if (nMax<512) nMod = 3.5e-14; 
    else if (nMax<1024) nMod = 1e-13; 
    return Math.random()*(nMax+nMod); 
} 

C'è probabilmente un algoritmo più efficiente per essere avuto da qualche parte.

0

Poiché questa domanda è stata posta di nuovo, e non ho letto questo approccio qui aggiungerò un'altra risposta.

IMO il meglio che si può fare, senza problemi troppo sarebbe:

esclusivo:

//simply ignore the 0 
for(var rand=0; !rand;rand = Math.random()); 

//or simpler: 
var rand = Math.random() || Math.random(); 
//because the probability for two consecutive `0` is pretty much non existant. 

questo non ha nemmeno introdurre un errore, dal momento che abbiamo appena escluso la possibilità di tornare 0, ogni altro valore compreso tra 0 e 1 ha la stessa probabilità

inclusiva:

var rand = Math.random() * 2; 
if(rand > 1) rand-=1; 
//so the 0 shares it's probability with the 1. 

tanto per essere chiari su come piccolo l ' "errore" è:

  • la probabilità per un 0 o un 1 è
    1/Math.pow(2, 54) o circa 5.55e-17
  • la probabilità per ogni altro valore compreso tra 0 e 1 è
    1/Math.pow(2, 53) o circa 11.1e-17

e la tutto random-funzione sarebbe:

function random(min, max, inclusive){ 
    var r = Math.random(); 
    if(inclusive) 
     r = r>0.5? 2*r-1: 2*r; 
    else 
     while(!r) r = Math.random(); 

    return r * (max - min) + min; 
} 

Edit: Non sono sicuro wether faccio un errore, ma non dovrebbe la probabilità essere fissato su un approccio inclusivo, se posso aggiungere un altro po 'per gli zeri e quelli, e quindi duplicare la loro probabilità:

var rand = Math.random() * 4; 
rand = (rand % 1) || (rand & 1);