web-dev-qa-db-ja.com

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

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

96
Terhorst

Zigguratアルゴリズム はこのために非常に効率的ですが、 Box-Muller変換 はゼロから実装する方が簡単です(とてつもなく遅くありません)。

48
Tyler

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

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

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

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

これは一般的な数学のアプローチです-これを使用することで、逆近似または良好な逆近似を持っている限り、確率または分布関数を選択できます。

これがお役に立てば幸いです。確率そのものではなく、分布の使用についての小さなコメントに感謝します。

27
Adi

以下は、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 */
}
21
user5084

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

N個の均一に分布した数を生成し、それらを合計し、n * 0.5を引くと、平均が0、分散が(1/12) * (1/sqrt(N))に等しいほぼ正規分布の出力が得られます( wikipediaを参照)その最後の分布の均一分布

n = 10の場合、半分の速度が得られます。タイラーズソリューションの半分以上を使用したい場合(正規分布の wikipediaエントリに記載されているように

5
jilles de wit

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

単純な加算および/または乗算により、ニーズに合わせて平均値と標準偏差が変更されます。

1
Pepijn Schmitz

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

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

標準のPythonライブラリモジュールrandomはあなたが望むものを持っています:

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

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

手動入力はこちら

1

ここで、R1、R2は一様な乱数です:

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

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

1
Erik Aronesty

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

  1. ソフトウェアの実装については、[0,1]の疑似一様ランダムシーケンスを与えるカップルランダムジェネレーター名を知っています(Mersenne Twister、Linear Congruate Generator)。それをU(x)と呼びましょう

  2. 確率論と呼ばれる数学的領域が存在します。まず、r.vをモデル化する場合。積分分布Fを使用すると、F ^ -1(U(x))を評価するだけで済みます。理論では、そのようなr.v.積分分布Fを持ちます。

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

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

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

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

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

0
bruziuz

これは、 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
madx

私はおそらく役立つかもしれない次のコードを持っています:

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)]

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

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

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

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

0
Hippo