我想知道 JavaScript 函数 Math.random
是否使用正态分布而非均匀分布。
如果不是,我该如何获得使用正态分布的数字?在互联网上没有找到一个清晰的算法来生成随机正态分布的数字。
我想重建一台 Schmidt 机器(德国物理学家)。该机器会产生0或1的随机数,并且它们必须服从正态分布,以便我可以将它们作为高斯钟形曲线绘制出来。
例如,随机函数生成120个数(0或1),这些数值的平均值(均值)必须接近60。
我想知道 JavaScript 函数 Math.random
是否使用正态分布而非均匀分布。
如果不是,我该如何获得使用正态分布的数字?在互联网上没有找到一个清晰的算法来生成随机正态分布的数字。
我想重建一台 Schmidt 机器(德国物理学家)。该机器会产生0或1的随机数,并且它们必须服从正态分布,以便我可以将它们作为高斯钟形曲线绘制出来。
例如,随机函数生成120个数(0或1),这些数值的平均值(均值)必须接近60。
由于在我的经验中,这是“js gaussian random”的第一个谷歌搜索结果,我感觉有义务给出对该查询的实际答案。
Box-Muller transform将(0, 1)上的两个独立均匀变量转换为两个标准高斯变量(平均值为0,方差为1)。这可能不太高效,因为涉及到sqrt
、log
和cos
调用,但这种方法比中心极限定理(对N个均匀变量求和)更优越,因为它不会将输出限制在有界范围(-N/2,N/2)内。而且它非常简单:
// Standard Normal variate using Box-Muller transform.
function gaussianRandom(mean=0, stdev=1) {
const u = 1 - Math.random(); // Converting [0,1) to (0,1]
const v = Math.random();
const z = Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v );
// Transform to the desired mean and standard deviation:
return z * stdev + mean;
}
[-3.10, 3.24]
。 - Dorian$ python3
; >>> import scipy.stats as stats
; >>> 1 - ( stats.norm.cdf( 3.24 ) - stats.norm.cdf( -3.10 ) )
; 0.0015652517111527375
- Maxwell Collard[0, 1]
,正如我的程序所需的那样:https://dev59.com/El8e5IYBdhLWcg3wlbTx#39187274。 - Dorianu = 1 - Math.random()
赋值,而不需要使用while循环的条件? - Vorticov
并用稍微更有效率的一行代码编写函数。return Math.sqrt(-2 * Math.log(1 - Math.random())) * Math.cos(2 * Math.PI * Math.random())
- Vortico在Maxwell的回答基础上,此代码使用Box-Muller变换给出了一个包括0和1在内的正态分布。如果距离平均值超过3.6个标准差(小于0.02%的概率),它将重新采样数值。
function randn_bm() {
let 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
}
n = 100
n = 10,000
n = 10,000,000
此版本允许您设置最小值、最大值和偏度系数。请参见我在底部的用法示例。
function randn_bm(min, max, skew) {
let 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
else{
num = Math.pow(num, skew) // Skew
num *= max - min // Stretch to fill range
num += min // offset to min
}
return num
}
randn_bm(-500, 1000, 1);
randn_bm(10, 20, 0.25);
randn_bm(10, 20, 3);
以下是这些屏幕截图的JSFiddle链接: https://jsfiddle.net/2uc346hp/
if (num > 1 || num < 0) num = randn_bm(min, max, skew); // resample between 0 and 1 if out of range
...应该改为: if (num > 1 || num < 0) { num = randn_bm(min, max, skew); //在超出范围时重新取样在0和1之间 } else { ... } 返回 num;
也就是说,只有在不重新采样的情况下才可以缩放最终数字。 - MushyMiddle0
,最大值是 1
时无法使其工作?我想模仿本地的 random
方法,只输出在 0 到 1 之间。 - vsync有几种方法可以获得符合正态分布的数字集合。正如 Maxwell Collard 所回答的那样,Box-Muller transform 可以将均匀分布转换为正态分布(代码可以在Maxwell Collard answer中找到)。
另一个stackoverflow问题的回答中有一个回复提供了其他均匀分布转换为正态分布的算法,例如: Ziggurat、 Ratio-of-uniforms、 反转CDF。 此外,其中一位回答者说:
Ziggurat算法对此非常有效,尽管从头开始实现Box-Muller transform更容易(而且不会太慢)。
最后,
我希望重建一台Schmidt机器(德国物理学家),该机器产生0或1的随机数,并且它们必须是正态分布的,以便我可以在高斯钟形曲线中绘制它们。function randomZero_One(){
return Math.round(Math.random());
}
这将足以满足需求。它会以大约相等的概率返回伪随机值0和1。
我想要在0到1之间获得大致符合高斯分布的随机数,通过多次测试后,我发现以下代码最适合:
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));
}
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));
}
这适用于start和end > 0且< 1```
- fedebJavascript的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 < 1000; i++) {
console.log( standard() )
}
我想这个来自于Knuth。
gaussian
函数返回的值总是正数,因此它不能是高斯分布的随机值。 - Alessandro Jacopsonif(retval > 0) return retval; return -retval;
。 - Alessandro Jacopson利用中心极限定理的函数。
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
}
nsamples
趋近于无穷大时,极限定理意味着分布更好地逼近。我不会使用此函数进行强健的统计分析。但是,如果您需要一个用于测试目的的生成器,则上述方法可以正常工作。可访问的最大中心距离mu
由nsamples
控制。 - Joereturn sigma*(run_total - nsamples/2)/Math.sqrt(nsamples/12) + mu
- ZephDavies15.8.2.14 随机数 ( )
返回一个正号的数字值,大于或等于0但小于1,并以近似均匀分布的方式随机或伪随机选择,使用实现相关的算法或策略。该函数不需要参数。
因此,这是一种均匀分布,而不是正态分布或高斯分布。这就是你会在任何基本语言运行时中的标准随机数工具中找到的内容,除了专门的统计库之外。
Math.random()
函数,然后将结果四舍五入为整数:如果小于0.5,则返回0;如果大于等于0.5,则返回1。现在你有了等概率的零和一,你可以继续使用你在问题中描述的方法。stdev = sqrt(p(1-p)N) = 5.48
当你有 n
个样本和概率为 p
(我们设定为0.5)时,特定数字k
的概率为
p = n! / ((n-k)! k!) p^k (1-p)^(n-k)
当p = 0.5时,你只会得到二项式系数 - 当n > 30时,这些系数通常趋近于正态分布。
以下是一个单行示例:
Math.sqrt(-2 * Math.log(Math.random()))*Math.cos((2*Math.PI) * Math.random())
一个 JSFiddle 实例: https://jsfiddle.net/rszgjqf8/
Math.sqrt(-2*Math.log(1-Math.random()))*Math.cos(2*Math.PI*Math.random())
,这样你就永远不会在 log
中得到零值。 - Brethlosze对于那些想要生成正态分布值的人,我建议查看这个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();
}
var z = new Ziggurat();
z.nextGaussian();
对我来说,它完美地运作,正如我在维基百科上所读到的那样,这是比Box-Muller更高效的算法。