Domanda

Esiste una classe nella libreria standard di .NET che mi dà la funzionalità per creare variabili casuali che seguono la distribuzione gaussiana?

È stato utile?

Soluzione

Il suggerimento di Jarrett di usare una trasformazione Box-Muller è buono per una soluzione rapida e sporca. Una semplice implementazione:

Random rand = new Random(); //reuse this if you are generating many
double u1 = 1.0-rand.NextDouble(); //uniform(0,1] random doubles
double u2 = 1.0-rand.NextDouble();
double randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) *
             Math.Sin(2.0 * Math.PI * u2); //random normal(0,1)
double randNormal =
             mean + stdDev * randStdNormal; //random normal(mean,stdDev^2)

Altri suggerimenti

Questa domanda sembra essersi spostata su Google per la generazione gaussiana di .NET, quindi ho pensato di pubblicare una risposta.

Ho realizzato alcuni metodi di estensione per la classe .NET Random , inclusa un'implementazione di la trasformazione di Box-Muller. Poiché sono estensioni, purché il progetto sia incluso (o fai riferimento alla DLL compilata), puoi comunque farlo

var r = new Random();
var x = r.NextGaussian();

Spero che nessuno si preoccupi della spina spudorata.

Esempio di istogramma dei risultati (è inclusa un'app demo per disegnare questo):

inserisci qui la descrizione dell'immagine

Math.NET fornisce questa funzionalità. Ecco come:

double mean = 100;
double stdDev = 10;

MathNet.Numerics.Distributions.Normal normalDist = new Normal(mean, stdDev);
double randomGaussianValue=   normalDist.Sample();

Puoi trovare la documentazione qui: http://numerics.mathdotnet.com/api/MathNet.Numerics.Distributions/Normal.htm

Ho creato una richiesta per tale funzionalità su Microsoft Connect. Se questo è qualcosa che stai cercando, per favore votalo e aumentane la visibilità.

https://connect.microsoft. com / VisualStudio / retroazione / dettagli / 634.346 / Guassian-normale-random-numeri-distribuzione

Questa funzione è inclusa in Java SDK. La sua implementazione è disponibile come parte di la documentazione ed è facilmente trasferibile in C # o in altri linguaggi .NET.

Se stai cercando pura velocità, il Zigorat Algorithm è generalmente riconosciuto come approccio più veloce.

Non sono un esperto di questo argomento, ma ho riscontrato la necessità di implementare un filtro antiparticolato per la mia libreria robotizzata per il calcio simulato 3D di RoboCup e sono rimasto sorpreso quando questo non è stato incluso nel framework.


Nel frattempo, ecco un wrapper per Random che fornisce un'implementazione efficiente del metodo polare Box Muller:

public sealed class GaussianRandom
{
    private bool _hasDeviate;
    private double _storedDeviate;
    private readonly Random _random;

    public GaussianRandom(Random random = null)
    {
        _random = random ?? new Random();
    }

    /// <summary>
    /// Obtains normally (Gaussian) distributed random numbers, using the Box-Muller
    /// transformation.  This transformation takes two uniformly distributed deviates
    /// within the unit circle, and transforms them into two independently
    /// distributed normal deviates.
    /// </summary>
    /// <param name="mu">The mean of the distribution.  Default is zero.</param>
    /// <param name="sigma">The standard deviation of the distribution.  Default is one.</param>
    /// <returns></returns>
    public double NextGaussian(double mu = 0, double sigma = 1)
    {
        if (sigma <= 0)
            throw new ArgumentOutOfRangeException("sigma", "Must be greater than zero.");

        if (_hasDeviate)
        {
            _hasDeviate = false;
            return _storedDeviate*sigma + mu;
        }

        double v1, v2, rSquared;
        do
        {
            // two random values between -1.0 and 1.0
            v1 = 2*_random.NextDouble() - 1;
            v2 = 2*_random.NextDouble() - 1;
            rSquared = v1*v1 + v2*v2;
            // ensure within the unit circle
        } while (rSquared >= 1 || rSquared == 0);

        // calculate polar tranformation for each deviate
        var polar = Math.Sqrt(-2*Math.Log(rSquared)/rSquared);
        // store first deviate
        _storedDeviate = v2*polar;
        _hasDeviate = true;
        // return second deviate
        return v1*polar*sigma + mu;
    }
}

Math.NET Iridium afferma anche di implementare generatori casuali non uniformi (normale, poisson, binomiale , ...) " ;.

Ecco un'altra soluzione rapida e sporca per generare variabili casuali che sono distribuite normalmente . Disegna un punto casuale (x, y) e verifica se questo punto si trova sotto la curva della funzione di densità della probabilità, altrimenti ripeti.

Bonus: puoi generare variabili casuali per qualsiasi altra distribuzione (ad es. distribuzione esponenziale o distribuzione di Poisson ) semplicemente sostituendo la funzione di densità.

    static Random _rand = new Random();

    public static double Draw()
    {
        while (true)
        {
            // Get random values from interval [0,1]
            var x = _rand.NextDouble(); 
            var y = _rand.NextDouble(); 

            // Is the point (x,y) under the curve of the density function?
            if (y < f(x)) 
                return x;
        }
    }

    // Normal (or gauss) distribution function
    public static double f(double x, double μ = 0.5, double σ = 0.5)
    {
        return 1d / Math.Sqrt(2 * σ * σ * Math.PI) * Math.Exp(-((x - μ) * (x - μ)) / (2 * σ * σ));
    }

Importante: selezionare l'intervallo di y e i parametri s e µ in modo che la curva della funzione non sia tagliata al massimo / punti minimi (ad esempio a x = media). Pensa agli intervalli di x e y come un rettangolo di selezione, in cui la curva deve adattarsi.

Vorrei approfondire la risposta di @ yoyoyoyosef rendendola ancora più veloce e scrivendo una classe wrapper. Il sovraccarico sostenuto potrebbe non significare due volte più veloce, ma penso che dovrebbe essere quasi due volte più veloce. Tuttavia, non è thread-safe.

public class Gaussian
{
     private bool _available;
     private double _nextGauss;
     private Random _rng;

     public Gaussian()
     {
         _rng = new Random();
     }

     public double RandomGauss()
     {
        if (_available)
        {
            _available = false;
            return _nextGauss;
        }

        double u1 = _rng.NextDouble();
        double u2 = _rng.NextDouble();
        double temp1 = Math.Sqrt(-2.0*Math.Log(u1));
        double temp2 = 2.0*Math.PI*u2;

        _nextGauss = temp1 * Math.Sin(temp2);
        _available = true;
        return temp1*Math.Cos(temp2);
     }

    public double RandomGauss(double mu, double sigma)
    {
        return mu + sigma*RandomGauss();
    }

    public double RandomGauss(double sigma)
    {
        return sigma*RandomGauss();
    }
}

Espandendo la risposta di Drew Noakes, se hai bisogno di prestazioni migliori di Box-Muller (circa il 50-75% più veloce), Colin Green ha condiviso un'implementazione dell'algoritmo Ziggurat in C #, che puoi trovare qui:

http://heliosphan.org/zigguratalgorithm/zigguratalgorithm.html

Ziggurat utilizza una tabella di ricerca per gestire i valori che cadono sufficientemente lontano dalla curva, che accetterà o rifiuterà rapidamente. Circa il 2,5% delle volte, deve eseguire ulteriori calcoli per determinare su quale lato della curva si trova un numero.

Espandendo le risposte di @Noakes e @ Hameer, ho anche implementato una classe 'gaussiana', ma per semplificare lo spazio di memoria, l'ho resa figlia della classe casuale in modo che tu possa anche chiamare il successivo Next (), NextDouble (), ecc. Anche della classe gaussiana senza dover creare un oggetto casuale aggiuntivo per gestirlo. Ho anche eliminato le proprietà di classe globale _available e _nextgauss, poiché non le vedevo necessarie poiché questa classe si basa sull'istanza, dovrebbe essere thread-safe, se si assegna a ciascun thread il proprio oggetto gaussiano. Ho anche spostato tutte le variabili allocate di runtime fuori dalla funzione e le ho rese proprietà di classe, questo ridurrà il numero di chiamate al gestore della memoria poiché i 4 doppi non dovrebbero teoricamente mai essere disallocati fino a quando l'oggetto non viene distrutto.

public class Gaussian : Random
{

    private double u1;
    private double u2;
    private double temp1;
    private double temp2;

    public Gaussian(int seed):base(seed)
    {
    }

    public Gaussian() : base()
    {
    }

    /// <summary>
    /// Obtains normally (Gaussian) distrubuted random numbers, using the Box-Muller
    /// transformation.  This transformation takes two uniformly distributed deviates
    /// within the unit circle, and transforms them into two independently distributed normal deviates.
    /// </summary>
    /// <param name="mu">The mean of the distribution.  Default is zero</param>
    /// <param name="sigma">The standard deviation of the distribution.  Default is one.</param>
    /// <returns></returns>

    public double RandomGauss(double mu = 0, double sigma = 1)
    {
        if (sigma <= 0)
            throw new ArgumentOutOfRangeException("sigma", "Must be greater than zero.");

        u1 = base.NextDouble();
        u2 = base.NextDouble();
        temp1 = Math.Sqrt(-2 * Math.Log(u1));
        temp2 = 2 * Math.PI * u2;

        return mu + sigma*(temp1 * Math.Cos(temp2));
    }
}

Potresti provare Infer.NET. Tuttavia, non è ancora concesso in licenza commerciale. Ecco qui link

È un framework probabilistico per .NET sviluppato la mia ricerca Microsoft. Hanno tipi .NET per le distribuzioni di Bernoulli, Beta, Gamma, Gaussian, Poisson e probabilmente alcuni altri che ho lasciato fuori.

Potrebbe realizzare ciò che vuoi. Grazie.

Questa è la mia semplice implementazione ispirata a Box Muller. È possibile aumentare la risoluzione in base alle proprie esigenze. Anche se questo funziona alla grande per me, si tratta di un'approssimazione di portata limitata, quindi tieni presente che le code sono chiuse e finite, ma sicuramente puoi espanderle secondo necessità.

    //
    // by Dan
    // islandTraderFX
    // copyright 2015
    // Siesta Key, FL
    //    
// 0.0  3231 ********************************
// 0.1  1981 *******************
// 0.2  1411 **************
// 0.3  1048 **********
// 0.4  810 ********
// 0.5  573 *****
// 0.6  464 ****
// 0.7  262 **
// 0.8  161 *
// 0.9  59 
//Total: 10000

double g()
{
   double res = 1000000;
   return random.Next(0, (int)(res * random.NextDouble()) + 1) / res;
}

public static class RandomProvider
{
   public static int seed = Environment.TickCount;

   private static ThreadLocal<Random> randomWrapper = new ThreadLocal<Random>(() =>
       new Random(Interlocked.Increment(ref seed))
   );

   public static Random GetThreadRandom()
   {
       return randomWrapper.Value;
   }
} 

Non credo ci sia. E spero davvero di no, dato che il framework è già abbastanza gonfio, senza che tale funzionalità specializzata lo riempia ancora di più.

Dai un'occhiata a http://www.extremeoptimization.com/Statistics /UsersGuide/ContinuousDistributions/NormalDistribution.aspx e http: //www.vbforums .com / showthread.php? t = 488959 per soluzioni .NET di terzi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top