如何从 JavaScript 中均值和标准差的截断正态分布中获取随机值?
How to get a random value from truncated normal distribution with mean and std in JavaScript?
相同 Python 实现的 JS 替代方案是什么?
import matplotlib.pyplot as plt
from scipy.stats import truncnorm
import numpy as np
mean = 1
std = 2
clip_a = -4
clip_b = 3
a, b = (clip_a - mean) / std, (clip_b - mean) / std
x_range = np.linspace(-3 * std, 3 * std, 1000)
plt.plot(x_range, truncnorm.pdf(x_range, a, b, loc = mean, scale = std));
我想根据分布得到一个随机值(在 JS 中与 size=1
相同的代码):
dist = truncnorm.rvs(a, b, loc = mean, scale = std, size=1000000)
plt.hist(dist);
这是一个实现截断的 skew-normal 伪随机数生成器 (PRNG) 的 JS 函数。它基于 Tom Liao 的 this blog post,并已扩展为考虑下限和上限(截断)。
本质上,该函数被递归调用,直到找到所需范围内的变量。
您可以使用 rng
属性 传递您自己的随机数生成器,但默认情况下将使用 Math.random
。此外,由于您没有要求 skew-normal 分布,您可以忽略偏斜 属性,因为它默认为 0。这将使您得到截断的正常 PRNG,正如您所要求的那样。
function randomTruncSkewNormal({
rng = Math.random,
range = [-Infinity, Infinity],
mean,
stdDev,
skew = 0
}) {
// Box-Muller transform
function randomNormals(rng) {
let u1 = 0,
u2 = 0;
//Convert [0,1) to (0,1)
while (u1 === 0) u1 = rng();
while (u2 === 0) u2 = rng();
const R = Math.sqrt(-2.0 * Math.log(u1));
const Θ = 2.0 * Math.PI * u2;
return [R * Math.cos(Θ), R * Math.sin(Θ)];
}
// Skew-normal transform
// If a variate is either below or above the desired range,
// we recursively call the randomSkewNormal function until
// a value within the desired range is drawn
function randomSkewNormal(rng, mean, stdDev, skew = 0) {
const [u0, v] = randomNormals(rng);
if (skew === 0) {
const value = mean + stdDev * u0;
if (value < range[0] || value > range[1])
return randomSkewNormal(rng, mean, stdDev, skew);
return value;
}
const sig = skew / Math.sqrt(1 + skew * skew);
const u1 = sig * u0 + Math.sqrt(1 - sig * sig) * v;
const z = u0 >= 0 ? u1 : -u1;
const value = mean + stdDev * z;
if (value < range[0] || value > range[1])
return randomSkewNormal(rng, mean, stdDev, skew);
return value;
}
return randomSkewNormal(rng, mean, stdDev, skew);
}
按以下方式调用此函数
const data = [];
for (let i = 0; i < 50000; i++) {
data.push({
x: i,
y: randomTruncSkewNormal({
range: [-4,3],
mean: 1,
stdDev: 2
})
});
}
并使用您选择的图表库绘制数据应该会为您提供所需的输出。
我还制作了一个 small Observable notebook 交互式演示您可能也想查看的功能。
相同 Python 实现的 JS 替代方案是什么?
import matplotlib.pyplot as plt
from scipy.stats import truncnorm
import numpy as np
mean = 1
std = 2
clip_a = -4
clip_b = 3
a, b = (clip_a - mean) / std, (clip_b - mean) / std
x_range = np.linspace(-3 * std, 3 * std, 1000)
plt.plot(x_range, truncnorm.pdf(x_range, a, b, loc = mean, scale = std));
我想根据分布得到一个随机值(在 JS 中与 size=1
相同的代码):
dist = truncnorm.rvs(a, b, loc = mean, scale = std, size=1000000)
plt.hist(dist);
这是一个实现截断的 skew-normal 伪随机数生成器 (PRNG) 的 JS 函数。它基于 Tom Liao 的 this blog post,并已扩展为考虑下限和上限(截断)。
本质上,该函数被递归调用,直到找到所需范围内的变量。
您可以使用 rng
属性 传递您自己的随机数生成器,但默认情况下将使用 Math.random
。此外,由于您没有要求 skew-normal 分布,您可以忽略偏斜 属性,因为它默认为 0。这将使您得到截断的正常 PRNG,正如您所要求的那样。
function randomTruncSkewNormal({
rng = Math.random,
range = [-Infinity, Infinity],
mean,
stdDev,
skew = 0
}) {
// Box-Muller transform
function randomNormals(rng) {
let u1 = 0,
u2 = 0;
//Convert [0,1) to (0,1)
while (u1 === 0) u1 = rng();
while (u2 === 0) u2 = rng();
const R = Math.sqrt(-2.0 * Math.log(u1));
const Θ = 2.0 * Math.PI * u2;
return [R * Math.cos(Θ), R * Math.sin(Θ)];
}
// Skew-normal transform
// If a variate is either below or above the desired range,
// we recursively call the randomSkewNormal function until
// a value within the desired range is drawn
function randomSkewNormal(rng, mean, stdDev, skew = 0) {
const [u0, v] = randomNormals(rng);
if (skew === 0) {
const value = mean + stdDev * u0;
if (value < range[0] || value > range[1])
return randomSkewNormal(rng, mean, stdDev, skew);
return value;
}
const sig = skew / Math.sqrt(1 + skew * skew);
const u1 = sig * u0 + Math.sqrt(1 - sig * sig) * v;
const z = u0 >= 0 ? u1 : -u1;
const value = mean + stdDev * z;
if (value < range[0] || value > range[1])
return randomSkewNormal(rng, mean, stdDev, skew);
return value;
}
return randomSkewNormal(rng, mean, stdDev, skew);
}
按以下方式调用此函数
const data = [];
for (let i = 0; i < 50000; i++) {
data.push({
x: i,
y: randomTruncSkewNormal({
range: [-4,3],
mean: 1,
stdDev: 2
})
});
}
并使用您选择的图表库绘制数据应该会为您提供所需的输出。
我还制作了一个 small Observable notebook 交互式演示您可能也想查看的功能。