在c ++中生成泊松变量

时间:2011-04-14 02:18:10

标签: c++ probability knuth poisson

我实现了这个函数来生成一个泊松随机变量

typedef long unsigned int luint;
luint poisson(luint lambda) {
    double L = exp(-double(lambda));
    luint k = 0;
    double p = 1;
    do {
        k++;
        p *= mrand.rand();
    } while( p > L);
    return (k-1);
}

其中mrand是MersenneTwister随机数生成器。我发现,当我增加lambda时,预期的分布将是错误的,其平均值在750左右饱和。是由于数值近似还是我犯了错误?

5 个答案:

答案 0 :(得分:2)

exp(-750)是一个非常小的数字,非常接近可能的最小双数,因此您的问题是数字。在任何情况下,你的复杂性在lambda中都是线性的,因此对于高lambda,算法效率不高。除非你有充分的理由自己编写代码,否则使用现有的库实现可能是有意义的,因为这些数值算法对于你遇到的精度问题往往是敏感的。

答案 1 :(得分:2)

由于您只在表达式L中使用(p>L),因此您实际上是在测试(log(p) > -lambda)。这不是一个非常有用的转变。当然,你不再需要exp(-750)了,但你只需要溢出p

现在,p只是Π(mrand.rand()),而log(p)是log(Π(mrand.rand()))是Σ(log(mrand.rand())。这为您提供了必要的转变:

double logp = 0;
do {
    k++;
    logp += log(mrand.rand());
} while( logp > -lambda);

double只有11位指数,但是52位尾数。因此,这是数值稳定性的大幅增加。支付的价格是每次迭代需要log,而不是预先单exp

答案 2 :(得分:2)

如果你去“现有的库”路由,你的编译器可能已经支持C ++ 11 std :: random包。以下是您使用它的方式:

#include <random>
#include <ctime>
#include <iostream>

std::mt19937 mrand(std::time(0));  // seed however you want

typedef long unsigned int luint;

luint poisson(luint lambda)
{
    std::poisson_distribution<luint> d(lambda);
    return d(mrand);
}

int main()
{
    std::cout << poisson(750) << '\n';
    std::poisson_distribution<luint> d(750);
    std::cout << d(mrand) << '\n';
    std::cout << d(mrand) << '\n';
}

我上面用过两种方式:

  1. 我试图模仿您现有的界面。

  2. 如果使用均值创建std :: poisson_distribution,则使用该分布对于相同的均值更有效(如在main()中所做的那样)。

  3. 以下是我的示例输出:

    751
    730
    779
    

答案 3 :(得分:1)

another question I asked earlier开始,您似乎也可以将poisson(750)视为poisson(375) + poisson(375)

答案 4 :(得分:0)

在这种情况下,您不需要多次调用随机数生成器。您所需要的只是一个累积概率表:

double c[k] = // the probability that X <= k (k = 0,...)

然后生成一个随机数0 <= r < 1,并获取X的第一个整数c[X] > r。您可以使用二进制搜索找到此X

要生成此表,我们需要个别概率

p[k] = lambda^k / (k! e^lambda) // // the probability that X = k

如果lambda很大,这就变得非常不准确,正如您所发现的那样。但我们可以在这里使用一个技巧:使用k = floor[lambda]从最大值开始(或接近),并假装p[k]等于1。然后使用递归关系计算p[i] i > k

p[i+1] = (p[i]*lambda) / (i+1)

i < k使用

p[i-1] = (p[i]*i)/lambda

这可确保最大概率具有最大可能的精度。

现在只需使用c[i]计算c[i+1] = c[i] + p[i+1],直至c[i+1]c[i]相同。然后,您可以通过除以此限制值c[i]来规范化数组;或者您可以保持数组不变,并使用随机数0 <= r < c[i]

请参阅:http://en.wikipedia.org/wiki/Inverse_transform_sampling