如何从 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 交互式演示您可能也想查看的功能。