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

时间:2014-08-30 13:30:18

标签: javascript algorithm random distribution gaussian

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

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

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

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

19 个答案:

答案 0 :(得分:90)

因为这是" 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 );
}

答案 1 :(得分:40)

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

可视化

enter image description here

n = 100

enter image description here

n = 10,000

enter image description here

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

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

答案 2 :(得分:39)

  

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

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

  

如何获得正态分布的数字

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

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

  

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

最后

  

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

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

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

就足够了。它会以近似相等的概率值0和1伪随机返回。

答案 3 :(得分:36)

我希望在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));
}

答案 4 :(得分: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那里得到的。

答案 5 :(得分: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
}

答案 6 :(得分:9)

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

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

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

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

stdev = sqrt(p(1-p)N) = 5.48

如果k样本的概率为n(我们固定为0.5),则特定数字p的概率为

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

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

答案 7 :(得分:8)

From the spec:

  

15.8.2.14 random()

     

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

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

答案 8 :(得分:4)

单行示例:

Math.sqrt(-2 * Math.log(Math.random()))*Math.cos((2*Math.PI) * Math.random())

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

答案 9 :(得分:2)

对于那些有兴趣产生正常分布值的人,我建议在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();
}

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

var z = new Ziggurat();
z.nextGaussian();

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

enter link description here

答案 10 :(得分:1)

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

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

答案 11 :(得分:1)

这是我使用Marsaglia polar method解决问题的方法。范围取决于您给出的参数,没有参数,它几乎不会生成超出范围的任何内容。

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

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

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

答案 12 :(得分:1)

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

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

如果将值限制在所需的范围内,它应该有效。

答案 13 :(得分:1)

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

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

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

中心限制比Box / Muller和Ziggurat稍微集中一点。

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

Joe(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
}

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

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

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
}

中央限制变体称为Bates分布,是平均值 https://en.wikipedia.org/wiki/Bates_distribution

并没有与欧文霍尔相提并论 https://en.wikipedia.org/wiki/Irwin%E2%80%93Hall_distribution

https://en.wikipedia.org/wiki/Normal_distribution#Generating_values_from_normal_distribution

答案 14 :(得分:0)

寻找价值的正态分布:

getNormal = (x, mean, standardDeviation, ) => {
 return (1 / standardDeviation * Math.sqrt(2 * (3, 14))) * Math.pow(Math.E, -Math.pow(x - mean, 2) / (2 * (standardDeviation * standardDeviation)));  
}

答案 15 :(得分:0)

我唯一具有这种资格的条件是参加了一次统计学课。如果我发现了错误,请告诉我,我想了解有关统计的更多信息,并且我不想一直在思考错误。

如果要创建一个随机数生成器,该生成器以正态分布生成数字,则应该能够从均匀分布中采样,这没有问题。如果您设置了一个基本的随机数生成器来生成a到b范围内的数字,则生成的值的分布将为µ =(a + b)/ 2和σ=(b-a)/√12。如果对许多此类样本取了从此分布中获取的几个样本值的平均值(≥30),则对于样本分布µ(样本均值)= µ(填充平均值),而σ(样本均值的标准差)=σ (人口标准偏差)/√n(样本中的值数)。

通过控制原始分布的均值和标准差,您可以控制产生正态分布的随机数生成器的最终均值和标准偏差。

function all_normal(mu, sigma, nsamp)
{
    var total = 0;
    for (var a = 0; a < nsamp; a ++)
    {
       total += rand_int(mu - (sigma * Math.sqrt(3 * nsamp)), mu + (sigma * Math.sqrt(3 * nsamp)));
    }
    return Math.ceil(total / nsamp);
}

答案 16 :(得分:0)

这是我从Donald Knuth的书计算机编程的艺术<3.4>的3.4.1节中的 Algorithm P 正常偏离的极化方法)的JavaScript实现。 / em>:

function gaussian(mean, stddev) {
    return function() {
        var V1
        var V2
        var S
        do{
            var U1 = Math.random()
            var U2 = Math.random()
            V1 = 2*U1-1
            V2 = 2*U2-1
            S = V1*V1+V2*V2
        }while(S >= 1)
        if(S===0) return 0
        return mean+stddev*(V1*Math.sqrt(-2*Math.log(S)/S))
    }
} 

像这样使用它:

var standard_normal = gaussian(0,1)
var a_standard_normal_deviate = standard_normal()

答案 17 :(得分:-1)

let iset = 0;
let gset;

function randn() {

   let v1, v2, fac, rsq;

   if (iset == 0) {
   do {
     v1 = 2.0*Math.random() - 1.0;
     v2 = 2.0*Math.random() - 1.0;
     rsq = v1*v1+v2*v2;
   } while ((rsq >= 1.0) || (rsq == 0));
   fac = Math.sqrt(-2.0*Math.log(rsq)/rsq);
   gset = v1*fac;
   iset = 1;
   return v2*fac;
 } else {
   iset = 0;
   return gset;
 }

}

答案 18 :(得分:-4)

//This is what I use for a Normal-ish distribution random function.
function normal_random(){
  var pos = [ Math.random(), Math.random() ];
  while ( Math.sin( pos[0] * Math.PI ) > pos[1] ){
    pos = [ Math.random(), Math.random() ];
  }   
  return pos[0];
};

此函数返回0到1之间的值。最接近0.5的值返回。