JavaScript Math.random Нормальное распределение (кривая гауссова колокола)?
Я хочу знать, использует ли функция JavaScript Math.random
нормальное (по сравнению с равномерным) распределение или нет.
Если нет, как я могу получить числа, которые используют нормальное распределение? Я не нашел ясного ответа в Интернете, для алгоритма создания случайных нормально распределенных чисел.
Я хочу перестроить машину Шмидта (немецкий физик). Машина производит случайные числа 0 или 1, и они должны быть распределены нормально, так что я могу нарисовать их как кривую гауссова колокола.
Например, случайная функция производит 120 чисел (0 или 1), а среднее (среднее) этих суммированных значений должно быть около 60.
Ответы
Ответ 1
Я хочу знать, является ли функция JavaScript Math.random нормальным распространением или нет
Javascript Math.random не является нормальным распределением (кривая гауссова колокола). Начиная с ES 2015, 20.2.2.27 "Возвращает значение" Число "с положительным знаком, большим или равным 0, но менее 1, выбранным случайным образом или псевдослучайно с приблизительно равномерным распределением по этому диапазону с использованием зависящего от реализации алгоритма или стратегии. функция не принимает аргументов ". Поэтому предоставленная коллекция, когда n достаточно высока, мы получим приблизительно равномерное распределение. Все значения в интервале будут иметь равную вероятность появления (прямая линия, параллельная оси х, обозначающая число от 0,0 до 1,0).
как я могу получить числа, которые являются нормальным распределением
Существует несколько способов получения чисел с нормальным распределением. Как ответил Максвелл Коллард, преобразование Box-Muller трансформирует равномерное распределение в нормальное распределение (код может быть найден в ответе Максвелла Колларда).
Ответ на другой ответ stackoverflow на вопрос имеет ответ с другим равномерным распределением на обычные алгоритмы распределения. Например: Ziggurat, Ratio-of-uniforms, Инвертирование CDF. Кроме того, один из ответов говорит, что: говорит:
Алгоритм Зиггурата довольно эффективен для этого, хотя преобразование Box-Muller проще реализовать с нуля (и не сумасшедший медленный).
И наконец
Я хочу перестроить машину Шмидта (немецкий физик), машина производит случайные числа 0 или 1, и они должны быть распределены нормально, поэтому я могу рисовать их по гауссовой колоколообразной кривой.
Когда мы имеем только два значения (0 или 1), гауссова кривая выглядит так же, как равномерное распределение с двумя возможными значениями. Вот почему
function randomZero_One(){
return Math.round(Math.random());
}
было бы достаточно. Он возвращался бы псевдослучайно с приблизительно равными значениями вероятности 0 и 1.
Ответ 2
Поскольку это первый результат Google для "js gaussian random" в моем опыте, я чувствую обязанность дать реальный ответ на этот запрос.
Преобразование Box-Muller преобразует два независимых равномерных вариации на (0, 1) в два стандартных гауссовских вариации (среднее значение 0, дисперсия 1), Это, вероятно, не очень эффективно из-за вызовов sqrt
, log
и cos
, но этот метод превосходит подход центральной предельной теоремы (суммируя 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 );
}
Ответ 3
Я хотел иметь приблизительно гауссовые случайные числа между 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));
}
Ответ 4
Нормальное распределение между 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;
}
Зрительные
![enter image description here]()
n = 100
![enter image description here]()
n = 10000
![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);
Ответ 5
Псевдослучайная функция 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());
}
Я думаю, что получил это от Кнута.
Ответ 6
Функция, использующая центральную предельную теорему.
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
}
Ответ 7
Вы вводите в заблуждение вывод функции (которая является равномерным распределением между 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
Вероятность определенного числа k
, когда у вас есть n
выборки с вероятностью p
(которые мы зафиксировали в 0.5), составляет
p = n! / ((n-k)! k!) p^k (1-p)^(n-k)
Когда p = 0,5, вы получаете только биномиальные коэффициенты, которые обычно подходят к нормальному распределению при n > 30.
Ответ 8
Из спецификации:
15.8.2.14 random()
Возвращает значение Number с положительным знаком, большим или равным 0, но менее 1, выбранным случайным образом или псевдослучайно с приблизительно равномерным распределением по этому диапазону с использованием зависящего от реализации алгоритма или стратегии. Эта функция не принимает аргументов.
Таким образом, это равномерное распределение, не нормальное или гауссовское. Это то, что вы собираетесь найти практически в любом стандартном объекте случайных чисел в любой базовой среде исполнения за пределами специализированных библиотек статистики.
Ответ 9
И пример с одной строкой:
Math.sqrt(-2 * Math.log(Math.random()))*Math.cos((2*Math.PI) * Math.random())
и скрипка
https://jsfiddle.net/rszgjqf8/
Ответ 10
Я нашел эту библиотеку, которая включает множество полезных функций Random. Вы можете установить его через simjs из npm или просто взять файл random- node - *. Js напрямую для того, что вам нужно.
http://www.simjs.com/random.html
http://www.simjs.com/download.html
Ответ 11
Для тех, кто заинтересован в генерации значений нормального распределения, я бы рекомендовал проверить эту реализацию алгоритма Зиггурата в JavaScript: 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.
введите ссылку здесь
Ответ 12
Это мое решение проблемы, используя полярный метод Марсалья.. Диапазон зависит от параметров, которые вы даете, без параметров, которые почти никогда не генерируют ничего вне диапазона.
Поскольку он генерирует два нормально распределенных числа на итерацию, я объявил переменную под окном .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;
}
Ответ 13
Неверная функция для выборки случайного значения из распределения Гаусса, который я написал некоторое время назад:
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;
}
Он должен работать, если вы зажимаете значения в нужный диапазон.
Ответ 14
Я проверил несколько функций с правильной конфигурацией, все работает одинаково и хорошо.
http://jsfiddle.net/p3y40gf3/29/
Центральный лимит хорош, должен быть с (n = 3 для 6) и 12 для 12, чтобы выглядеть как другие. Я также настроил других (6) или 12 или 1/12 как стандартное отклонение, не уверен, почему 12.
Центральный предел меньше, чем меньше, чем Box/Muller и Ziggurat.
Box/Muller и Ziggurat выглядят точно так же
этот вариант Joe (fooobar.com/questions/355206/...) делает стандартное отклонение правильно:
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 также хорош, но его нужно скорректировать с нуля от 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
не путается с Irwin Hall, это сумма https://en.wikipedia.org/wiki/Irwin%E2%80%93Hall_distribution
https://en.wikipedia.org/wiki/Normal_distribution#Generating_values_from_normal_distribution
Ответ 15
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;
}
}
Ответ 16
//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.