JavaScript Math.random正态分布(高斯钟形曲线)?

Man*_*oxx 47 javascript random algorithm distribution gaussian

我想知道JavaScript函数是否Math.random使用普通(与统一)分布.

如果没有,我如何获得使用正态分布的数字?我没有在互联网上找到一个明确的答案,一个算法来创建随机的正态分布数字.

我想重建施密特机器(德国物理学家).机器产生0或1的随机数,它们必须是正态分布的,这样我才能将它们绘制成高斯钟形曲线.

例如,随机函数产生120个数字(0或1),并且这些求和值的平均值(平均值)必须接近60.

Max*_*ard 107

由于这是我经验中"js高斯随机"的第一个Google结果,我觉得有义务给出该查询的实际答案.

所述盒-缪勒变换转换两个独立的均匀个变量上(0,1)分成两个标准高斯分布随机数(平均值为0,方差为1).这可能是不很高性能的,因为的sqrt,logcos呼叫,但这种方法是优于中心极限定理方法(求和Ñ均匀个变量),因为它不限制输出到有限范围(-N/2, N/2).这也很简单:

// Standard Normal variate using Box-Muller transform.
function randn_bm() {
    var u = 0, v = 0;
    while(u === 0) u = Math.random(); //Converting [0,1) to (0,1)
    while(v === 0) v = Math.random();
    return Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v );
}
Run Code Online (Sandbox Code Playgroud)

  • 为什么不直接设置 `u = 1 - Math.random()` 并忘记 while 循环条件呢? (5认同)
  • @Indra 是的,这不是问题。事实上,我们可以忘记“v”的 while 循环,并以性能稍高的单行代码编写该函数。`返回 Math.sqrt(-2 * Math.log(1 - Math.random())) * Math.cos(2 * Math.PI * Math.random())` (5认同)
  • @Dorian并不是完全出乎意料的,因为正常变量超出该范围的概率大约是千分之一:`$ python3`; `>>>将scipy.stats导入为stats`; `>>> 1 - (stats.norm.cdf(3.24) - stats.norm.cdf(-3.10))`; `0.0015652517111527375` (3认同)
  • 在1000个样本上,范围是`[ - 3.10,3.24]`https://gist.github.com/Dorian/919f9ad749f28e5b57e655919d7eebb9 (2认同)
  • @Dorian:谢谢,我最终根据我的程序的需要使用了一个近似高斯函数,该函数的范围为`[0, 1]`:http://stackoverflow.com/a/39187274/407213。 (2认同)
  • @Artod可能只是乘以均值并除以方差 (2认同)

jos*_*ell 60

0到1之间的正态分布

在麦克斯韦答案的基础上,此代码使用Box-Muller变换为您提供0到1之间的正态分布.它只是重新取样值,如果它超过3.6标准偏差(小于0.02%的几率).

function randn_bm() {
    var u = 0, v = 0;
    while(u === 0) u = Math.random(); //Converting [0,1) to (0,1)
    while(v === 0) v = Math.random();
    let num = Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v );
    num = num / 10.0 + 0.5; // Translate to 0 -> 1
    if (num > 1 || num < 0) return randn_bm(); // resample between 0 and 1
    return num;
}
Run Code Online (Sandbox Code Playgroud)

可视化

在此输入图像描述

n = 100

在此输入图像描述

n = 10,000

在此输入图像描述

n = 10,000,000

具有最小,最大,偏斜的正态分布

此版本允许您提供最小值,最大值和倾斜因子.请参阅底部的我的用法示例.

function randn_bm(min, max, skew) {
    var u = 0, v = 0;
    while(u === 0) u = Math.random(); //Converting [0,1) to (0,1)
    while(v === 0) v = Math.random();
    let num = Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v );

    num = num / 10.0 + 0.5; // Translate to 0 -> 1
    if (num > 1 || num < 0) num = randn_bm(min, max, skew); // resample between 0 and 1 if out of range
    num = Math.pow(num, skew); // Skew
    num *= max - min; // Stretch to fill range
    num += min; // offset to min
    return num;
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

randn_bm(-500, 1000, 1);
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

randn_bm(10, 20, 0.25);
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

randn_bm(10, 20, 3);
Run Code Online (Sandbox Code Playgroud)

  • 偏斜因子到底代表什么?它似乎并不代表标准差或方差。我如何从数学上理解它? (2认同)
  • @André我承认,我的答案中的倾斜因子背后没有太多数学支持。这只是为我的用例拉伸或收缩数据的一种便捷方法。如果您想使用适当的均值、标准差和形状变量来实现合法的偏斜正态变换,您可以查看此链接。https://spin.atomicobject.com/2019/09/30/skew-normal-prng-javascript/ (2认同)
  • 很好的答案,但是我在您的代码中发现了一个小错误: ` if (num &gt; 1 || num &lt; 0) num = randn_bm(min, max, skew); // 如果超出范围,则在 0 和 1 之间重新采样 ` ...应该是: ` if (num &gt; 1 || num &lt; 0) { num = randn_bm(min, max, skew); // 如果超出范围,则在 0 和 1 之间重新采样 } else { ... } return num; 也就是说,如果不重新采样,则仅缩放最终数字。 (2认同)

kod*_*vin 44

我想知道JavaScript函数Math.random是否是正态分布

使用Javascript的Math.random 不是一个正态分布(高斯钟形曲线).来自 ES 2015,20.2.2.27 "使用依赖于实现的算法或策略,返回具有正号,大于或等于0但小于1的数字值,随机或伪随机选择,在该范围内具有近似均匀分布.函数不带参数." 因此,当n足够高时,所提供的集合将得到近似均匀的分布.区间中的所有值都具有相同的出现概率(与x轴平行的直线,表示介于0.0和1.0之间的数字).

如何获得正态分布的数字

有几种方法可以收集正态分布的数字.正如Maxwell Collard所回答的那样,Box-Muller变换 确实将均匀分布转换为正态分布(代码可以在Maxwell Collard答案中找到).

另一个计算器回答一个答案的问题与其他均匀分布正态分布算法的答复.如:Ziggurat,制服比率,反转CDF除了其中一个答案之外:说:

Ziggurat算法对此非常有效,尽管Box-Muller变换更容易从头开始实现(而不是疯狂慢).

最后

我想重建施密特机器(德国物理学家),机器产生0或1的随机数,它们必须是正态分布的,所以我可以用高斯钟形曲线绘制它们.

当我们只有两个值(0或1)时,高斯曲线看起来与具有2个可能值的均匀分布相同.这就是为什么简单

function randomZero_One(){
    return Math.round(Math.random());
}
Run Code Online (Sandbox Code Playgroud)

就够了 它将以大致相等的概率值0和1伪随机返回.

  • 如果您想知道这些赞成票的来源,请大家提醒一下.这个答案在reddit线程中提到,它引发了元效应.https://www.reddit.com/r/ProgrammerHumor/comments/a3ngy7/stackoverflow_is_a_weird_place/ (21认同)
  • ......我在Reddit上发表了评论.现在这个答案有积极的选票,我为自己所做的事感到自豪. (2认同)
  • 所有这些答案,除了少数几个之外,都没有意识到**随机分布不分布在0和1之间**,但在某些情况下可以获得任意高的值。这是一种极其混乱的情况。 (2认同)

Dor*_*ian 40

我希望在0和1之间有大约高斯随机数,经过多次测试后我发现这是最好的:

function gaussianRand() {
  var rand = 0;

  for (var i = 0; i < 6; i += 1) {
    rand += Math.random();
  }

  return rand / 6;
}
Run Code Online (Sandbox Code Playgroud)

作为奖励:

function gaussianRandom(start, end) {
  return Math.floor(start + gaussianRand() * (end - start + 1));
}
Run Code Online (Sandbox Code Playgroud)

  • 值得一提的是,这是(中心极限定理)[https://en.wikipedia.org/wiki/Central_limit_theorem]的实现,是样本大小的"6".与高斯曲线相比,它特权中心数字和忘记尾数.这是我的一个实现:http://plnkr.co/edit/jaky1FHCGpt81vs5Lohz?p = preview; (6认同)
  • 这实际上是一个非常好的简单解决方案.增加因子(6)会增加分布. (4认同)

小智 17

Javascript Math.random()伪随机函数返回在0和1之间均匀分布的变量.为了获得高斯分布,我使用:

// returns a gaussian random function with the given mean and stdev.
function gaussian(mean, stdev) {
    var y2;
    var use_last = false;
    return function() {
        var y1;
        if(use_last) {
           y1 = y2;
           use_last = false;
        }
        else {
            var x1, x2, w;
            do {
                 x1 = 2.0 * Math.random() - 1.0;
                 x2 = 2.0 * Math.random() - 1.0;
                 w  = x1 * x1 + x2 * x2;               
            } while( w >= 1.0);
            w = Math.sqrt((-2.0 * Math.log(w))/w);
            y1 = x1 * w;
            y2 = x2 * w;
            use_last = true;
       }

       var retval = mean + stdev * y1;
       if(retval > 0) 
           return retval;
       return -retval;
   }
}

// make a standard gaussian variable.     
var standard = gaussian(100, 15);

// make a bunch of standard variates
for(i=0; i<2000; i++) {
    console.log(standard());
}
Run Code Online (Sandbox Code Playgroud)

我想我是从Knuth得到的.

  • 这是[Marsaglia极地方法.](https://en.wikipedia.org/wiki/Marsaglia_polar_method) (5认同)
  • 你能解释返回函数最后3行背后的原因吗?为什么`retval`必须是积极的?如果平均值是0,怎么办?然后我会期待正面和负面的样本. (5认同)
  • @克里斯K。你是对的:由于最后 3 行代码,“gaussian”函数返回的值始终为正,因此它不能是高斯分布的随机值。 (2认同)
  • 我认为 Knuth 没有 `if(retval &gt; 0) return retval; 返回-retval;` (2认同)

Joe*_*Joe 12

利用中心极限定理的函数.

function normal(mu, sigma, nsamples){
    if(!nsamples) nsamples = 6
    if(!sigma) sigma = 1
    if(!mu) mu=0

    var run_total = 0
    for(var i=0 ; i<nsamples ; i++){
       run_total += Math.random()
    }

    return sigma*(run_total - nsamples/2)/(nsamples/2) + mu
}
Run Code Online (Sandbox Code Playgroud)

  • 当然,极限定理意味着分布更好地近似于大的"nsamples"的极限.我不会使用此函数进行稳健的统计分析.但是,如果你想要一个发电机用于测试,那么上述工作正常.距离可访问的中心"mu"的最大距离由"nsamples"控制. (2认同)

Poi*_*nty 9

从规格:

15.8.2.14 random()

使用依赖于实现的算法或策略,返回具有正号,大于或等于0但小于1的Number值,随机或伪随机选择,在该范围内具有近似均匀的分布.此函数不带参数.

因此,它是均匀分布,而不是正常分布或高斯分布.这就是您将在专业统计库之外的任何基本语言运行库中的任何标准随机数设施中找到的内容.


Flo*_*ris 9

你混淆函数的输出(这是0和1之间的均匀分布)需要通过重复绘制0或1的随机数来生成高斯分布 - 在大量试验之后,它们的总和将是近似正态分布.

您可以使用该Math.random()函数,然后将结果舍入为整数:如果它<0.5,则返回0; 如果它> = 0.5,则返回1.现在你有相同的概率为零和一,你可以继续你在问题中描述的方法.

只是为了澄清:我认为不可能有一个以正态分布方式产生0或1的算法 - 正态分布需要连续变量.

当您执行上述120个数字时,您将平均得到60个1和60个0.您获得的实际分布将是二项分布,平均值为60,标准差为

stdev = sqrt(p(1-p)N) = 5.48
Run Code Online (Sandbox Code Playgroud)

k当你有可能的n样本p(我们固定为0.5)时,特定数字的概率是

p = n! / ((n-k)! k!) p^k (1-p)^(n-k)
Run Code Online (Sandbox Code Playgroud)

当p = 0.5时,你最终只得到二项式系数 - 通常接近n> 30的正态分布.

  • 当谈到二元(或一元)时,你是对的,不是高斯分布.但一个人可以是"数字化的"; 例如,钟形曲线可以从(至少)三元整数值(0,1,2)构造,显示高斯分布的影响(其中1出现最多,作为均值),但为了得到一个符合68-95-99.7规则的合适曲线形状,粒度需要更高.另外,关于舍入整数的注释是获取随机布尔值的绝佳方法! (2认同)

uns*_*ted 6

还有一个单行示例:

Math.sqrt(-2 * Math.log(Math.random()))*Math.cos((2*Math.PI) * Math.random())
Run Code Online (Sandbox Code Playgroud)

和小提琴 https://jsfiddle.net/rszgjqf8/

  • 这应该是 `Math.sqrt(-2*Math.log(1-Math.random()))*Math.cos(2*Math.PI*Math.random())` 所以你里面永远不会有零值“日志”。 (2认同)

Edu*_*.R. 5

对于那些对生成正态分布值感兴趣的人,我建议在 JavaScript 中检查 Ziggurat 算法的这种实现:https ://www.npmjs.com/package/node-ziggurat

在作者页面中找到的代码是:

function Ziggurat(){

var jsr = 123456789;

var wn = Array(128);
var fn = Array(128);
var kn = Array(128);

function RNOR(){
  var hz = SHR3();
  var iz = hz & 127;
  return (Math.abs(hz) < kn[iz]) ? hz * wn[iz] : nfix(hz, iz);
}

this.nextGaussian = function(){
  return RNOR();
}

function nfix(hz, iz){
  var r = 3.442619855899;
  var r1 = 1.0 / r;
  var x;
  var y;
  while(true){
    x = hz * wn[iz];
    if( iz == 0 ){
      x = (-Math.log(UNI()) * r1); 
      y = -Math.log(UNI());
      while( y + y < x * x){
        x = (-Math.log(UNI()) * r1); 
        y = -Math.log(UNI());
      }
      return ( hz > 0 ) ? r+x : -r-x;
    }

    if( fn[iz] + UNI() * (fn[iz-1] - fn[iz]) < Math.exp(-0.5 * x * x) ){
      return x;
    }
    hz = SHR3();
    iz = hz & 127;

    if( Math.abs(hz) < kn[iz]){
      return (hz * wn[iz]);
    }
  }
}

function SHR3(){
  var jz = jsr;
  var jzr = jsr;
  jzr ^= (jzr << 13);
  jzr ^= (jzr >>> 17);
  jzr ^= (jzr << 5);
  jsr = jzr;
  return (jz+jzr) | 0;
}

function UNI(){
  return 0.5 * (1 + SHR3() / -Math.pow(2,31));
}

function zigset(){
  // seed generator based on current time
  jsr ^= new Date().getTime();

  var m1 = 2147483648.0;
  var dn = 3.442619855899;
  var tn = dn;
  var vn = 9.91256303526217e-3;

  var q = vn / Math.exp(-0.5 * dn * dn);
  kn[0] = Math.floor((dn/q)*m1);
  kn[1] = 0;

  wn[0] = q / m1;
  wn[127] = dn / m1;

  fn[0] = 1.0;
  fn[127] = Math.exp(-0.5 * dn * dn);

  for(var i = 126; i >= 1; i--){
    dn = Math.sqrt(-2.0 * Math.log( vn / dn + Math.exp( -0.5 * dn * dn)));
    kn[i+1] = Math.floor((dn/tn)*m1);
    tn = dn;
    fn[i] = Math.exp(-0.5 * dn * dn);
    wn[i] = dn / m1;
  }
}
zigset();
}
Run Code Online (Sandbox Code Playgroud)

创建一个 Ziggurat.js 文件,然后:

var z = new Ziggurat();
z.nextGaussian();
Run Code Online (Sandbox Code Playgroud)

对我来说,它工作得非常完美,正如我在维基百科中读到的那样,这是一种比 Box-Muller 更有效的算法。

在此处输入链接描述