2009-08-20 15 views
10

Dato l'inizio e la fine di un intervallo intero, come faccio a calcolare un intero casuale distribuito normalmente tra questo intervallo?Come generare casuale distribuito casualmente da un intervallo intero?

Mi rendo conto che la distribuzione normale va in - + infinito. Immagino che le code possano essere tagliate, quindi quando un random viene calcolato al di fuori dell'intervallo, ricalcalo. Questo eleva la probabilità di numeri interi nell'intervallo, ma finché questo effetto è tollerabile (< 5%), va bene.

public class Gaussian 
{ 
    private static bool uselast = true; 
    private static double next_gaussian = 0.0; 
    private static Random random = new Random(); 

    public static double BoxMuller() 
    { 
     if (uselast) 
     { 
      uselast = false; 
      return next_gaussian; 
     } 
     else 
     { 
      double v1, v2, s; 
      do 
      { 
       v1 = 2.0 * random.NextDouble() - 1.0; 
       v2 = 2.0 * random.NextDouble() - 1.0; 
       s = v1 * v1 + v2 * v2; 
      } while (s >= 1.0 || s == 0); 

      s = System.Math.Sqrt((-2.0 * System.Math.Log(s))/s); 

      next_gaussian = v2 * s; 
      uselast = true; 
      return v1 * s; 
     } 
    } 

    public static double BoxMuller(double mean, double standard_deviation) 
    { 
     return mean + BoxMuller() * standard_deviation; 
    } 

    public static int Next(int min, int max) 
    { 
     return (int)BoxMuller(min + (max - min)/2.0, 1.0); 
    } 
} 

probabilmente ho bisogno di scalare la deviazione standard in qualche modo rispetto alla gamma, ma non capisco come.

Risposta:

// Will approximitely give a random gaussian integer between min and max so that min and max are at 
    // 3.5 deviations from the mean (half-way of min and max). 
    public static int Next(int min, int max) 
    { 
     double deviations = 3.5; 
     int r; 
     while ((r = (int)BoxMuller(min + (max - min)/2.0, (max - min)/2.0/deviations)) > max || r < min) 
     { 
     } 

     return r; 
    } 

risposta

6

Se il metodo di Box-Muller restituisce un "standard" distribuzione normale, avrà media 0 e deviazione standard 1. Per trasformare una distribuzione normale standard, si moltiplica il tuo numero a caso da X per ottenere la deviazione standard X, e si aggiunge Y per ottenere Y medio, se la memoria mi serve correttamente.

Vedere il Wikipedia article's section on normalizing standard normal variables (property 1) per una prova più formale.


In risposta al tuo commento, la regola generale è che il 99,7% di una distribuzione normale sarà entro +/- 3 volte la deviazione standard. Se hai bisogno di una distribuzione normale da 0 a 100, ad esempio, la tua media sarà a metà strada e la tua SD sarà (100/2)/3 = 16.667. Quindi, indipendentemente dai valori che si ottengono dall'algoritmo Box-Muller, moltiplicare per 16.667 per "estendere" la distribuzione, quindi aggiungere 50 per "centrarlo".


John, in risposta al vostro ultimo commento, io non sono davvero sicuro di quello che è il punto della funzione Next. Utilizza sempre una deviazione standard di 1 e una media di metà tra il minimo e il massimo.

Se si desidera una media di Y, con ~ 99,7% dei numeri nell'intervallo da -X a + X, basta chiamare lo BoxMuller(Y, X/3).

+0

Sì, questo è ciò che il BoxMuller (double mean, double standard_deviation) raggiunge sopra. Il problema, tuttavia, è che il prossimo (int min, int max) restituisce valori molto vicini alla metà del range. Questo perché non capisco come "scalare" correttamente la deviazione. –

+0

Quindi per 3,5 deviazioni standard, sarebbe "return (int) BoxMuller (min + (max - min)/2.0, (max - min)/2.0/3.5)"? –

+0

Posso solo commentare che +/- 3 volte la deviazione standard non ti dà il 97% ma il 99,7%. +/- sigma: ~ 68% +/- 2sigma: ~ 95% +/- 3sigma: ~ 99,7% http://en.wikipedia.org/wiki/68-95-99.7_rule – DmitryK

1

Bene, il sigma -2 * sigma .. + 2 * vi darà il 95% della curva campana. (controlla la sezione "Deviazione standard e intervalli di confidenza" nell'articolo wiki già citato).

Quindi modificare questo pezzo:

return (int)BoxMuller(min + (max - min)/2.0, 1.0); 

e cambiare 1,0 (deviazione standard) a 2,0 (o anche di più se si desidera una copertura superiore al 95%)

return (int)BoxMuller(min + (max - min)/2.0, 2.0); 
+0

Ora capisco cosa voglio formulare grazie a te .. Voglio che il sigma -2 * sigma .. + 2 * sia all'inizio e alla fine dell'intervallo (min, max) rispettivamente. –

+0

Ah, capisco ora. Quindi vuoi uscire dal valore medio a sinistra e a destra e "colpire" min o max con una confidenza del 95%. In questo caso, si mantiene la media come si ha (min + (max-min)/2), ma è necessario calcolare il proprio sigma (deviazione standard). Uscire di 2 * sigma ci dà quell'intervallo del 95%. Quindi la lunghezza di questo intervallo è 4 * sigma. Ma possiamo anche calcolarlo come (max-min). Il che ci dà sigma = (max-min)/4. Potete per favore provarlo? – DmitryK

+0

"hit" nella gamma min..max con il 95% di confidenza - solo per essere precisi nel mio testo. – DmitryK

Problemi correlati