Biến Gaussian ngẫu nhiên


118

Có lớp nào trong thư viện chuẩn của .NET cung cấp cho tôi chức năng tạo các biến ngẫu nhiên tuân theo phân phối Gaussian không?


http://mathworld.wolfram.com/Box-MullerTransformation.html Sử dụng hai biến ngẫu nhiên, bạn có thể tạo các giá trị ngẫu nhiên dọc theo phân phối Gaussian. Đó không phải là một nhiệm vụ khó khăn.
Jarrett Meyer

1
Tôi chỉ muốn thêm một kết quả toán học không hữu ích ngay cho các bản phân phối Bình thường (do CDF phức tạp), nhưng hữu ích cho nhiều bản phân phối khác. Nếu bạn đặt các số ngẫu nhiên có phân phối đồng đều trong [0,1] (với Random.NextDouble()) vào nghịch đảo của CDF của phân phối BẤT KỲ, bạn sẽ nhận được các số ngẫu nhiên tuân theo phân phối ĐÓ. Nếu ứng dụng của bạn không cần các biến được phân phối chuẩn chính xác, thì Phân phối Logistic là một giá trị gần đúng với bình thường và có CDF dễ dàng đảo ngược.
Ozzah

1
Gói MedallionRandom NuGet chứa một phương thức mở rộng để lấy các giá trị được phân phối chuẩn từ một Randombiến đổi Box-Muller (được đề cập trong một số câu trả lời bên dưới).
ChaseMedallion

Câu trả lời:


181

Đề xuất của Jarrett về việc sử dụng phép biến hình Box-Muller là một giải pháp tốt và nhanh chóng. Cách thực hiện đơn giản:

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
Tôi đã thử nghiệm nó và so sánh với Mersenne Twister RNG và NormalDistribution của MathNet. Phiên bản của bạn nhanh hơn gấp đôi và kết quả cuối cùng về cơ bản là giống nhau (kiểm tra trực quan "chuông").
Johann Gerell

4
@Johann, nếu bạn đang tìm kiếm tốc độ thuần túy, thì Thuật toán Zigorat thường được công nhận là cách tiếp cận nhanh nhất. Hơn nữa, cách tiếp cận trên có thể được thực hiện nhanh hơn bằng cách mang một giá trị từ cuộc gọi này sang lệnh gọi tiếp theo.
Drew Noakes

Xin chào, stdDevbiến nên được đặt là gì? Tôi hiểu rằng điều này có thể được định cấu hình theo các yêu cầu cụ thể, nhưng có bất kỳ giới hạn nào không (nghĩa là giá trị tối đa / tối thiểu)?
hofnarwillie

@hofnarwillie stdDev là tham số tỷ lệ của phân phối chuẩn, có thể là bất kỳ số dương nào. Nó càng lớn, các số được tạo ra sẽ càng bị phân tán. Đối với phân phối chuẩn chuẩn, sử dụng các tham số mean = 0 và stdDev = 1.
yoyoyoyosef

1
@Jack Tôi không nghĩ vậy. Chỉ -2 * Math.log (u1) là bên trong sqrt, và các bản ghi sẽ luôn là số âm hoặc không có từ u1 <= 1
yoyoyoyosef

62

Câu hỏi này dường như đã chuyển lên đầu Google cho thế hệ .NET Gaussian, vì vậy tôi nghĩ rằng tôi sẽ đăng một câu trả lời.

Tôi đã tạo một số phương thức mở rộng cho lớp .NET Random , bao gồm cả việc triển khai biến đổi Box-Muller. Vì chúng là phần mở rộng, nên miễn là dự án được bao gồm (hoặc bạn tham chiếu đến DLL đã biên dịch), bạn vẫn có thể

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

Hy vọng không ai bận tâm đến cái phích cắm vô liêm sỉ.

Biểu đồ kết quả mẫu (bao gồm một ứng dụng demo để vẽ cái này):

nhập mô tả hình ảnh ở đây


Lớp học mở rộng của bạn có một số thứ mà tôi đang tìm kiếm! cảm ơn!
Thomas

1
bạn có một lỗi nhỏ trong phương pháp NextGaussian của mình. NextDouble () Trả về một số dấu phẩy động ngẫu nhiên lớn hơn hoặc bằng 0,0 và nhỏ hơn 1,0. Vì vậy, bạn nên có u1 = 1,0 - nextDouble () .... nhật ký khác (0) sẽ thổi lên
Mitch Wheat

21

Math.NET cung cấp chức năng này. Đây là cách thực hiện:

double mean = 100;
double stdDev = 10;

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

Bạn có thể tìm tài liệu tại đây: http://numerics.mathdotnet.com/api/MathNet.Numerics.Distributions/Normal.htm


Câu trả lời chính xác! Chức năng này có sẵn trên NuGet trong gói MathNet.Numerics . Luôn luôn tuyệt vời để không phải cuộn của riêng bạn.
jpmc26

8

Tôi đã tạo một yêu cầu cho một tính năng như vậy trên Microsoft Connect. Nếu đây là thứ bạn đang tìm kiếm, vui lòng bỏ phiếu cho nó và tăng khả năng hiển thị của nó.

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

Tính năng này được bao gồm trong Java SDK. Việc triển khai nó có sẵn như một phần của tài liệu và dễ dàng chuyển sang C # hoặc các ngôn ngữ .NET khác.

Nếu bạn đang tìm kiếm tốc độ thuần túy, thì Thuật toán Zigorat thường được công nhận là cách tiếp cận nhanh nhất.

Mặc dù vậy, tôi không phải là chuyên gia về chủ đề này - tôi đã bắt gặp nhu cầu này khi triển khai bộ lọc hạt cho thư viện bóng đá mô phỏng RoboCup 3D và rất ngạc nhiên khi điều này không được đưa vào khuôn khổ.


Trong khi đó, đây là một trình bao bọc để Randomcung cấp triển khai hiệu quả phương pháp phân cực 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;
    }
}

Tôi đã nhận được một số giá trị -ve từ nó. ai đó có thể kiểm tra những gì sai?
mk7

@ mk7, một hàm xác suất Gaussian xoay quanh số 0 có khả năng cho giá trị âm cũng như cho giá trị dương.
Drew Noakes

Bạn đúng! Vì tôi muốn có danh sách cân nặng trong một quần thể điển hình bằng PDF gaussian, tôi đang đặt mu thành 75 [tính bằng kg] và sigma thành 10. Tôi có cần đặt một phiên bản mới của GaussianRandom để tạo mọi trọng lượng ngẫu nhiên?
mk7,

Bạn có thể tiếp tục vẽ mẫu từ một phiên bản.
Drew Noakes

5

Math.NET Iridium cũng tuyên bố thực hiện "bộ tạo ngẫu nhiên không đồng nhất (bình thường, poisson, nhị thức, ...)".


BUt nó không hoạt động bình thường. Đã cố gắng âm mưu nó, Đưa ra số ngẫu nhiên đồng nhất.
Nikhil Chilwant

4

Đây là một giải pháp nhanh chóng và hiệu quả khác để tạo ra các biến ngẫu nhiên có phân phối chuẩn . Nó vẽ một số điểm ngẫu nhiên (x, y) và kiểm tra xem điểm này có nằm dưới đường cong của hàm mật độ xác suất của bạn hay không, nếu không thì lặp lại.

Phần thưởng: Bạn có thể tạo các biến ngẫu nhiên cho bất kỳ phân phối nào khác (ví dụ: phân phối hàm mũ hoặc phân phối poisson ) chỉ bằng cách thay thế hàm mật độ.

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

Quan trọng: Chọn khoảng của y và các tham số σμ để đường cong của hàm không bị cắt tại các điểm cực đại / cực tiểu của nó (ví dụ: tại x = mean). Hãy coi các khoảng của xy như một hộp giới hạn, trong đó đường cong phải vừa với.


4
Tangenial, nhưng điều này thực sự là lần đầu tiên tôi đã nhận ra bạn có thể sử dụng biểu tượng Unicode cho các biến thay vì một cái gì đó ngớ ngẩn như _sigma hoặc _phi ...
Slothario

@Slothario Tôi cảm ơn các nhà phát triển ở khắp mọi nơi đã sử dụng 'cái gì đó ngu ngốc': |
user2864740

2

Tôi muốn mở rộng câu trả lời của @ yoyoyoyosef bằng cách làm cho nó nhanh hơn và viết một lớp trình bao bọc. Chi phí phát sinh có thể không nhanh gấp đôi, nhưng tôi nghĩ nó phải nhanh hơn gần gấp đôi. Tuy nhiên, nó không an toàn cho chuỗi.

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

Mở rộng các câu trả lời của @Noakes và @ Hameer, tôi cũng đã triển khai lớp 'Gaussian', nhưng để đơn giản hóa không gian bộ nhớ, tôi đã đặt nó trở thành con của lớp Ngẫu nhiên để bạn cũng có thể gọi cơ bản Next (), NextDouble () , v.v. từ lớp Gaussian mà không cần phải tạo thêm một đối tượng Ngẫu nhiên để xử lý nó. Tôi cũng đã loại bỏ các thuộc tính lớp toàn cục _available và _nextgauss, vì tôi không thấy chúng là cần thiết vì lớp này dựa trên cá thể, nó sẽ an toàn cho luồng, nếu bạn cung cấp cho mỗi luồng đối tượng Gaussian của riêng nó. Tôi cũng đã di chuyển tất cả các biến được cấp phát thời gian chạy ra khỏi hàm và đặt chúng thành thuộc tính lớp, điều này sẽ làm giảm số lượng lệnh gọi tới trình quản lý bộ nhớ vì 4 biến đôi về mặt lý thuyết sẽ không bao giờ được hủy cấp phát cho đến khi đối tượng bị phá hủy.

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

Biến cục bộ cũng là bạn bè.
user2864740

1

Mở rộng câu trả lời của Drew Noakes, nếu bạn cần hiệu suất tốt hơn Box-Muller (nhanh hơn khoảng 50-75%), Colin Green đã chia sẻ cách triển khai thuật toán Ziggurat trong C #, bạn có thể tìm thấy ở đây:

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

Ziggurat sử dụng một bảng tra cứu để xử lý các giá trị nằm đủ xa so với đường cong mà nó sẽ nhanh chóng chấp nhận hoặc từ chối. Khoảng 2,5% thời gian, nó phải tính toán thêm để xác định một số nằm ở phía nào của đường cong.


0

Bạn có thể thử Infer.NET. Tuy nhiên, nó chưa được cấp phép thương mại. Đây là liên kết

Nó là một khuôn khổ xác suất cho .NET đã phát triển nghiên cứu Microsoft của tôi. Họ có các loại .NET cho các bản phân phối Bernoulli, Beta, Gamma, Gaussian, Poisson, và có lẽ tôi đã bỏ qua một số loại khác.

Nó có thể đạt được những gì bạn muốn. Cảm ơn.


0

Đây là cách triển khai lấy cảm hứng từ Box Muller đơn giản của tôi. Bạn có thể tăng độ phân giải để phù hợp với nhu cầu của mình. Mặc dù điều này rất hiệu quả đối với tôi, nhưng đây là một khoảng gần đúng trong phạm vi giới hạn, vì vậy hãy nhớ rằng các phần đuôi là đóng và hữu hạn, nhưng chắc chắn bạn có thể mở rộng chúng khi cần thiết.

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

Đây là cách triển khai lấy cảm hứng từ Box Muller đơn giản của tôi. Bạn có thể tăng độ phân giải để phù hợp với nhu cầu của mình. Điều này rất nhanh, đơn giản và phù hợp với các ứng dụng mạng thần kinh của tôi cần một loại hàm mật độ xác suất Gaussian gần đúng để hoàn thành công việc. Hy vọng nó sẽ giúp ai đó tiết kiệm thời gian và chu kỳ CPU. Mặc dù điều này rất hiệu quả đối với tôi, nhưng đây là một khoảng gần đúng trong phạm vi giới hạn, vì vậy hãy nhớ rằng các đuôi là đóng và hữu hạn, nhưng chắc chắn bạn có thể mở rộng chúng khi cần thiết.
Daniel Howard,

1
Này Daniel, tôi đã đề xuất một chỉnh sửa kết hợp mô tả từ nhận xét của bạn vào chính câu trả lời. Nó cũng loại bỏ '//' đã nhận xét ra mã thực trong câu trả lời của bạn. Bạn có thể làm các chỉnh sửa cho mình nếu bạn muốn / nếu nó bị từ chối :)
mbrig

-1

Tôi không nghĩ là có. Và tôi thực sự hy vọng là không có, vì khung công tác đã đủ cồng kềnh, mà không có chức năng chuyên biệt như vậy sẽ lấp đầy nó hơn nữa.

Tuy nhiên, hãy xem http://www.extremeoptimization.com/Statistics/UsersGuide/ContinuousDistributions/NormalDistribution.aspxhttp://www.vbforums.com/showthread.php?t=488959 để biết các giải pháp .NET của bên thứ ba.


7
Từ khi nào phân phối Gaussian là 'chuyên biệt'? Nó tổng quát hơn nhiều so với AJAX hoặc DataTables.
TraumaPony 20/10/08

@TraumaPony: bạn có đang cố gắng đề xuất nhiều nhà phát triển sử dụng phân phối Gaussian hơn là sử dụng AJAX thường xuyên không?
David Arno

3
Có khả năng; những gì tôi đang nói là nó chuyên biệt hơn nhiều. Nó chỉ có một công dụng - ứng dụng web. Các bản phân phối Gaussian có một số lượng đáng kinh ngạc về việc sử dụng không liên quan.
TraumaPony 21/10/08

@DavidArno, bạn có thực sự đề xuất rằng ít chức năng hơn sẽ cải thiện một khuôn khổ.
Jodrell

1
@Jodrell, để trích dẫn một ví dụ cụ thể, tôi nghĩ quyết định biến MVC thành một khuôn khổ riêng biệt, thay vì một phần của khuôn khổ .NET chính, là một quyết định tốt.
David Arno
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.