使用非均匀分布生成随机数

11

Math.random()返回一个带有“均匀”分布的伪随机数。

我需要生成一个在[0,1]范围内的随机数,该随机数偏向于两侧中的一侧。 (即更有可能获得靠近0或靠近1的数字)

理想情况下,我希望有一个参数来设置这个曲线。

我可以使用Math.random^2来获得此结果,但还有什么更复杂的方法可以实现这一点?


根据下面的回答,泊松分布不是[0,1]。它们是[0,无穷大)。那么你想要哪个? - djechlin
+1,这是一个很好的问题,需要非平凡的解决方案。 - Nishanth
@djechlin 技术上说,泊松随机变量的支持范围为{0,…,∞},这对于考虑从基于[0,∞)空间上的速率分布生成泊松随机变量的泊松过程中取样很重要。如果您考虑整个空间,您将得到一个泊松随机变量事件数,其平均值为∞,传统上被视为期望泊松随机变量=∞(w.p.=1)的有效边缘情况。 - mpacer
4个回答

17

我认为你想要 alpha=beta=0.5的beta分布

通过反向累积分布可以将均匀随机数转换为beta分布。

unif = Math.random()

我不熟悉javascript,但这应该很清楚:

beta = sin(unif*pi/2)^2

PS:您可以生成许多这样的数字并绘制直方图。 enter image description here

编辑:

为了偏向0,将beta值进行变换 -

beta_left = (beta < 0.5) ? 2*beta : 2*(1-beta);

在此输入图片描述

将偏斜度调整为1,转换方式为 -

beta_right = (beta > 0.5) ? 2*beta-1 : 2*(1-beta)-1;

在此输入图片描述


有趣!我会尝试一下。感谢图表说明。如果我希望分布只偏向一侧,我该怎么做? - OpherV
1
有一种方法可以使用反向CDF方法将均匀分布映射到任何分布。我会更新我的答案,将已有的数字转换为右/左偏斜。 - Nishanth

4

我刚想到了一种更简单的方法来获取偏向于每个方面的随机数字,并且不含任何依赖关系。

这种方法使用了Javascript中的两个常规随机数。第一个数与自身相乘(指数越大,偏斜效果越大),而第二个数选择了要偏斜到分布的哪一侧。

function skewedRandom() {
    const a = Math.pow(Math.random(), 2);
    if (Math.random() < 0.5) {
        return a;
    }
    return 1 - a;
}

在这段代码中,指数被设置为2。以下是使用指数为2进行10,000次执行生成的样本直方图: 指数为2的直方图 当指数为3时: 指数为3的直方图 指数为10时: 指数为10的直方图

1
我注意到这个方法可能存在问题。Math.random() 生成的数字从0(包括)到1(不包括)。然而,这个方法可以生成从0到1(包括)的数字。如果这对你来说是个问题,一个可能的解决方法是将最后一行改为 return 1 - Number.EPSILON - a;。虽然我不确定这是否会产生略小于0的值。 - tremby

3

我认为你需要重新思考你的问题。泊松分布是一种计数分布,它是以速率为基础来指定的,例如在每个时间段内平均看到多少次某件事情的发生。它产生正整数,因此结果不能仅在[0,1]范围内。请问你能否澄清你想要什么?

无论如何,要生成一个速率为lambda的泊松分布,一种算法是:

threshold = Math.exp(-lambda)
count = 0
product = 1.0
while (product *= rand) >= threshold {
      count += 1
}
return count

这里的"rand"是调用Uniform(0,1)函数。我不懂JavaScript,但你应该能够很容易地实现它。

针对修改后的问题作出回应:

有几种分布会在有限范围内生成结果,但其中许多并不适合新手,比如Johnson家族或Beta分布。

一个简单的方法是三角形分布。Sqrt(rand)将产生一个向1聚集的三角形分布,而(1-Sqrt(1-rand))将产生一个向零聚集的三角形分布。

更普遍的模式(最频繁值)位于m处的三角形分布(其中0 <= m <= 1)可以通过以下方式生成:

if rand <= m
    return m * Sqrt(rand)
else
    return 1 - ((1 - m) * Sqrt(1 - rand))

请注意,每次调用rand函数都会生成一个独立的均匀分布随机数。如果您只生成一个rand值并在整个程序中使用它,则不会得到正确的结果。

你说得完全正确。我一直在反过来思考这个问题 :) 谢谢你纠正了我(并指引了我正确的方向!)。无论如何,算法方面的知识很有用。 - OpherV

2
您可以在可用的情况下使用 window.crypto.getRandomValues
<div id="result"></div>

var randVal = new Uint8Array(1);

window.crypto.getRandomValues(randVal);

document.getElementById("result").textContent = randVal[0] / 255;

jsfiddle 上(如果这是你所询问的,我不确定),或者可以像这样:
<div id="result"></div>

function poissonRandomNumber(lambda) {
    var L = Math.exp(-lambda),
        k = 0,
        p = 1;

    do {
        k = k + 1;
        p = p * Math.random();
    } while (p > L);

    return k - 1;
}

document.getElementById("result").textContent = poissonRandomNumber(100);

还在jsfiddle


2
"crypto.getRandomValues()" 适用于需要防篡改或加密安全的数字。 "Math.random()" 在其他情况下都可以,当速度很重要时,例如游戏中(但不适用于身份验证)。 - Solomon Ucko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接