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

问题描述 投票:38回答:16

我想知道JavaScript函数Math.random是否使用正态(与均匀)分布。

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

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

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

javascript algorithm random distribution gaussian
16个回答
41
投票

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

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

如何获得正态分布的数字

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

question的另一个stackoverflow答案的答案具有对正态分布算法的其他均匀分布的答复。如:Ziggurat,制服比率,反转CDF除了其中一个答案之外:说:

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

最后

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

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

function randomZero_One(){
    return Math.round(Math.random());
}

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


4
投票

对于那些有兴趣生成正态分布值的人,我建议在JavaScript中检查Ziggurat算法的实现:qazxsw poi

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

https://jsfiddle.net/rszgjqf8/

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

https://www.npmjs.com/package/node-ziggurat

对我而言,它的工作非常完美,正如我在维基百科中所读到的,这是一个比Box-Muller更有效的算法。

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(); }


1
投票

我发现这个库包含许多有用的随机函数。您可以通过npm的simjs安装它,也可以直接将random-node - * .js文件直接输出,以满足您的需求。

var z = new Ziggurat(); z.nextGaussian(); enter link description here


1
投票

这是我使用http://www.simjs.com/random.html解决问题的方法。范围取决于您给出的参数,没有参数,它几乎不会生成超出范围的任何内容。

因为它每次迭代生成两个正态分布的数字,所以我在window.temp.spareNormal下声明了一个变量来获取备用的数据(如果它在那里)。可能不是最适合它的地方,但是嘿。

您可能需要对结果进行舍入才能得到您想要的结果。

http://www.simjs.com/download.html

1
投票

我已经使用正确的配置测试了几个功能,所有功能都相似且良好。

Marsaglia polar method

中心限制很好,必须与(n = 3表示6)和12表示12表示其他情况。我将其他人也配置为(6)或12或1/12作为标准偏差,不知道为什么12。

中心限制比Box / Muller和Ziggurat稍微偏向中心。

Box / Muller和Ziggurat看起来完全一样

Joe(window.temp = { spareNormal: undefined }; Math.normal = function (mean, standardDeviation) { let q, u, v, p; mean = mean || 0.5; standardDeviation = standardDeviation || 0.125; if (typeof temp.spareNormal !== 'undefined') { v = mean + standardDeviation * temp.spareNormal; temp.spareNormal = undefined; return v; } do { u = 2.0 * Math.random() - 1.0; v = 2.0 * Math.random() - 1.0; q = u * u + v * v; } while (q >= 1.0 || q === 0); p = Math.sqrt(-2.0 * Math.log(q) / q); temp.spareNormal = v * p; return mean + standardDeviation * u * p; } )的这个变种正确地做了标准偏差:

http://jsfiddle.net/p3y40gf3/29/

Ziggurat也不错,但需要从z得分调整到0到1看起来好像数字。

Box / Muller剪裁很好,但是在剪裁的边缘处给出了很少的重复数字,但是它与其他的非常相似,不应该丢弃不正确的随机数。

https://stackoverflow.com/a/33567961/466363

中央极限变量称为贝茨分布,即平均function normal(mu, sigma, nsamples){ // using central limit if(!nsamples) nsamples = 3 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 }

并没有与Irwin Hall混淆,这是一个总和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 / 6.0 + 0.5; // Translate to 0 -> 1 // changed here 10 to 6 if(num>1||num<0) return randn_bm(); return num; // bad random numbers should be discared not clipped //return Math.max(Math.min(num, 1), 0); // cap between 0 and 1 }

https://en.wikipedia.org/wiki/Bates_distribution


1
投票

一个非冗长的函数,用于从我前一段时间写过的高斯分布中抽样随机值:

https://en.wikipedia.org/wiki/Irwin%E2%80%93Hall_distribution

如果将值限制在所需范围内,它应该可以工作。



-4
投票
function gaussianRandom(mean, sigma) {
  let u = Math.random()*0.682;
  return ((u % 1e-8 > 5e-9 ? 1 : -1) * (Math.sqrt(-Math.log(Math.max(1e-9, u)))-0.618))*1.618 * sigma + mean;
}

此函数返回介于0和1之间的值。最接近0.5的值将返回。


96
投票

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

Box-Muller transform将(0,1)上的两个独立的均匀变量转换为两个标准高斯变量(均值0,方差1)。由于sqrtlogcos调用,这可能不是非常高效,但是这种方法优于中心极限定理方法(求和N个均匀变量),因为它不会将输出限制在有界范围内(-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 );
}

48
投票

Normal Distribution Between 0 and 1

在麦克斯韦答案的基础上,此代码使用Box–Muller transform为您提供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;
}

Visualizations

enter image description here

n = 100

enter image description here

n = 10,000

enter image description here

n = 10,000,000

Normal Distribution With Min, Max, Skew

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

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;
}

enter image description here

randn_bm(-500, 1000, 1);

enter image description here

randn_bm(10, 20, 0.25);

enter image description here

randn_bm(10, 20, 3);

38
投票

我希望在0和1之间有大约高斯随机数,在many tests之后我发现这是最好的:

function gaussianRand() {
  var rand = 0;

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

  return rand / 6;
}

作为奖励:

function gaussianRandom(start, end) {
  return Math.floor(start + gaussianRand() * (end - start + 1));
}

16
投票

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());
}

我想我是从Knuth得到的。


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
}

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

当qzxswpoi样本具有概率k(我们固定为0.5)时,特定数字n的概率是

p

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


8
投票

p = n! / ((n-k)! k!) p^k (1-p)^(n-k)

15.8.2.14 random()

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

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


4
投票

一行例子:

From the spec:

和小提琴Math.sqrt(-2 * Math.log(Math.random()))*Math.cos((2*Math.PI) * Math.random())

© www.soinside.com 2019 - 2024. All rights reserved.