我想知道 JavaScript 函数 Math.random
是否使用正态分布而非均匀分布。
如果不是,我该如何获得使用正态分布的数字?在互联网上没有找到一个清晰的算法来生成随机正态分布的数字。
我想重建一台 Schmidt 机器(德国物理学家)。该机器会产生0或1的随机数,并且它们必须服从正态分布,以便我可以将它们作为高斯钟形曲线绘制出来。
例如,随机函数生成120个数(0或1),这些数值的平均值(均值)必须接近60。
我想知道 JavaScript 函数 Math.random
是否使用正态分布而非均匀分布。
如果不是,我该如何获得使用正态分布的数字?在互联网上没有找到一个清晰的算法来生成随机正态分布的数字。
我想重建一台 Schmidt 机器(德国物理学家)。该机器会产生0或1的随机数,并且它们必须服从正态分布,以便我可以将它们作为高斯钟形曲线绘制出来。
例如,随机函数生成120个数(0或1),这些数值的平均值(均值)必须接近60。
我已经测试了几个函数,使用正确的配置后,它们都能够正常地工作。
http://jsfiddle.net/p3y40gf3/29/
中心极限定理很好,必须使用(n=3 for 6)和12 for 12才能看起来与其他内容相同。我还将其他内容配置为(6)或12或1/12作为标准差,不确定为什么是12。
中心极限定理比Box/Muller和Ziggurat略微偏离中心。
Box/Muller和Ziggurat看起来完全相同。
Joe(https://dev59.com/El8e5IYBdhLWcg3wlbTx#33567961)提供的这个变量正确地计算了标准差:
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
}
https://en.wikipedia.org/wiki/Normal_distribution#Generating_values_from_normal_distribution
skewnormal
通过 normal
和 normal01
实现skewnormal(min, max, ..)
返回一个随机数,该随机数来自于正态分布,并被拉伸和偏移以使其范围从min
到max
,呈指数偏斜,且被截断为sigma
个标准差(反向计算)。为了清晰易懂并可以直接从这些中间函数生成随机数,将其分解成逻辑步骤normal
和normal01
。(还有一个额外的lognormal
!)
/// skewnormal(..) returns a random number from the normal distribution that has
/// been streched and offset to range from `min` to `max`, skewed with `skew`,
/// and truncated to `sigma` standard deviations. See https://dev59.com/El8e5IYBdhLWcg3wlbTx#74258559
const skewnormal = (min, max, skew = 1, sigma = 8) => {
/// normal() returns a random number from the standard normal distribution.
/// Uses the Box-Muller transform.
const normal = () => Math.sqrt(-2.0 * Math.log(Math.random())) * Math.cos(2.0 * Math.PI * Math.random());
/// normal01(..) returns normally distributed random number, whose range is
/// truncated at `sigma` standard deviations and shifted to interval `[0, 1]`.
const normal01 = (sigma) => {
while (true) {
let num = normal() / (sigma + 0.0) + 0.5; // translate to [0, 1]
if (0 <= num && num <= 1) return num; // ok if in range, else resample
}
}
var num = normal01(sigma);
num = Math.pow(num, skew) // skew
num *= max - min // stretch to fill range
num += min // offset to min
return num;
}
/// lognormal() returns a random number from the log-normal distribution.
const lognormal = () => Math.exp(normal());
基于 joshuakcockrell 的另一个流行答案。您可能更喜欢这个实现,因为:1.它被分解为展示中间函数,2.它公开了在数学上相关和有用的 sigma
参数,3.它有更好的名称和注释。
查看 JSFiddle 完整演示环境,可以轻松定义、测试和可视化自己的随机分布函数,如下图所示:
查看交互式图表:https://jsfiddle.net/rgefzusq/34/show/ Playground: https://jsfiddle.net/rgefzusq/34/
我之前写了一个非冗长函数,用于从高斯分布中随机抽样:
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;
}
如果您将值夹在所需范围内,它应该能够工作。
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()
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;
}
function getGaussianRandom(mean, standardDeviation) { return () => { let q, u, v, p; 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); return mean + standardDeviation * u * p; }; }
- alecmce以防万一:Math.pow(Math.random(), p)
。
例如:
function testR(max = 100, min = 0, p = 1, c = 20)
{
let t = [];
for (let i = 0; i < c; ++i)
{
t.push(Math.floor(Math.pow(Math.random(), p) * (max - min + 1) + min));
}
console.log(
`p = ${String(p).padStart(5)}`, '|',
t.sort(function (a, b) { return a - b; }).join(', ')
);
}
testR(9, 0, 10);
testR(9, 0, 2);
testR(9, 0, 1);
testR(9, 0, 0.5);
testR(9, 0, 0.1);
testR(9, 0, 0.05);
Results in client/JS console
jsFiddle 图形测试:
http://www.simjs.com/random.html http://www.simjs.com/download.html
我唯一与此相关的资格是上过一门统计学课程。如果我有什么错误,请告诉我,我想更多地了解统计学,不想一直保持错误的想法。
如果你想创建一个产生正态分布数字的随机数生成器,你应该能够从均匀分布中取样,这没有问题。如果你设置一个基本的随机数生成器,它生成范围在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);
}
寻找值的正态分布:
getNormal = (x, mean, standardDeviation, ) => {
return (1 / standardDeviation * Math.sqrt(2 * (3, 14))) * Math.pow(Math.E, -Math.pow(x - mean, 2) / (2 * (standardDeviation * standardDeviation)));
}
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;
}
}