6

(-\infty, a]累積分布関数は閉じた形では利用できないため、から確率密度関数を統合したいと思います。しかし、C++でこれを行う方法がわかりません。

このタスクはMathematicaではとても簡単です。私がする必要があるのは、関数を定義することだけです。

f[x_, lambda_, alpha_, beta_, mu_] := 
   Module[{gamma}, 
     gamma = Sqrt[alpha^2 - beta^2]; 
     (gamma^(2*lambda)/((2*alpha)^(lambda - 1/2)*Sqrt[Pi]*Gamma[lambda]))*
      Abs[x - mu]^(lambda - 1/2)*
      BesselK[lambda - 1/2, alpha Abs[x - mu]] E^(beta (x - mu))
   ];

次に、ルーチンを呼び出しNIntegrateて数値積分します。

F[x_, lambda_, alpha_, beta_, mu_] := 
    NIntegrate[f[t, lambda, alpha, beta, mu], {t, -\[Infinity], x}] 

今、私はC++で同じことを達成したいと思います。gsl_integration_qagilgsl数値ライブラリのルーチンを使用しています。それは私が欲しいものである半無限の間隔(-\infty, a]で機能を統合するように設計されています。しかし、残念ながら私はそれを機能させることができません。

これはC++の密度関数です。

density(double x)
{
using namespace boost::math;

if(x == _mu)
    return std::numeric_limits<double>::infinity();

    return pow(_gamma, 2*_lambda)/(pow(2*_alpha, _lambda-0.5)*sqrt(_pi)*tgamma(_lambda))* pow(abs(x-_mu), _lambda - 0.5) * cyl_bessel_k(_lambda-0.5, _alpha*abs(x - _mu)) * exp(_beta*(x - _mu));

}  

次に、gslルーチンを呼び出して、統合してcdfを取得しようとします。

cdf(double x)
{
gsl_integration_workspace * w = gsl_integration_workspace_alloc (1000);

    double result, error;      
    gsl_function F;
    F.function = &density;

    double epsabs = 0;
    double epsrel = 1e-12;

    gsl_integration_qagil (&F, x, epsabs, epsrel, 1000, w, &result, &error);

    printf("result          = % .18f\n", result);
    printf ("estimated error = % .18f\n", error);
    printf ("intervals =  %d\n", w->size);

    gsl_integration_workspace_free (w);

    return result;

}

ただしgsl_integration_qagil、エラーを返しますnumber of iterations was insufficient

 double mu = 0.0f;
 double lambda = 3.0f;
 double alpha = 265.0f;
 double beta = -5.0f;

 cout << cdf(0.01) << endl;

ワークスペースのサイズを大きくすると、ベッセル関数は評価されません。

私の問題について何か洞察を与えてくれる人はいないかと思っていました。上記の対応する数学関数Fを呼び出すと、がx = 0.01返されます0.904384

密度が非常に小さな間隔に集中している可能性があります(つまり[-0.05, 0.05]、密度の外側はほぼ0です。プロットを以下に示します)。もしそうなら、これについて何ができるか。読んでくれてありがとう。

密度

4

3 に答える 3

1

私はC++コードを試していませんが、Mathematicaで関数をチェックすると、パラメータlambda、alpha、betaによって決定されるピークの広がりで、muの周りで非常にピークに達しているように見えます。

私がすることは、pdfの予備検索を行うことです:与えられた許容範囲より下の最初の値が見つかるまで、x=muの右と左を見てください。負の無限大の代わりに、これらを累積分布関数の境界として使用します。

擬似コードは次のとおりです。

x_mu
step = 0.000001
adaptive_step(y_value) -> returns a small step size if close to 0, and larger if far.

while (pdf_current > tolerance):
  step = adaptive_step(pdf_current)
  xtest = xtest - step
  pdf_current = pdf(xtest)

left_bound = xtest

//repeat for left bound

この関数のピークがどれほどきつく見えるかを考えると、境界を厳しくすると、ゼロの計算に現在浪費されているコンピューター時間を大幅に節約できる可能性があります。また、-\ infty、bではなく、有界積分ルーチンを使用できます。

ちょっとした考え...

PS:Mathematicaは私にF [0.01、3、265、-5、0]=0.884505を与えます

于 2012-05-28T00:24:35.527 に答える
1

このglslの完全な説明は http://linux.math.tifr.res.in/manuals/html/gsl-ref-html/gsl-ref_16.htmlにあり、役立つ情報が見つかるかもしれません。

私はGSLの専門家ではないので、数学の観点からあなたの問題に焦点を当てませんでしたが、浮動小数点プログラミングに関するいくつかの重要な側面を思い出させなければなりません。

IEEE754標準を使用して数値を正確に表すことはできません。MathLabは、無限の数の表現ロジックを使用して事実を隠します。これにより、ルーティングエラーのない結果が得られます。これが、ネイティブコードと比較して遅い理由です。

FPUを使用する科学的微積分に関係する人には、このリンクを強くお勧めします:http: //docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

あなたがその記事を楽しんだと仮定すると、上記のGSLリンクでこれに気づきました:「エラー範囲が厳しすぎると、ルーチンは収束に失敗します」。

上限と下限の差がdoubleの表現可能な最小値、つまりstd :: neuro_limits :: epsilon();未満の場合、境界が厳しすぎる可能性があります。

さらに、2番目のリンクから、C / C ++コンパイラの実装では、デフォルトの丸めモードが「切り捨て」であることに注意してください。これにより、誤った結果につながる微妙な計算エラーが発生します。私は単純なLiangBarskyラインクリッパー、1次で問題を抱えていました!したがって、この行の混乱を想像してみてください。

return pow(_gamma, 2*_lambda)/(pow(2*_alpha, _lambda-0.5)*sqrt(_pi)*tgamma(_lambda))* pow(abs(x-_mu), _lambda - 0.5) * cyl_bessel_k(_lambda-0.5, _alpha*abs(x - _mu)) * exp(_beta*(x - _mu));

原則として、C / C ++では、中間結果を保持する変数を追加することをお勧めします。これにより、段階的にデバッグしてから、丸め誤差を確認できます。ネイティブプログラミングでこのような式を入力しようとしないでください。言語。コンパイラよりも変数を最適化することはできません。

最後に、原則として、微積分の動的な振る舞いに自信がない限り、すべてを乗算してから除算する必要があります。

幸運を。

于 2012-05-28T02:46:32.030 に答える
1

Re:+/-無限大に統合:

Mathematicaを使って|x-μ|の経験的限界を見つけます >> K、ここでKは平均の周りの「幅」を表し、Kはアルファ、ベータ、ラムダの関数です。たとえば、Fはa(x - μ) -2またはae-以下であり、ほぼ等しいです。b(x-μ)2か何か。これらの関数には、経験的に評価できる無限大までの既知の積分があります。次に、数値積分してKに変換し、有界近似を使用してKから無限大に到達できます。

Kを理解するのは少し難しいかもしれません。私はベッセル関数にあまり詳しくないので、そこではあまり役に立ちません。

一般に、明らかではない数値計算の場合、数値評価を行う前に、できるだけ多くの分析計算を行うのが最善の方法であることがわかりました。(オートフォーカスカメラのようなものです。目的の場所に近づけてから、残りはカメラに任せてください。)

于 2012-05-28T03:56:49.013 に答える