Variabel Gaussian Acak

118

Apakah ada kelas di pustaka standar .NET yang memberi saya fungsionalitas untuk membuat variabel acak yang mengikuti distribusi Gaussian?

Sebastian Müller
sumber
http://mathworld.wolfram.com/Box-MullerTransformation.html Menggunakan dua variabel acak, Anda dapat menghasilkan nilai acak sepanjang distribusi Gaussian. Ini sama sekali bukan tugas yang sulit.
Jarrett Meyer
1
Saya hanya ingin menambahkan hasil matematika yang tidak langsung berguna untuk distribusi Normal (karena CDF kompleks), tetapi berguna untuk banyak distribusi lainnya. Jika Anda memasukkan bilangan acak terdistribusi seragam di [0,1] (dengan Random.NextDouble()) ke dalam kebalikan dari CDF distribusi APAPUN, Anda akan mendapatkan bilangan acak yang mengikuti distribusi ITU. Jika aplikasi Anda tidak membutuhkan variabel terdistribusi normal secara tepat, Distribusi Logistik adalah perkiraan yang sangat dekat dengan normal dan memiliki CDF yang mudah dibalik.
Ozzah
1
Paket MedallionRandom NuGet berisi metode ekstensi untuk mengambil nilai terdistribusi normal dari Randommenggunakan transformasi Box-Muller (disebutkan dalam beberapa jawaban di bawah).
ChaseMedallion

Jawaban:

182

Saran Jarrett untuk menggunakan transformasi Box-Muller bagus untuk solusi cepat dan kotor. Implementasi sederhana:

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)
yoyoyoyosef
sumber
3
Saya mengujinya dan dibandingkan dengan Mersenne Twister RNG dan NormalDistribution dari MathNet. Versi Anda lebih dari dua kali lebih cepat dan hasil akhirnya pada dasarnya sama (inspeksi visual dari "lonceng").
Johann Gerell
4
@Johann, jika Anda mencari kecepatan murni, maka Algoritma Zigorat secara umum dikenal sebagai pendekatan tercepat. Selanjutnya pendekatan di atas dapat dibuat lebih cepat dengan membawa nilai dari satu panggilan ke panggilan berikutnya.
Drew Noakes
Hai, stdDevvariabel mana yang harus disetel? Saya memahami bahwa ini dapat dikonfigurasi untuk persyaratan tertentu, tetapi apakah ada batasan (yaitu nilai maks / min)?
hofnarwillie
@hofnarwillie stdDev adalah parameter skala dari distribusi normal, yang dapat berupa bilangan positif apapun. Semakin besar nilainya, semakin banyak pula angka yang dihasilkan. Untuk distribusi normal standar menggunakan parameter mean = 0 dan stdDev = 1.
yoyoyosef
1
@ Saya tidak berpikir begitu. Hanya matematika -2 *. Log (u1) yang berada di dalam akar kuadrat, dan log akan selalu negatif atau nol karena u1 <= 1
yoyoyosef
63

Pertanyaan ini tampaknya telah dipindahkan ke atas Google untuk generasi Gaussian .NET, jadi saya pikir saya akan memposting jawaban.

Saya telah membuat beberapa metode ekstensi untuk kelas .NET Random , termasuk implementasi transformasi Box-Muller. Karena mereka adalah ekstensi, selama proyek disertakan (atau Anda mereferensikan DLL yang dikompilasi), Anda masih dapat melakukannya

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

Semoga tidak ada yang keberatan dengan steker yang tidak tahu malu.

Contoh histogram hasil (aplikasi demo untuk menggambar ini disertakan):

masukkan deskripsi gambar di sini

Hebat
sumber
Kelas ekstensi Anda memiliki beberapa hal yang saya cari! Terima kasih!
Thomas
1
Anda memiliki bug kecil dalam metode NextGaussian Anda. NextDouble () Mengembalikan angka floating-point acak yang lebih besar dari atau sama dengan 0,0, dan kurang dari 1,0. Jadi Anda harus memiliki u1 = 1.0 - NextDouble () .... log lain (0) akan meledak
Mitch Wheat
21

Math.NET menyediakan fungsionalitas ini. Begini caranya:

double mean = 100;
double stdDev = 10;

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

Anda dapat menemukan dokumentasinya di sini: http://numerics.mathdotnet.com/api/MathNet.Numerics.Distributions/Normal.htm

Gordon Slysz
sumber
Jawaban yang bagus! Fungsi ini tersedia di NuGet dalam paket MathNet.Numerics . Selalu hebat untuk tidak harus menggulung sendiri.
jpmc26
8

Saya membuat permintaan untuk fitur seperti itu di Microsoft Connect. Jika ini adalah sesuatu yang Anda cari, silakan pilih dan tingkatkan visibilitasnya.

https://connect.microsoft.com/VisualStudio/feedback/details/634346/guassian-normal-distribution-random-numbers

Fitur ini termasuk dalam Java SDK. Implementasinya tersedia sebagai bagian dari dokumentasi dan dengan mudah diporting ke C # atau bahasa .NET lainnya.

Jika Anda mencari kecepatan murni, maka Algoritma Zigorat secara umum dikenal sebagai pendekatan tercepat.

Saya bukan ahli dalam topik ini - saya menemukan kebutuhan untuk ini saat menerapkan filter partikel untuk pustaka robot sepak bola simulasi RoboCup 3D saya dan terkejut ketika ini tidak termasuk dalam kerangka kerja.


Sementara itu, berikut adalah pembungkusnya Randomyang menyediakan implementasi yang efisien dari metode kutub 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;
    }
}
Drew Noakes
sumber
Saya mendapat beberapa nilai darinya. bisakah seseorang memeriksa apa yang salah?
mk7
@ mk7, fungsi probabilitas Gaussian yang berpusat di sekitar nol kemungkinan besar memberikan nilai negatif seperti halnya memberikan nilai positif.
Drew Noakes
Kamu benar! Karena saya ingin mendapatkan daftar bobot dalam populasi tipikal dengan gaussian PDF, saya menyetel mu menjadi, katakanlah, 75 [dalam kg] dan sigma menjadi 10. Apakah saya perlu menyetel contoh baru GaussianRandom untuk menghasilkan setiap bobot acak?
mk7
Anda dapat terus menggambar sampel dari satu contoh.
Drew Noakes
5

Math.NET Iridium juga mengklaim menerapkan "generator acak tidak seragam (normal, poisson, binomial, ...)".

Jason DeFontes
sumber
Tapi itu tidak berfungsi dengan baik. Mencoba untuk merencanakannya, Memberikan no acak seragam.
Nikhil Chilwant
4

Berikut adalah solusi cepat dan kotor lainnya untuk menghasilkan variabel acak yang terdistribusi normal . Ini menarik beberapa titik acak (x, y) dan memeriksa apakah titik ini terletak di bawah kurva fungsi kepadatan probabilitas Anda, jika tidak ulangi.

Bonus: Anda dapat membuat variabel acak untuk distribusi lain (misalnya distribusi eksponensial atau distribusi poisson ) hanya dengan mengganti fungsi kerapatan.

    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 * σ * σ));
    }

Penting: Pilih interval y dan parameter σ dan μ sehingga kurva fungsi tidak terpotong pada titik maksimum / minimumnya (misalnya pada x = mean). Pikirkan interval x dan y sebagai kotak pembatas, di mana kurva harus pas.

Doomjunky
sumber
4
Tangenial, tetapi ini sebenarnya pertama kalinya saya menyadari Anda dapat menggunakan simbol Unicode untuk variabel alih-alih sesuatu yang bodoh seperti _sigma atau _phi ...
Slothario
@Slothario Saya berterima kasih kepada developer di mana pun karena telah menggunakan 'sesuatu yang bodoh': |
pengguna2864740
2

Saya ingin memperluas jawaban @ yoyoyosef dengan membuatnya lebih cepat, dan menulis kelas pembungkus. Overhead yang dikeluarkan mungkin tidak berarti dua kali lebih cepat, tapi menurut saya seharusnya hampir dua kali lebih cepat. Ini tidak aman untuk benang.

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();
    }
}
Hameer Abbasi
sumber
2

Memperluas jawaban @Noakes dan @ Hameer, saya juga telah menerapkan kelas 'Gaussian', tetapi untuk menyederhanakan ruang memori, saya menjadikannya anak dari kelas Random sehingga Anda juga dapat memanggil dasar Next (), NextDouble () , dll dari kelas Gaussian juga tanpa harus membuat objek Random tambahan untuk menanganinya. Saya juga menghilangkan properti kelas global _available, dan _nextgauss, karena saya tidak melihatnya seperlunya karena kelas ini berbasis instance, seharusnya aman untuk thread, jika Anda memberikan objek Gaussiannya sendiri pada setiap thread. Saya juga memindahkan semua variabel yang dialokasikan waktu proses keluar dari fungsi dan menjadikannya properti kelas, ini akan mengurangi jumlah panggilan ke manajer memori karena 4 ganda secara teoritis tidak boleh dialokasikan sampai objek dihancurkan.

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));
    }
}

sumber
Variabel lokal adalah teman juga.
pengguna2864740
1

Memperluas jawaban Drew Noakes, jika Anda membutuhkan kinerja yang lebih baik daripada Box-Muller (sekitar 50-75% lebih cepat), Colin Green telah membagikan implementasi algoritma Ziggurat di C #, yang dapat Anda temukan di sini:

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

Ziggurat menggunakan tabel pencarian untuk menangani nilai yang jatuh cukup jauh dari kurva, yang akan dengan cepat menerima atau menolaknya. Sekitar 2,5% dari waktu, itu harus melakukan perhitungan lebih lanjut untuk menentukan sisi kurva mana sebuah angka berada.

Neil
sumber
0

Anda dapat mencoba Infer.NET. Ini belum berlisensi komersial. Di sini ada tautan

Ini adalah kerangka kerja probabilistik untuk .NET mengembangkan penelitian Microsoft saya. Mereka memiliki tipe .NET untuk distribusi Bernoulli, Beta, Gamma, Gaussian, Poisson, dan mungkin beberapa lagi yang saya tinggalkan.

Itu mungkin mencapai apa yang Anda inginkan. Terima kasih.

Aaron Stainback
sumber
0

Ini adalah implementasi sederhana yang terinspirasi dari Box Muller. Anda dapat meningkatkan resolusi agar sesuai dengan kebutuhan Anda. Meskipun ini berfungsi dengan baik untuk saya, ini adalah perkiraan rentang terbatas, jadi ingatlah bahwa ekornya tertutup dan terbatas, tetapi tentu saja Anda dapat mengembangkannya sesuai kebutuhan.

    //
    // 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;
   }
} 
Daniel Howard
sumber
Ini adalah implementasi sederhana yang terinspirasi dari Box Muller. Anda dapat meningkatkan resolusi agar sesuai dengan kebutuhan Anda. Ini sangat cepat, sederhana, dan berfungsi untuk aplikasi jaringan neural saya yang memerlukan perkiraan jenis fungsi kepadatan probabilitas jenis Gaussian untuk menyelesaikan pekerjaan. Semoga dapat membantu seseorang menghemat waktu dan siklus CPU. Meskipun ini berfungsi dengan baik untuk saya, ini adalah perkiraan rentang terbatas, jadi ingatlah bahwa ekornya tertutup dan terbatas, tetapi tentu saja Anda dapat mengembangkannya sesuai kebutuhan.
Daniel Howard
1
Hai Daniel, saya telah menyarankan pengeditan yang menggabungkan deskripsi dari komentar Anda ke dalam jawaban itu sendiri. Ini juga menghapus '//' yang mengomentari kode sebenarnya dalam jawaban Anda. Anda dapat mengeditnya sendiri jika Anda ingin / jika ditolak :)
mbrig
-1

Saya rasa tidak ada. Dan saya sangat berharap tidak ada, karena kerangka kerjanya sudah cukup membengkak, tanpa fungsionalitas khusus seperti itu yang mengisinya lebih banyak lagi.

Lihatlah http://www.extremeoptimization.com/Statistics/UsersGuide/ContinuousDistributions/NormalDistribution.aspx dan http://www.vbforums.com/showthread.php?t=488959 untuk solusi pihak ketiga .NET.

David Arno
sumber
7
Sejak kapan distribusi Gaussian 'terspesialisasi'? Ini jauh lebih umum daripada, katakanlah, AJAX atau DataTables.
TraumaPony
@TraumaPony: apakah Anda serius mencoba menyarankan lebih banyak pengembang menggunakan distribusi Gaussian daripada menggunakan AJAX secara teratur?
David Arno
3
Mungkin; apa yang saya katakan adalah bahwa itu jauh lebih terspesialisasi. Ini hanya memiliki satu aplikasi web penggunaan. Distribusi Gaussian memiliki banyak sekali kegunaan yang tidak terkait.
TraumaPony
@DavidArno, apakah Anda benar-benar menyarankan agar lebih sedikit fungsionalitas yang meningkatkan kerangka kerja.
Jodrell
1
@ Jodrell, untuk mengutip contoh spesifik, saya pikir keputusan untuk membuat MVC kerangka kerja terpisah, daripada bagian dari kerangka .NET utama, adalah keputusan yang bagus.
David Arno