ตัวแปร Gaussian แบบสุ่ม


118

มีคลาสในไลบรารีมาตรฐานของ. NET ที่ให้ฟังก์ชันการสร้างตัวแปรสุ่มตามการแจกแจงแบบเกาส์หรือไม่


http://mathworld.wolfram.com/Box-MullerTransformation.htmlการใช้ตัวแปรสุ่มสองตัวคุณสามารถสร้างค่าสุ่มตามการแจกแจงแบบเกาส์เซียน ไม่ใช่งานยากเลย
Jarrett Meyer

1
ฉันแค่ต้องการเพิ่มผลลัพธ์ทางคณิตศาสตร์ซึ่งไม่มีประโยชน์ในทันทีสำหรับการแจกแจงปกติ (เนื่องจาก CDF ที่ซับซ้อน) แต่มีประโยชน์สำหรับการแจกแจงอื่น ๆ อีกมากมาย หากคุณใส่ตัวเลขสุ่มที่กระจายอย่างสม่ำเสมอใน [0,1] (ด้วยRandom.NextDouble()) ลงในผกผันของ CDF ของการแจกแจงใด ๆ คุณจะได้ตัวเลขสุ่มตามการแจกแจงนั้น หากแอปพลิเคชันของคุณไม่ต้องการตัวแปรที่กระจายตามปกติอย่างแม่นยำ Logistic Distribution เป็นการประมาณที่ใกล้เคียงกับปกติมากและมี CDF ที่กลับหัวได้อย่างง่ายดาย
Ozzah

1
แพคเกจ MedallionRandom NuGetมีวิธีขยายสำหรับการเรียกค่าปกติกระจายจากRandomการใช้กล่อง-Muller เปลี่ยน (กล่าวถึงในหลายคำตอบด้านล่าง)
ChaseMedallion

คำตอบ:


182

คำแนะนำของ Jarrett ในการใช้การแปลงร่าง Box-Muller นั้นดีสำหรับการแก้ปัญหาที่รวดเร็วและสกปรก การใช้งานง่ายๆ:

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)

3
ฉันทดสอบและเปรียบเทียบกับ Mersenne Twister RNG ของ MathNet และ NormalDistribution เวอร์ชันของคุณเร็วกว่าสองเท่าและผลลัพธ์สุดท้ายก็เหมือนกัน (การตรวจสอบ "ระฆัง" ด้วยภาพ)
Johann Gerell

4
@Johann หากคุณกำลังมองหาความเร็วที่แท้จริงอัลกอริทึม Zigoratมักได้รับการยอมรับว่าเป็นแนวทางที่เร็วที่สุด นอกจากนี้วิธีการข้างต้นยังสามารถทำได้เร็วขึ้นโดยใช้ค่าจากการเรียกหนึ่งไปยังการเรียกถัดไป
Drew Noakes

สวัสดีstdDevค่าตัวแปรควรตั้งเป็นอะไร? ฉันเข้าใจว่าสิ่งนี้สามารถกำหนดค่าตามข้อกำหนดเฉพาะได้ แต่มีขอบเขต (เช่นค่าสูงสุด / นาที) หรือไม่
hofnarwillie

@hofnarwillie stdDev คือพารามิเตอร์มาตราส่วนของการแจกแจงปกติซึ่งอาจเป็นจำนวนบวกก็ได้ ยิ่งมีขนาดใหญ่เท่าใดตัวเลขที่สร้างก็จะกระจายออกไปมากขึ้นเท่านั้น สำหรับพารามิเตอร์การแจกแจงปกติมาตรฐานให้ใช้ค่าเฉลี่ย = 0 และ stdDev = 1
yoyoyoyosef

1
@ แจ็คฉันไม่คิดอย่างนั้น มีเพียง -2 * Math.Log (u1) เท่านั้นที่อยู่ภายใน sqrt และบันทึกจะเป็นลบหรือศูนย์เสมอตั้งแต่ u1 <= 1
yoyoyoyosef

63

คำถามนี้ดูเหมือนจะย้ายไปอยู่ด้านบนของ Google สำหรับรุ่น. NET Gaussian ดังนั้นฉันคิดว่าฉันจะโพสต์คำตอบ

ฉันได้สร้างวิธีการขยายบางอย่างสำหรับคลาส. NET Randomรวมถึงการใช้งานการแปลง Box-Muller เนื่องจากเป็นส่วนขยายตราบใดที่รวมโปรเจ็กต์ไว้ (หรือคุณอ้างอิง DLL ที่คอมไพล์แล้ว) คุณก็ยังสามารถทำได้

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

หวังว่าไม่มีใครสนใจปลั๊กไร้ยางอาย

ตัวอย่างฮิสโตแกรมของผลลัพธ์ (รวมแอปสาธิตสำหรับการวาดภาพ):

ใส่คำอธิบายภาพที่นี่


ชั้นเรียนส่วนขยายของคุณมีบางสิ่งที่ฉันกำลังมองหา! ขอบคุณ!
Thomas

1
คุณมีจุดบกพร่องเล็กน้อยในวิธีการ NextGaussian NextDouble () ส่งคืนตัวเลขทศนิยมแบบสุ่มที่มากกว่าหรือเท่ากับ 0.0 และน้อยกว่า 1.0 ดังนั้นคุณควรมี u1 = 1.0 - NextDouble () .... บันทึกอื่น ๆ (0) จะระเบิด
Mitch Wheat

21

Math.NETมีฟังก์ชันนี้ วิธีการมีดังนี้

double mean = 100;
double stdDev = 10;

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

คุณสามารถค้นหาเอกสารได้ที่นี่: http://numerics.mathdotnet.com/api/MathNet.Numerics.Distributions/Normal.htm


ตอบโจทย์มาก! ฟังก์ชั่นนี้สามารถใช้ได้บน NuGet ในแพคเกจ MathNet.Numerics ดีเสมอที่จะไม่ต้องม้วนของคุณเอง
jpmc26

8

ฉันสร้างคำขอสำหรับคุณลักษณะดังกล่าวบน Microsoft Connect หากนี่คือสิ่งที่คุณกำลังมองหาโปรดลงคะแนนให้และเพิ่มการแสดงผล

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

คุณลักษณะนี้รวมอยู่ใน Java SDK การใช้งานมีให้เป็นส่วนหนึ่งของเอกสารและสามารถย้ายไปยัง C # หรือภาษา. NET อื่น ๆ ได้อย่างง่ายดาย

หากคุณกำลังมองหาความเร็วที่แท้จริงอัลกอริทึม Zigoratมักได้รับการยอมรับว่าเป็นแนวทางที่เร็วที่สุด

ฉันไม่ใช่ผู้เชี่ยวชาญในหัวข้อนี้ - ฉันพบความต้องการนี้ในขณะที่ใช้ตัวกรองอนุภาคสำหรับไลบรารีฟุตบอลหุ่นยนต์จำลอง RoboCup 3Dของฉันและรู้สึกประหลาดใจเมื่อสิ่งนี้ไม่รวมอยู่ในเฟรมเวิร์ก


ในขณะเดียวกันนี่คือเสื้อคลุมสำหรับRandomการใช้งานวิธีการขั้วโลกของ 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;
    }
}

ฉันได้รับค่า -ve จากมัน ใครช่วยตรวจสอบสิ่งผิดปกติได้ไหม
mk7

@ mk7 ฟังก์ชันความน่าจะเป็นแบบเกาส์ที่มีศูนย์กลางอยู่ที่ศูนย์มีแนวโน้มที่จะให้ค่าลบเช่นเดียวกับการให้ค่าบวก
Drew Noakes

คุณถูก! เนื่องจากฉันต้องการรับรายการน้ำหนักในประชากรทั่วไปด้วยไฟล์ Gaussian PDF ฉันจึงตั้งค่า mu เป็น 75 [in kg] และ sigma เป็น 10 ฉันจำเป็นต้องตั้งค่า GaussianRandom ใหม่สำหรับการสร้าง ทุกน้ำหนักสุ่ม?
mk7

คุณสามารถวาดตัวอย่างจากอินสแตนซ์เดียวได้
Drew Noakes

5

Math.NET Iridiumยังอ้างว่าใช้ "เครื่องกำเนิดไฟฟ้าแบบสุ่มที่ไม่สม่ำเสมอ (ปกติ, ปัวซอง, ทวินาม, ... )"


BUt ทำงานไม่ถูกต้อง พยายามที่จะพล็อตมันให้หมายเลขสุ่มสม่ำเสมอ
Nikhil Chilwant

4

นี่เป็นอีกวิธีที่รวดเร็วและสกปรกสำหรับการสร้างตัวแปรสุ่มที่มีปกติกระจาย มันดึงจุดสุ่ม (x, y) และตรวจสอบว่าจุดนี้อยู่ใต้เส้นโค้งของฟังก์ชันความหนาแน่นของความน่าจะเป็นของคุณหรือไม่หรือทำซ้ำ

โบนัส: คุณสามารถสร้างตัวแปรสุ่มสำหรับการแจกแจงอื่น ๆ (เช่นการแจกแจงเลขชี้กำลังหรือการแจกแจงแบบปัวซอง ) เพียงแค่แทนที่ฟังก์ชันความหนาแน่น

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

สำคัญ: เลือกช่วงเวลาของyและพารามิเตอร์σและμเพื่อไม่ให้เส้นโค้งของฟังก์ชันตัดที่จุดสูงสุด / ต่ำสุด (เช่นที่ x = ค่าเฉลี่ย) ให้คิดว่าช่วงเวลาของxและyเป็นกรอบล้อมรอบซึ่งเส้นโค้งต้องพอดี


4
Tangenial แต่นี่เป็นครั้งแรกที่ฉันรู้ว่าคุณสามารถใช้สัญลักษณ์ Unicode สำหรับตัวแปรแทนที่จะเป็นอะไรที่โง่ ๆ เช่น _sigma หรือ _phi ...
Slothario

@Slothario ฉันขอขอบคุณนักพัฒนาทุกที่ที่ใช้ 'อะไรโง่ ๆ ': |
user2864740

2

ฉันต้องการขยายคำตอบของ @ yoyoyoyosef โดยทำให้เร็วขึ้นและเขียนคลาสกระดาษห่อ ค่าใช้จ่ายที่เกิดขึ้นอาจไม่ได้หมายความว่าเร็วขึ้นสองเท่า แต่ฉันคิดว่ามันน่าจะเร็วขึ้นเกือบสองเท่า แม้ว่าจะไม่ปลอดภัยต่อเธรด

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

2

การขยายคำตอบของ @Noakes และ @ Hameer ออกไปฉันได้ใช้คลาส 'Gaussian' ด้วย แต่เพื่อลดความซับซ้อนของพื้นที่หน่วยความจำฉันจึงทำให้มันเป็นลูกของคลาส Random เพื่อให้คุณสามารถเรียก Basic Next (), NextDouble () ฯลฯ จากคลาส Gaussian ด้วยโดยไม่ต้องสร้างวัตถุ Random เพิ่มเติมเพื่อจัดการกับมัน ฉันยังตัดคุณสมบัติคลาสส่วนกลาง _available และ _nextgauss เนื่องจากฉันไม่เห็นว่าจำเป็นเนื่องจากคลาสนี้อิงตามอินสแตนซ์จึงควรเป็นเธรดที่ปลอดภัยหากคุณให้แต่ละเธรดเป็นอ็อบเจกต์เกาส์ของตัวเอง ฉันยังย้ายตัวแปรที่จัดสรรรันไทม์ทั้งหมดออกจากฟังก์ชันและทำให้เป็นคุณสมบัติคลาสซึ่งจะลดจำนวนการเรียกไปยังตัวจัดการหน่วยความจำเนื่องจากในทางทฤษฎีแล้ว 4 คู่ไม่ควรถูกยกเลิกการจัดสรรจนกว่าอ็อบเจ็กต์จะถูกทำลาย

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

ตัวแปรท้องถิ่นเป็นเพื่อนด้วย
user2864740

1

การขยายคำตอบของ Drew Noakes หากคุณต้องการประสิทธิภาพที่ดีกว่า Box-Muller (เร็วขึ้นประมาณ 50-75%) Colin Green ได้แบ่งปันการใช้งานอัลกอริทึม Ziggurat ใน C # ซึ่งคุณสามารถพบได้ที่นี่:

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

Ziggurat ใช้ตารางการค้นหาเพื่อจัดการกับค่าที่อยู่ห่างจากเส้นโค้งมากพอสมควรซึ่งจะยอมรับหรือปฏิเสธอย่างรวดเร็ว ประมาณ 2.5% ของเวลาจะต้องทำการคำนวณเพิ่มเติมเพื่อพิจารณาว่าตัวเลขอยู่ด้านใดของเส้นโค้ง


0

คุณสามารถลองใช้ Infer.NET ยังไม่ได้รับอนุญาตทางการค้า นี่คือลิงค์

เป็นกรอบความน่าจะเป็นสำหรับ. NET ที่พัฒนางานวิจัย Microsoft ของฉัน พวกเขามีประเภท. NET สำหรับการแจกแจงของ Bernoulli, Beta, Gamma, Gaussian, Poisson และอาจมีบางอย่างที่ฉันทิ้งไว้

มันอาจจะสำเร็จในสิ่งที่คุณต้องการ ขอบคุณ


0

นี่คือการนำ Box Muller มาใช้อย่างง่าย ๆ ของฉัน คุณสามารถเพิ่มความละเอียดให้เหมาะกับความต้องการของคุณ แม้ว่าจะใช้งานได้ดีสำหรับฉัน แต่นี่เป็นการประมาณแบบ จำกัด ดังนั้นโปรดจำไว้ว่าหางจะปิดและ จำกัด แต่แน่นอนว่าคุณสามารถขยายได้ตามต้องการ

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

นี่คือการนำ Box Muller มาใช้อย่างง่าย ๆ ของฉัน คุณสามารถเพิ่มความละเอียดให้เหมาะกับความต้องการของคุณ สิ่งนี้เร็วมากง่ายและใช้ได้กับแอปเครือข่ายประสาทของฉันซึ่งต้องการฟังก์ชันความหนาแน่นของความน่าจะเป็นแบบเกาส์เซียนโดยประมาณเพื่อให้งานสำเร็จลุล่วง หวังว่าจะช่วยให้ใครบางคนประหยัดเวลาและรอบ CPU แม้ว่าจะใช้งานได้ดีสำหรับฉัน แต่นี่เป็นการประมาณแบบ จำกัด ดังนั้นโปรดจำไว้ว่าหางจะปิดและ จำกัด แต่แน่นอนว่าคุณสามารถขยายได้ตามต้องการ
Daniel Howard

1
เฮ้แดเนียลฉันได้แนะนำการแก้ไขที่รวมคำอธิบายจากความคิดเห็นของคุณไว้ในคำตอบ นอกจากนี้ยังลบ "//" ที่แสดงความคิดเห็นของรหัสจริงในคำตอบของคุณ คุณสามารถแก้ไขเองได้หากต้องการ / หากถูกปฏิเสธ :)
mbrig

-1

ฉันไม่คิดว่าจะมี และฉันหวังเป็นอย่างยิ่งว่าจะไม่มีเนื่องจากเฟรมเวิร์กมีการขยายตัวเพียงพอแล้วหากไม่มีฟังก์ชันพิเศษดังกล่าวเติมเต็มให้มากขึ้น

ลองดูที่http://www.extremeoptimization.com/Statistics/UsersGuide/ContinuousDistributions/NormalDistribution.aspxและhttp://www.vbforums.com/showthread.php?t=488959สำหรับโซลูชัน. NET ของบุคคลที่สาม


7
การแจกแจงแบบเกาส์เซียนเป็นแบบ 'เฉพาะทาง' ตั้งแต่เมื่อไร? มันเป็นเรื่องทั่วไปมากกว่า AJAX หรือ DataTables
TraumaPony

@TraumaPony: คุณพยายามแนะนำให้นักพัฒนาใช้การกระจายแบบเกาส์มากกว่าใช้ AJAX เป็นประจำอย่างจริงจังหรือไม่?
David Arno

3
อาจเป็นไปได้; สิ่งที่ฉันพูดคือมันเชี่ยวชาญกว่ามาก มีแอปพลิเคชันเว็บสำหรับใช้งานเพียงรายการเดียว การแจกแจงแบบเสียนมีการใช้งานที่ไม่เกี่ยวข้องกันจำนวนมากอย่างไม่น่าเชื่อ
TraumaPony

@DavidArno คุณแนะนำอย่างจริงจังเกี่ยวกับฟังก์ชันการทำงานที่น้อยลงเพื่อปรับปรุงกรอบงาน
Jodrell

1
@Jodrell เพื่ออ้างถึงตัวอย่างที่เฉพาะเจาะจงฉันคิดว่าการตัดสินใจสร้าง MVC เป็นเฟรมเวิร์กที่แยกจากกันแทนที่จะเป็นส่วนหนึ่งของเฟรมเวิร์ก. NET หลักเป็นสิ่งที่ดี
David Arno
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.