简体   繁体   English

在 C# 中实现 Box-Mueller 随机数生成器

[英]Implementing Box-Mueller random number generator in C#

From this question: Random number generator which gravitates numbers to any given number in range?这个问题:随机数生成器将数字吸引到范围内的任何给定数字? I did some research since I've come across such a random number generator before.因为我以前遇到过这样的随机数生成器,所以我做了一些研究。 All I remember was the name "Mueller", so I guess I found it, here:我只记得名字“Mueller”,所以我想我在这里找到了它:

I can find numerous implementations of it in other languages, but I can't seem to implement it correctly in C#.我可以在其他语言中找到许多它的实现,但我似乎无法在 C# 中正确实现它。

This page, for instance, The Box-Muller Method for Generating Gaussian Random Numbers says that the code should look like this (this is not C#):这个页面,例如,用于生成高斯随机数的 Box-Muller 方法说代码应该是这样的(这不是 C#):

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

double gaussian(void)
{
   static double v, fac;
   static int phase = 0;
   double S, Z, U1, U2, u;

   if (phase)
      Z = v * fac;
   else
   {
      do
      {
         U1 = (double)rand() / RAND_MAX;
         U2 = (double)rand() / RAND_MAX;

         u = 2. * U1 - 1.;
         v = 2. * U2 - 1.;
         S = u * u + v * v;
      } while (S >= 1);

      fac = sqrt (-2. * log(S) / S);
      Z = u * fac;
   }

   phase = 1 - phase;

   return Z;
}

Now, here's my implementation of the above in C#.现在,这是我在 C# 中的上述实现。 Note that the transform produces 2 numbers, hence the trick with the "phase" above.请注意,转换会产生 2 个数字,因此是上面“相位”的技巧。 I simply discard the second value and return the first.我只是丢弃第二个值并返回第一个值。

public static double NextGaussianDouble(this Random r)
{
    double u, v, S;

    do
    {
        u = 2.0 * r.NextDouble() - 1.0;
        v = 2.0 * r.NextDouble() - 1.0;
        S = u * u + v * v;
    }
    while (S >= 1.0);

    double fac = Math.Sqrt(-2.0 * Math.Log(S) / S);
    return u * fac;
}

My question is with the following specific scenario, where my code doesn't return a value in the range of 0-1, and I can't understand how the original code can either.我的问题是针对以下特定场景,其中我的代码没有返回 0-1 范围内的值,我也无法理解原始代码是如何做到的。

  • u = 0.5, v = 0.1 u = 0.5,v = 0.1
  • S becomes 0.5*0.5 + 0.1*0.1 = 0.26 S 变为0.5*0.5 + 0.1*0.1 = 0.26
  • fac becomes ~ 3.22 fac 变为 ~ 3.22
  • the return value is thus ~ 0.5 * 3.22 or ~ 1.6因此,返回值为 ~ 0.5 * 3.22或 ~ 1.6

That's not within 0.. 1 .那不在0.. 1之内。

What am I doing wrong/not understanding?我在做什么错/不理解?

If I modify my code so that instead of multiplying fac with u , I multiply by S , I get a value that ranges from 0 to 1, but it has the wrong distribution (seems to have a maximum distribution around 0.7-0.8 and then tapers off in both directions.)如果我修改我的代码,而不是将facu相乘,而是乘以S ,我得到一个范围从 0 到 1 的值,但它的分布错误(似乎最大分布在 0.7-0.8 左右,然后逐渐变细双向关闭。)

Your code is fine.你的代码很好。 Your mistake is thinking that it should return values exclusively within [0, 1] .您的错误是认为它应该仅在[0, 1]内返回值。 The (standard) normal distribution is a distribution with nonzero weight on the entire real line. (标准)正态分布是在整条实线上具有非零权重的分布。 That is, values outside of [0, 1] are possible.也就是说, [0, 1]之外的值是可能的。 In fact, values within [-1, 0] are just as likely as values within [0, 1] , and moreover, the complement of [0, 1] has about 66% of the weight of the normal distribution.实际上, [-1, 0]内的值与[0, 1]内的值一样可能,而且[0, 1]的补集具有正态分布权重的约 66%。 Therefore, 66% of the time we expect a value outside of [0, 1] .因此,66% 的时间我们期望[0, 1]之外的值。

Also, I think this is not the Box-Mueller transform, but is actually the Marsaglia polar method.另外,我认为这不是 Box-Mueller 变换,而实际上是 Marsaglia 极坐标法。

I am no mathematician, or statistician, but if I think about this I would not expect a Gaussian distribution to return numbers in an exact range.我不是数学家或统计学家,但如果我考虑到这一点,我不会期望高斯分布返回精确范围内的数字。 Given your implementation the mean is 0 and the standard deviation is 1 so I would expect values distributed on the bell curve with 0 at the center and then reducing as the numbers deviate from 0 on either side.鉴于您的实现,平均值为 0,标准差为 1,因此我希望值分布在钟形曲线上,中心为 0,然后随着数字在两侧偏离 0 而减小。 So the sequence would definitely cover both +/- numbers.所以这个序列肯定会涵盖两个 +/- 数字。

Then since it is statistical, why would it be hard limited to -1..1 just because the std.dev is 1?那么既然是统计的,为什么仅仅因为std.dev是1就硬限制在-1..1呢? There can statistically be some play on either side and still fulfill the statistical requirement.任何一方都可以在统计上进行一些比赛,但仍能满足统计要求。

The uniform random variate is indeed within 0..1, but the gaussian random variate (which is what Box-Muller algorithm generates) can be anywhere on the real line.均匀随机变量确实在 0..1 以内,但高斯随机变量(这是 Box-Muller 算法生成的)可以在实线上的任何位置。 See wiki/NormalDistribution for details.有关详细信息,请参阅wiki/NormalDistribution

I think the function returns polar coordinates.我认为 function 返回极坐标。 So you need both values to get correct results.因此,您需要这两个值才能获得正确的结果。

Also, Gaussian distribution is not between 0.. 1 .此外,高斯分布不在0.. 1之间。 It can easily end up as 1000, but probability of such occurrence is extremely low.它可以很容易地结束为 1000,但这种情况发生的概率极低。

This is a monte carlo method so you can't clamp the result, but what you can do is ignore samples.这是一种蒙特卡罗方法,因此您无法限制结果,但您可以做的是忽略样本。

// return random value in the range [0,1].
double gaussian_random()
{
    double sigma = 1.0/8.0; // or whatever works.
    while ( 1 ) {
        double z = gaussian() * sigma + 0.5;
        if (z >= 0.0 && z <= 1.0)
            return z;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM