JavaScript関数Math.random
は、正規分布(均一分布)を使用するかどうかを指定します。
そうでない場合、正規分布を使用する数値を取得するにはどうすればよいですか?ランダムに正規分布した番号を作成するアルゴリズムについて、インターネット上で明確な答えを見つけていません。
シュミットマシン(ドイツの物理学者)を再構築します。マシンは0または1の乱数を生成しますが、それらは正規分布である必要があります。そのため、それらをガウスの鐘型曲線として描くことができます。
たとえば、ランダム関数は120個の数値(0または1)を生成し、これらの合計値の平均(平均)は60に近くなければなりません。
JavaScript関数Math.randomが正規分布であるかどうかを知りたい
Javascript Math.randomisnota Normal分布(ガウス鐘型曲線)。 From ES 2015、20.2.2.27 "0以上1未満の正符号を持つ数値を返します。実装を使用して、その範囲にほぼ均一な分布を持つランダムまたは疑似ランダムを選択します依存のアルゴリズムまたは戦略。この関数は引数を取りません。」したがって、nが十分に高いときに提供されるコレクションは、ほぼ均一な分布を取得します。間隔内のすべての値の出現確率は等しくなります(x軸に平行な直線。0.0から1.0の間の数値を示します)。
どうすれば正規分布の数値を取得できますか
正規分布で数値のコレクションを取得する方法はいくつかあります。 Maxwell Collard によって回答されたように Box-Muller変換 は均一分布を正規分布に変換します(コードは Maxwell Collard answer にあります)。
question に対する別のstackoverflow回答への回答には、正規分布アルゴリズムへの他の均一分布での返信があります。など:ジグラット、均一比、CDFの反転答えの1つに加えて:
Zigguratアルゴリズムは、このために非常に効率的ですが、Box-Muller変換はゼロから実装するのが簡単です(非常に遅くはありません)。
そして最後に
シュミットマシン(ドイツの物理学者)を再構築します。このマシンは0または1の乱数を生成し、正規分布でなければならないため、ガウスの鐘型曲線で描くことができます。
2つの値(0または1)のみがある場合、ガウス曲線は2つの値を持つ均一分布と同じに見えます。それがシンプルな理由です
function randomZero_One(){
return Math.round(Math.random());
}
十分でしょう。ほぼ等しい確率値0と1で擬似ランダムに戻ります。
これは私の経験では「jsガウスランダム」の最初のGoogle結果であるため、そのクエリに対して実際の回答をする義務があると感じています。
Box-Muller変換 は、(0、1)の2つの独立した一様変量を2つの標準ガウス変量(平均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 );
}
Maxwell's Answerに基づいて、このコードは 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;
}
n = 100
n = 10,000
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;
}
randn_bm(-500, 1000, 1);
randn_bm(10, 20, 0.25);
randn_bm(10, 20, 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));
}
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());
}
これはクヌースから手に入れたと思う。
中心極限定理を利用する関数。
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
}
関数の出力(0と1の間の一様分布)を混同しているため、0または1の乱数を繰り返し描画してガウス分布を生成する必要があります-多数の試行の後、それらの合計はほぼ正規分布。
Math.random()
関数を使用して、結果を整数に丸めることができます。0.5未満の場合、0を返します。 0.5以上の場合、1を返します。0と1の等しい確率が得られ、質問で説明したアプローチを続行できます。
明確にするために、正規分布の方法で0または1のいずれかを生成するアルゴリズムを使用することはできないと考えています。正規分布には連続変数が必要です。
たとえば、120個の数字に対して上記を実行すると、平均で60個の1と60個の0が得られます。実際に得られる分布は、平均が60で標準偏差が2項分布になります。
stdev = sqrt(p(1-p)N) = 5.48
確率k
(0.5で固定)のn
サンプルがある場合、特定の数p
の確率は
p = n! / ((n-k)! k!) p^k (1-p)^(n-k)
P = 0.5の場合、最終的には二項係数のみになります。これは通常、n> 30の正規分布に近づきます。
15.8.2.14ランダム()
0以上1未満の正符号を持つNumber値を返します。ランダムまたは疑似ランダムが選択され、その範囲全体でほぼ均一に分布します、実装依存のアルゴリズムまたは戦略を使用します。この関数は引数を取りません。
したがって、それはuniform分布であり、正規分布でもガウス分布でもありません。これは、特殊な統計ライブラリ以外の基本的な言語ランタイムのほぼすべての標準的な乱数機能で見つけることができるものです。
通常の分布の値を生成することに興味がある場合は、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();
私にとっては完璧に機能しており、Wikipediaで読んだように、これはBox-Mullerよりも効率的なアルゴリズムです。
そして一行の例:
Math.sqrt(-2 * Math.log(Math.random()))*Math.cos((2*Math.PI) * Math.random())
およびFiddle https://jsfiddle.net/rszgjqf8/
先ほど書いたガウス分布からランダムな値をサンプリングする非冗長関数:
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;
}
必要な範囲に値を固定すれば機能するはずです。
適切な構成でいくつかの機能をテストしましたが、すべて同じように機能します。
http://jsfiddle.net/p3y40gf3/29/
中央の制限はニースです。他の人のように見えるためには(6の場合n = 3)、12の場合12でなければなりません。他の人も標準偏差として(6)または12または1/12に設定しましたが、なぜ12かはわかりません。
中央の制限は、Box/MullerおよびZigguratよりも中心が少し小さくなっています。
Box/MullerとZigguratはまったく同じに見えます
joeによるこのバリアント( https://stackoverflow.com/a/33567961/46636 )は、標準偏差を正しく行います。
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はクリップされますが、クリップされたエッジで数が繰り返されることはほとんどありませんが、他のクリップと非常によく似ています。不正な乱数はdiscardedクリップされません。
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
合計であるアーウィンホールと混同されない https://en.wikipedia.org/wiki/Irwin%E2%80%93Hall_distribution
https://en.wikipedia.org/wiki/Normal_distribution#Generating_values_from_normal_distribution
たくさんの便利なランダム関数を含むこのライブラリを見つけました。 npmからsimjsを介してインストールするか、必要なもののためにrandom-node-*。jsファイルを直接取り出すことができます。
http://www.simjs.com/random.htmlhttp://www.simjs.com/download.html
これが Marsaglia polar method を使用した問題の私の解決策です。範囲は、指定したパラメーターによって異なります。パラメーターがなければ、範囲外のものはほとんど生成されません。
繰り返しごとに2つの正規分布数を生成するため、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;
}