一様分布から正規分布への変換


106

どのようにして(ほとんどの乱数ジェネレータが生成するように、たとえば0.0と1.0の間で)均一分布を正規分布に変換できますか?選択した平均値と標準偏差が必要な場合はどうなりますか?


3
言語仕様はありますか、またはこれは一般的なアルゴリズムの質問ですか?
トカゲに請求する

3
一般的なアルゴリズムの質問。どの言語でもかまいません。しかし、私は答えがその言語だけが提供する特定の機能に依存しないことを望みます。
Terhorst 2008

回答:


47

ジッグラトアルゴリズムがあるが、このためにかなり効率的であるボックス・ミュラー変換を最初から実装が簡単(と遅いクレイジーではない)です。


7
線形合同ジェネレーターに関する通常の警告は、これらの両方の方法に適用されるため、適切な下線ジェネレーターを使用してください。乾杯。
dmckee ---元モデレーターの子猫

3
Mersenee Twisterなど、他に提案はありますか?
グレッグリンド

47

たくさんの方法があります:

  • Box Muller 使用しないでください。特に多くのガウス数を描く場合。Box Mullerは-6と6の間にクランプされる結果を生成します(倍精度を想定しています。フロートを使用すると事態は悪化します)。そしてそれは他の利用可能な方法よりも実際にはあまり効率的ではありません。
  • Zigguratは問題ありませんが、テーブルルックアップが必要です(キャッシュサイズの問題のため、プラットフォーム固有の調整が必要です)
  • ユニフォームの比率は私のお気に入りです。数回の加算/乗算とログの1/50の時間だけです(たとえば、そこを見てください)。
  • CDFの反転効率的です(見落とされていますが、なぜですか?)。googleを検索すれば、CDFの高速実装を利用できます。準乱数には必須です。

2
[-6,6]クランプについて本当によろしいですか?これは、trueの場合はかなり重要なポイントです(ウィキペディアページのメモに値します)。
redcalx 2011

1
@locster:これは私の先生が私に言ったことです(彼はそのような発電機を研究しました、そして私は彼の言葉を信頼しています)。参考資料を見つけられるかもしれません。
アレクサンドルC.

7
@locster:この望ましくないプロパティは、逆CDFメソッドでも共有されます。cimat.mx/~src/prope08/randomgauss.pdfを参照してください。これは、ゼロ以外の確率を持つ均一なRNGを使用して、ゼロに非常に近い浮動小数点数を生成することで軽減できます。ほとんどのRNGは生成しません。これは、(通常64ビット)整数を生成し、[0,1]にマッピングされるためです。このため、これらのメソッドはガウス変数の裾をサンプリングするのに適していません(計算ファイナンスでの低/高ストライクオプションの価格設定を考えてください)。
アレクサンドルC.

6
@AlexandreC。2つの点を明確にするために、64ビットの数値を使用すると、末尾は8.57または9.41(ログを取る前に[0,1]に変換することに対応するより低い値)のいずれかになります。[-6、6]に固定されていても、この範囲外になる可能性は約1.98e-9であり、科学分野のほとんどの人にとっても十分です。8.57および9.41の数値の場合、これは1.04e-17および4.97e-21になります。これらの数は非常に小さいため、ボックスミュラーサンプリングと真のガウスサンプリングとの上記の制限の違いは、ほとんど純粋に学術的なものです。あなたがより良い必要がある場合は、ちょうど2によってそれらと除算の4つまで追加
CrazyCasta

6
Box Muller変換を使用しないという提案は、大部分のユーザーにとって誤解を招くと思います。制限について知っておくのは素晴らしいことですが、CrazyCastaが指摘しているように、外れ値に大きく依存していないほとんどのアプリケーションでは、おそらくこれを心配する必要はありません。例として、numpyを使用して法線からのサンプリングに依存したことがある場合は、Box Muller変換(極座標形式)github.com/numpy/numpy/blob/…に依存しています。
Andreas Grivas

30

関数の分布を別の関数に変更するには、必要な関数の逆を使用する必要があります。

言い換えると、特定の確率関数p(x)を目指す場合、それを積分して分布を得る-> d(x)= integral(p(x))とその逆を使用:Inv(d(x)) 。次に、ランダムな確率関数(一様分布)を使用し、関数Inv(d(x))を使用して結果値をキャストします。選択した関数に応じて、ランダムな値が分布でキャストされるはずです。

これは一般的な数学のアプローチです。これを使用することで、逆近似または良好な逆近似がある限り、任意の確率または分布関数を選択できるようになります。

これが役に立ったといいのですが、確率そのものではなく分布の使用についての小さな発言に感謝します。


4
+1これは、非常にうまく機能するガウス変数を生成するための見落とされている方法です。この場合、ニュートン法で逆CDFを効率的に計算できます(導関数はe ^ {-t ^ 2}です)。初期近似は有理数として簡単に取得できるため、erfとexpを3〜4回評価する必要があります。準乱数を使用する場合は必須です。これは、ガウスの数値を得るために1つの均一な数値を使用する必要がある場合です。
Alexandre C.

9
確率分布関数ではなく、累積分布関数を反転する必要があることに注意してください。アレクサンドルはこれを暗示するが、私はそれをより明確に言及することは害を
及ぼさ

平均に対する方向をランダムに選択する準備ができている場合は、PDFを使用できます。その権利を理解できますか?
マークマッケナ


1
ここでは、SEの関連する質問と、より一般的な回答と優れた説明を示します。
ダッシュ1995年

23

以下は、Box-Muller変換の極形式を使用したjavascript実装です。

/*
 * Returns member of set with a given mean and standard deviation
 * mean: mean
 * standard deviation: std_dev 
 */
function createMemberInNormalDistribution(mean,std_dev){
    return mean + (gaussRandom()*std_dev);
}

/*
 * Returns random number in normal distribution centering on 0.
 * ~95% of numbers returned should fall between -2 and 2
 * ie within two standard deviations
 */
function gaussRandom() {
    var u = 2*Math.random()-1;
    var v = 2*Math.random()-1;
    var r = u*u + v*v;
    /*if outside interval [0,1] start over*/
    if(r == 0 || r >= 1) return gaussRandom();

    var c = Math.sqrt(-2*Math.log(r)/r);
    return u*c;

    /* todo: optimize this algorithm by caching (v*c) 
     * and returning next time gaussRandom() is called.
     * left out for simplicity */
}

5

中心極限定理のウィキペディアエントリ mathworldエントリを利用してください。

均一に分布した数のnを生成し、それらを合計し、n * 0.5を引くと、平均が0で分散が等しいほぼ正規分布の出力が得られます(1/12) * (1/sqrt(N))(最後の分布については、ウィキペディアを参照してください)。

n = 10を指定すると、速度が半分ほど速くなります。半分以上のものが必要な場合は、タイラーソリューションを使用してください(正規分布のWikipediaのエントリに記載されています)。


1
これは、特に近い正規を与えません(「テール」またはエンドポイントは実際の正規分布に近くなりません)。他の人が示唆しているように、Box-Mullerの方が優れています。
Peter K.

1
Box Mullerにも間違った尾があります(倍精度で-6から6の間の数値を返します)
Alexandre C.

n = 12(0から1の範囲の12の乱数を合計し、6を引く)は、stddev = 1および平均= 0になります。その後、これを使用して正規分布を生成できます。結果に目的のstddevを乗算して、平均を加算するだけです。
JerryM 2016

3

Box-Mullerを使用します。これに関する2つのこと:

  1. 反復ごとに2つの値が得られます。
    通常、1つの値をキャッシュして、もう1つの値を返します。サンプルの次の呼び出しで、キャッシュされた値を返します。
  2. Box-MullerはZスコアを与える次に
    、標準偏差でZスコアをスケーリングし、平均を追加して正規分布の完全な値を取得する必要があります。

Zスコアをどのようにスケーリングしますか?
Terhorst 2008

3
scaled = mean + stdDev * zScore //は、normal(mean、stdDev ^ 2)を提供します
yoyoyoyosef 2008年

2

ここで、R1、R2はランダムで一様な数です。

SDが1の通常の分布:sqrt(-2 * log(R1))* cos(2 * pi * R2)

これは正確です...これらすべての遅いループを実行する必要はありません!


誰かが私を修正する前に...私が思いついた近似は次のとおりです:(1.5-(R1 + R2 + R3))* 1.88。私も好きです。
Erik Aronesty、2011年

2

8年後にこれに何かを追加できるとは信じられないようですが、Javaの場合は、読者に平均0.0と標準偏差1.0のガウス分布を生成するRandom.nextGaussian()メソッドを紹介したいと思います。

単純な加算または乗算、あるいはその両方により、平均と標準偏差がニーズに変わります。


1

標準のPythonライブラリモジュールrandomには、次のような機能があります。

normalvariate(mu、sigma)
正規分布。muは平均、sigmaは標準偏差です。

アルゴリズム自体については、Pythonライブラリのrandom.pyの関数をご覧ください。

マニュアルエントリーはこちら


2
残念ながら、pythonのライブラリは、Kinderman、AJとMonahan、JFを使用しています。これは、単一の変数ではなく、2つの均一なランダム変数を使用して通常の値を生成するため、OPが必要とするマッピングとしてそれをどのように使用するかは明らかではありません。
Ian

1

これは、Donald Knuthの著書The Art of Computer Programmingのセクション3.4.1からのアルゴリズムP通常の逸脱のためのPolarメソッド)の私のJavaScript実装です。

function normal_random(mean,stddev)
{
    var V1
    var V2
    var S
    do{
        var U1 = Math.random() // return uniform distributed in [0,1[
        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))
}

0

私はあなたがこれをEXCELで試すべきだと思う: =norminv(rand();0;1)。これにより、平均がゼロで分散が統一される正規分布の乱数が生成されます。「0」には任意の値を指定できるため、数値は希望の平均値となり、「1」を変更すると、入力の2乗に等しい分散が得られます。

例:=norminv(rand();50;3)MEAN = 50 VARIANCE = 9の正規分布の数値になります。


0

Qどのようにして均一分布(ほとんどの乱数ジェネレータが生成する、たとえば0.0と1.0の間)を正規分布に変換できますか?

  1. ソフトウェアの実装では、[0,1](Mersenne Twister、Linear Congruate Generator)で疑似均一ランダムシーケンスを提供するいくつかのランダムジェネレーター名を知っています。それをU(x)としましょう

  2. 確率論と呼ばれる数学的領域が存在します。最初に、積分分布Fを使用してrvをモデル化する場合は、F ^ -1(U(x))を評価してみてください。理論では、そのようなrvが積分分布Fを持つことが証明されました。

  3. ステップ2は、F ^ -1が問題なく分析的に導出できる場合、カウント方法を使用せずにrv〜Fを生成するために適用できます。(例:分布)

  4. 正規分布をモデル化するには、y1 * cos(y2)を計算します。ここで、y1〜は[0,2pi]で均一です。y2はレリー分布です。

Q:選択した平均と標準偏差が必要な場合はどうなりますか?

sigma * N(0,1)+ mを計算できます。

そのようなシフトとスケーリングがN(m、sigma)につながることを示すことができます


0

これは、Box-Muller変換の極形式を使用したMatlab実装です。

機能randn_box_muller.m

function [values] = randn_box_muller(n, mean, std_dev)
    if nargin == 1
       mean = 0;
       std_dev = 1;
    end

    r = gaussRandomN(n);
    values = r.*std_dev - mean;
end

function [values] = gaussRandomN(n)
    [u, v, r] = gaussRandomNValid(n);

    c = sqrt(-2*log(r)./r);
    values = u.*c;
end

function [u, v, r] = gaussRandomNValid(n)
    r = zeros(n, 1);
    u = zeros(n, 1);
    v = zeros(n, 1);

    filter = r==0 | r>=1;

    % if outside interval [0,1] start over
    while n ~= 0
        u(filter) = 2*rand(n, 1)-1;
        v(filter) = 2*rand(n, 1)-1;
        r(filter) = u(filter).*u(filter) + v(filter).*v(filter);

        filter = r==0 | r>=1;
        n = size(r(filter),1);
    end
end

そして、histfit(randn_box_muller(10000000),100);これを呼び出すのが結果です: Box-Muller Matlab Histfit

明らかに、それはMatlab組み込みrandnと比較して本当に非効率的です


0

私はおそらく助けることができる次のコードを持っています:

set.seed(123)
n <- 1000
u <- runif(n) #creates U
x <- -log(u)
y <- runif(n, max=u*sqrt((2*exp(1))/pi)) #create Y
z <- ifelse (y < dnorm(x)/2, -x, NA)
z <- ifelse ((y > dnorm(x)/2) & (y < dnorm(x)), x, z)
z <- z[!is.na(z)]

0

また、正規分布用の乱数ジェネレーターを作成するよりも高速なので、実装された関数rnorm()を使用する方が簡単です。証明として次のコードを参照してください

n <- length(z)
t0 <- Sys.time()
z <- rnorm(n)
t1 <- Sys.time()
t1-t0

-2
function distRandom(){
  do{
    x=random(DISTRIBUTION_DOMAIN);
  }while(random(DISTRIBUTION_RANGE)>=distributionFunction(x));
  return x;
}

ただし、戻る保証はありませんか?;-)
ピーターK.

5
乱数は偶然に任せるにはあまりに重要です。
Drew Noakes、2011年

質問には答えません-正規分布には無限の領域があります。
Matt
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.