我实现了这个函数来生成一个泊松随机变量
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左右饱和。是由于数值近似还是我犯了错误?
答案 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';
}
我上面用过两种方式:
我试图模仿您现有的界面。
如果使用均值创建std :: poisson_distribution,则使用该分布对于相同的均值更有效(如在main()中所做的那样)。
以下是我的示例输出:
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]
。