伪随机数发生器 - 指数分布

时间:2010-01-21 02:23:50

标签: algorithm language-agnostic random exponential-distribution

我想生成一些伪随机数,直到现在我一直非常满意.Net库的Random.Next(int min, int max)函数。这个品种的PRNG 假设使用Uniform distribution,但我非常希望使用Exponential Distribution生成一些数字。

我正在用C#编程,虽然我会接受伪代码或C ++,Java等。

任何建议/代码片段/算法/想法?

9 个答案:

答案 0 :(得分:96)

由于您可以访问统一的随机数生成器,因此使用inversion method可以轻松生成随您知道的CDF分发的随机数。

因此,在u中生成一个统一的随机数[0,1),然后通过以下方式计算x

x = log(1-u)/( - {λ{1}},

其中λ是指数分布的速率参数。现在,)是一个具有指数分布的随机数。请注意,上面的xlog,即自然对数。

答案 1 :(得分:13)

采样的基本定理认为,如果你可以归一化,整合和反转所需的分布,你就可以免费回家了。

如果您在F(x)上标准化了所需的分发[a,b]。你计算

C(y) = \int_a^y F(x) dx

将其反转为C^{-1},将z统一投掷到[0,1)并找到

x_i = C^{-1}(z_i)

将具有所需的分布。


在您的情况下:F(x) = ke^{-kx}我会假设您需要[0,infinity]。我们得到:

C(y) = 1 - e^{-ky}

可以转换为

x = -1/k  ln(1 - z)

for z在[0,1)上统一抛出。


但是,坦率地说,使用经过良好调试的库比较聪明,除非你为自己的启发做这件事。

答案 2 :(得分:12)

这是我在维基百科上找到的公式:

T = -Ln(u) / λ

我们创建一个在 [0,1] 中具有均匀分布 (u) 的随机数,我们得到 x :

随机 R = new Random();

double u = R.NextDouble();

double x = -Math.Log(u)/(λ);

答案 3 :(得分:6)

如果您想要好的随机数,请考虑链接到gsl例程:http://www.gnu.org/software/gsl/。他们有例程gsl_ran_exponential。如果你想使用在[0,1)上具有均匀分布的内置生成器生成随机数(例如u = Random.Next(0,N-1)/ N,对于某些大N),那么只需使用:

-mu * log (1-u)

请参阅gsl源代码中的randist / exponential.c。

编辑:只是为了与后来的一些答案进行比较 - 这与mu = 1 / lambda相当。 mu这里是分布的均值,也称为OP链接到的维基百科页面上的scale参数,lambda是rate参数。

答案 4 :(得分:4)

指数分布的一个有趣特性:考虑具有指数到达间隔时间的到达过程。在任何时间段(t1,t2)和该时期内的到达时间。这些到达者是在t1和t2之间分配的UNIFORMLY。 (Sheldon Ross,Stochastic Processes)。

如果我有一个伪随机数生成器,并且由于某种原因(例如我的软件无法计算日志)你不想进行上述转换,但想要一个指数r.v.平均值为1.0。

你可以:

1)创建1001个U(0,1)随机变量。

2)按顺序排序

3)从第一个减去第二个,从第二个减去第三个,......得到1000个差异。

4)这些差异是具有均值= 1.0的分布的指数RV。

我认为效率较低,但却是达到同样目的的手段。

答案 5 :(得分:1)

开源Uncommons Maths library by Dan Dyer为Java提供随机数生成器,概率分布,组合和统计。

在其他有价值的课程中,ExponentialGenerator基本上实现了@Alok Singhal解释的想法。在its tutorial blog中,代码片段用于模拟平均每分钟发生10次的随机事件:

final long oneMinute = 60000;
Random rng = new MersenneTwisterRNG();

// Generate events at an average rate of 10 per minute.
ExponentialGenerator gen = new ExponentialGenerator(10, rng);
boolean running = true;
while (true)
{
    long interval = Math.round(gen.nextValue() * oneMinute);
    Thread.sleep(interval);

    // Fire event here.
}

当然,如果你更喜欢时间单位per second(而不是a minute),你只需要设置final long oneMinute = 1000

深入研究nextValue()方法ExponentialGenerator的{​​{3}},您会发现{{3}中描述的所谓逆变换采样 }:

public Double nextValue()
{
    double u;
    do
    {
        // Get a uniformly-distributed random double between
        // zero (inclusive) and 1 (exclusive)
        u = rng.nextDouble();
    } while (u == 0d); // Reject zero, u must be positive for this to work.
    return (-Math.log(u)) / rate.nextValue();
}  

P.S。:最近我使用的是Uncommons Maths库。谢谢Dan Dyer。

答案 6 :(得分:0)

如果我理解你的问题,并且你可以接受有限数量的PRNG,你可以遵循以下方法:

  • 创建一个数组,其中每个元素都在指数分布中
  • 生成PRNG,它是数组的整数索引。返回该索引处数组中的元素。

答案 7 :(得分:0)

还有另一种生成指数(rate)随机数的方法,尽管它不像现在使用对数那样方便。它来自 John von Neumann (1951) 的算法,仅使用比较。

  1. scale1/rate。将 highpart 设置为 0。
  2. 生成一个uniform(0, scale)随机数(如NextDouble()*scale),称之为u
  3. val 设置为 u,并将 accept 设置为 1。
  4. 生成一个uniform(0, scale)随机数,称之为v
  5. 如果u大于v,将u设置为v,然后将accept设置为1减去accept,然后转到第 4 步。
  6. 如果接受为 1,则返回 val + highpart
  7. scale 添加到 highpart 并转到第 2 步。

参考:

  • Von Neumann, J.,“与随机数字相关的各种技术”,1951 年。

答案 8 :(得分:-2)

这是我在面对类似要求时所使用的:

// sorry.. pseudocode, mine was in Tcl:

int weighted_random (int max) {
    float random_number = rand();
    return floor(max - ceil( max * random_number * random_number))
}

当然,这是对随机数求平方的公式,因此您沿着二次曲线生成一个随机数。