RandLib。 C ++ 17確率分布ライブラリ


RandLibライブラリを使用すると、50を超える有名な分布、連続、離散、2次元、周期的、さらには1つの特異な分布を扱うことができます 。 何らかの配布が必要な場合は、その名前を入力し、接尾辞Randを追加します。 に興味がありますか?

ランダムジェネレーター


標準C ++テンプレートライブラリを使用して正規分布から100万のランダム変数を生成する場合、次のように記述します。

std::random_device rd; std::mt19937 gen(rd()); std::normal_distribution<> X(0, 1); std::vector<double> data(1e6); for (double &var : data) var = X(gen); 

あまり直感的ではない6行。 RandLibでは、その数を半分にすることができます。

 NormalRand X(0, 1); std::vector<double> data(1e6); X.Sample(data); 

1つのランダムな標準正規分布数量のみが必要な場合、これは1行で実行できます。

 double var = NormalRand::StandardVariate(); 

ご覧のとおり、RandLibは2つのことからユーザーを救います:ベースジェネレーター(0から特定のRAND_MAXまでの整数を返す関数)の選択と、ランダムシーケンスの開始位置の選択(srand()関数)。 多くのユーザーはこの選択を必要としない可能性が高いため、これは便利な名前で行われます。 ほとんどの場合、ランダム値はベースジェネレーターから直接生成されるのではなく、このベースジェネレーターに既に依存している0から1に均等に分布するランダム値Uを通じて生成されます。 Uの生成方法を変更するには、次のディレクティブを使用する必要があります。

 #define UNIDBLRAND //           #define JLKISS64RAND //        64-   #define UNICLOSEDRAND // U   0  1 #define UNIHALFCLOSEDRAND // U   0,     1 

デフォルトでは、Uは0も1も返しません。

発生率


次の表は、1つのランダム変数をマイクロ秒単位で生成する時間の比較を示しています。

システム仕様
Ubuntu 16.04 LTS
プロセッサー:Intel Core i7-4710MQ CPU @ 2.50GHz×8
OSタイプ:64ビット

配布STLランドリブ

 mathcalU01
0.017μs0.006μs

 mathcalN01
0.075μs0.018μs

 mathrmExp1
0.109μs0.016μs

 mathrmCauchy01
0.122μs0.024μs

 mathrmLogNormal01
0.158μs0.101μs

 mathrmWeibull11
0.108μs0.019μs

その他の比較
ガンマ分布:

\ガ0.11
0.207μs0.09μs

\ガ11 sim mathrmExp1
0.161μs0.016μs

\ガ1.51
0.159μs0.032μs

\ガ21
0.159μs0.03μs

\ガ51
0.159μs0.082μs
学生の分布:

 mathcalt0.1
0.248μs0.107μs

 mathcalt1 sim mathrmCauchy01
0.262μs0.024μs

 mathcalt1.5
0.33μs0.107μs

 mathcalt2
0.236μs0.039μs

 mathcalt5
0.233μs0.108μs
フィッシャー分布:

 mathrmF11
0.361μs0.099μs

 mathrmF22
0.319μs0.013μs

 mathrmF33
0.314μs0.027μs

 mathrmF110
0.331μs0.169μs

 mathrmF51
0.333μs0.177μs
二項分布:

 mathrmBin100.5
0.655μs0.033μs

 mathrmBin100.7
0.444μs0.093μs

 mathrmBin1000.07
0.873μs0.197μs
ポアソン分布:

 mathrmPo1
0.048μs0.015μs

 mathrmPo15
0.446μs0.105μs
負の二項分布:

 mathrmNB10.5 sim mathrmGeometric0.5
0.297μs0.019μs

 mathrmNB100.5
0.587μs0.257μs

 mathrmNB1000.05
1.017μs0.108μs


ご覧のとおり、RandLibはSTLの1.5倍の速さで、2、10の場合もありますが、決して遅くはありません。 RandLibで実装されたアルゴリズムの例は、 ここここで見ることができます

分布関数、モーメント、およびその他のプロパティ


ジェネレーターに加えて、RandLibはこれらの分布の確率関数を計算する機能を提供します。 たとえば、パラメーターaのポアソン分布を持つ確率変数が値kをとる確率を見つけるには、関数Pを呼び出す必要があります。

 int a = 5, k = 1; PoissonRand X(a); XP(k); // 0.0336897 

そのため、離散分布の値を取る確率を返す関数を指定するのは大文字のPでした。 連続分布の場合、この確率はほぼすべてゼロであるため、代わりに文字fで示される密度を考慮します。 連続分布と離散分布の両方の分布関数を計算するには、関数Fを呼び出す必要があります。

 double x = 0; NormalRand X(0, 1); Xf(x); // 0.398942 XF(x); // 0 

関数1-F(x)を計算する必要がある場合があります。F(x)は非常に小さな値を取ります。 この場合、精度を失わないために、関数S(x)を呼び出す必要があります。

値のセット全体の確率を計算する必要がある場合は、関数を呼び出す必要があります。

 //x  y - std::vector X.CumulativeDistributionFunction(x, y); // y = F(x) X.SurvivalFunction(x, y); // y = S(x) X.ProbabilityDensityFunction(x, y) // y = f(x) -   X.ProbabilityMassFunction(x, y) // y = P(x) -   

分位数は、p = F(x)となるxを返すpの関数です。 対応する実装は、1次元分布に対応する各有限RandLibクラスにもあります。

 X.Quantile(p); //  x = F^(-1)(p) X.Quantile1m(p); //  x = S^(-1)(p) X.QuantileFunction(x, y) // y = F^(-1)(x) X.QuantileFunction1m(x, y) // y = S^(-1)(x) 

関数f(x)またはP(k)の代わりに、対応する対数を取得する必要がある場合があります。 この場合、次の機能を使用するのが最適です。

 X.logf(k); //  x = log(f(k)) X.logP(k); //  x = log(P(k)) X.LogProbabilityDensityFunction(x, y) // y = logf(x) -   X.LogProbabilityMassFunction(x, y) // y = logP(x) -   

RandLibは、特性関数を計算する機能も提供します。

 phiXt= mathbbE[eitX]



 X.CF(t); //    \phi(t) X.CharacteristicFunction(x, y) // y = \phi(x) 

さらに、最初の4つのポイントまたは期待値、分散、非対称係数、尖度を簡単に取得できます。 さらに、中央値(F ^(-1)(0.5))およびモード(fまたはPが最大値をとるポイント)。

 LogNormalRand X(1, 1); std::cout << " Mean = " << X.Mean() << " and Variance = " << X.Variance() << "\n Median = " << X.Median() << " and Mode = " << X.Mode() << "\n Skewness = " << X.Skewness() << " and Excess kurtosis = " << X.ExcessKurtosis(); 


 Mean = 4.48169 and Variance = 34.5126 Median = 2.71828 and Mode = 1 Skewness = 6.18488 and Excess Kurtosis = 110.936 

パラメータ推定と統計的検定


確率論から統計まで。 一部の(まだすべてではない)クラスには、推定値に対応するパラメーターを設定するFit関数があります。 正規分布の例を考えてみましょう。

 using std::cout; NormalRand X(0, 1); std::vector<double> data(10); X.Sample(data); cout << "True distribution: " << X.Name() << "\n"; cout << "Sample: "; for (double var : data) cout << var << " "; 

標準正規分布から10個の要素を生成しました。 出力は次のようになります。

 True distribution: Normal(0, 1) Sample: -0.328154 0.709122 -0.607214 1.11472 -1.23726 -0.123584 0.59374 -1.20573 -0.397376 -1.63173 

この場合のFit関数は、最尤推定に対応するパラメーターを設定します。

 X.Fit(data); cout << "Maximum-likelihood estimator: " << X.Name(); // Normal(-0.3113, 0.7425) 

既知のように、正規分布の分散の最尤は、偏った推定値を与えます。 したがって、Fit関数には追加の不偏パラメータ(デフォルトではfalse)があり、これを使用して推定値のバイアス/非バイアスを調整できます。

 X.Fit(data, true); cout << "UMVU estimator: " << X.Name(); // Normal(-0.3113, 0.825) 

ベイジアンイデオロギーのファンのために、ベイジアン評価もあります。 RandLib構造により、アプリオリ分布と事後分布での操作が非常に便利になります。

 NormalInverseGammaRand prior(0, 1, 1, 1); NormalInverseGammaRand posterior = X.FitBayes(data, prior); cout << "Bayesian estimator: " << X.Name(); // Normal(-0.2830, 0.9513) cout << "(Posterior distribution: " << posterior.Name() << ")"; // Normal-Inverse-Gamma(-0.2830, 11, 6, 4.756) 

テスト


分布関数のようなジェネレーターが正しい値を返すことをどのように知っていますか? 回答:互いに比較してください。 連続分布の場合、対応する分布のこのサンプルのメンバーシップについてコルモゴロフ-スミルノフ検定を実行する関数が実装されます。 KolmogorovSmirnovTest関数は、序数統計と入力として\ alphaレベルを受け入れ、選択が分布と一致する場合にtrueを返します。 離散分布は、PearsonChiSquaredTest関数に対応しています。

おわりに


この記事は、特にhabrasocietyの一部のために書かれており、これに興味があり、感謝することができます。 理解して、突っついて、健康を使いましょう。 主な利点:


リリースへのリンク。

Source: https://habr.com/ru/post/J323158/


All Articles