既然您拥有一种均匀随机数生成器,那么使用反演法生成其他分布的随机数就很容易了,只要你知道它们的累积分布函数。
首先,在 [0,1)
范围内生成一个均匀随机数 u
,然后通过以下公式计算 x
:
x = log(1-u)/(-λ)
x = log(1-uniformRand(0, 1))/(-λ)
其中,λ
是指数分布的速率参数。现在,x
是具有指数分布的随机数。请注意,上述的log
指的是自然对数(即ln
)。
[0,1)
中生成数字,因此某些人可能会意外尝试进行log([0,1))/(-λ)
的计算。这可能非常罕见地创建+infinity
,即如果随机数生成器生成0
(由于[0,1)
)。我认为这种情况发生的几率非常非常小,但不是零。此外,分布可能会稍微有些错误。通过使用1-[0,1)
,可以避免这个错误。如果没有这个,我们需要一个if-then来检查0
,这将增加运行时间。无论如何,你的评论是正确的。 - Thomas Weise抽样的基本定理是:如果你能够对所需的分布进行归一化、积分和反演,那么你就可以轻松获得所需的结果。
如果你有一个定义在区间[a,b]
上的已归一化的所需分布F(x)
,则计算:
C(y) = \int_a^y F(x) dx
反转它以获取C^{-1}
,在[0,1)上均匀分布地抛出z
并找到
x_i = C^{-1}(z_i)
对于你的情况:F(x) = ke^{-kx}
,我将假设你想要[0,无穷大]
范围内的分布。我们得到:
C(y) = 1 - e^{-ky}
可逆以得到
x = -1/k ln(1 - z)
如果你不是出于个人兴趣,使用一个经过充分调试的库会更明智。
-mu * log (1-u)
我在维基百科上找到了以下公式:
T = -Ln(u) / λ
我们使用均匀分布的随机数(u)在[0,1]之间生成一个随机数,并得到x:
Random R = new Random();
double u = R.NextDouble();
double x = -Math.Log(u)/(λ);
指数分布的一个有趣属性:考虑到一个到达过程,其到达间隔时间服从指数分布。取任意一段时间(t1,t2)和该时段内的到达次数。这些到达次数在t1和t2之间是均匀分布的。(Sheldon Ross, 随机过程)
如果我有一个伪随机数生成器,并且由于某种原因(例如,我的软件无法计算对数),您不想执行上述转换,但想要具有平均值为1.0的指数随机变量。
你可以:
1)创建1001个U(0,1)的随机变量。
2)按顺序排序
3)从第一个中减去第二个,从第二个中减去第三个,...得到1000个差异。
4)这些差异是来自平均值为1.0的分布的指数随机变量。
我认为效率更低,但达到相同目的的方法。
ExponentialGenerator
本质上实现了@Alok Singhal解释的想法。在它的教程博客中,给出了一个代码片段来模拟平均每分钟发生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.
}
final long oneMinute = 1000
。ExponentialGenerator
方法nextValue()
的源代码,您会发现所谓的反变换抽样,描述在生成指数变量[维基百科]中: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();
}
还有一种生成指数分布(rate
)随机变量的方法,虽然现在使用对数更加方便。这个算法来自John von Neumann (1951),只使用比较。
scale
为1/rate
。将highpart
设置为0。scale
)内生成均匀分布的随机变量,称为u
。val
设置为u
,并将accept
设置为1。scale
)内生成均匀分布的随机变量,称为v
。u
大于v
,则将u
设置为v
,然后将accept
设置为1减去accept
,然后转到第4步。val + highpart
。scale
添加到highpart
,并转到第2步。参考文献:
如果我理解你的问题,并且你可以接受有限数量的伪随机数生成器(PRNG),那么你可以采用以下方法:
在面对类似要求时,这是我使用的方法:
// sorry.. pseudocode, mine was in Tcl:
int weighted_random (int max) {
float random_number = rand();
return floor(max - ceil( max * random_number * random_number))
}
当然,这是平方随机数的公式,因此您正在沿着二次曲线生成随机数。