如何在没有超几何函数的点处逼近student-t CDF?

机器算法验证 累积分布函数 t分布
2022-04-16 01:06:52

有没有一种方法可以在某个点上近似地逼近学生 t 分布的 CDFx 不涉及超几何函数例如,通过使用级数展开,或者用更简单的函数来表达 CDF?

t 分布可以用正态分布和卡方分布表示:

t=XnY

在哪里X正态分布,均值为 0,方差为σ2;Y2σ2具有卡方分布。也许这个表达式可以帮助简化 t CDF?

我正在尝试编写一个算法来计算任何实数的 student-t CDF,因此使用超几何函数将非常困难。我也试图避免集成 PDF 来获取 CDF。

1个回答

维基百科上的文章t分布有助于告诉我们,

如果X具有自由度的学生 t 分布ν那么可以得到一个 Beta 分布:

νν+X2B(12,ν2).

这是一个类似 C 语言的实现:

extern FLOAT tcum(FLOAT t, DEGREES m) {
    if (m == INFINITY) return zcum(t);
    assert(m > 0);

    if (t == 0.0) return 0.5;
    if (t < 0.0)  return 0.5 * (1.0 - inbet(t*t / (m + t*t), 1, m));
    else          return 0.5 * (1.0 + inbet(t*t / (m + t*t), 1, m));
} 

zcum(对于任意大,它会退回到标准的 Normal CDFν.)

Numerical Recipesinbet中给出了 Beta 累积分布的一个很好的实现,其中不完整的 Beta 函数inbet(x,a,b)Ix(a,b).

很久以前,我将他们的 Fortran 版本的函数移植到 C 中(一路上清理了一些问题)。这不是太痛苦。这是一个连续的分数扩展。 数值食谱备注

这个连分数快速收敛x<(a+1)/(a+b+2),在最坏的情况下O(max(a,b))迭代。但是对于我们可以只使用对称关系来获得等价的计算其中连分数也将迅速收敛。x>(a+1)/(a+b+2)Ix(a,b)=1I1x(b,a)

您可以在下面经过广泛测试的版本中看到此策略。您不需要头文件 (.h) 的详细信息来移植它。确实需要足够的编写科学软件的经验,才能知道必须彻底测试您的端口并知道如何进行测试和调试。(复制函数的广泛表格以达到完美的准确性是一种方法。它也有助于绘制结果:捕捉各种可能影响数值程序的不稳定错误。)

/* 
 *  INBET.C
 *
 * The incomplete beta function with parameters a/2 and b/2, i.e.,
 * integral, 0 to x, of 
 *     pow(t,(FLOAT)a/2-1)*pow(1-t,(FLOAT)b/2-1),
 * divided by beta(a/2,b/2).
 * x must be >= 0, <= 1, a, b must be > 0.  
 */                       
#include <math.h>
#include <float.h>
#include "stats.h"

static FLOAT betacf(FLOAT a, FLOAT b, FLOAT x);
/*--------------------------------------------------------------------*/

extern FLOAT inbet(FLOAT x, DEGREES a, DEGREES b) {
    FLOAT bt;

    assert((FLOAT)0.0<=x && x<=(FLOAT)1.0);
    if (x==0.0 || x==1.0) {
        bt = 0.0;
    } else {
        bt = exp( lngamma2(a+b)-lngamma2(a)-lngamma2(b) +
                  0.5 * (a*log(x) + b*log(1.0-x)) );
    }
    if (x < (a+2.0) / (a+b+4.0))
        return 2 * bt * betacf((FLOAT)a/2.0, (FLOAT)b/2.0, x) / a;
    else
        return 1.0 - 2 * bt * betacf((FLOAT)b/2.0, (FLOAT)a/2.0, 1.0-x) / b;
} /* inbet() */
/*--------------------------------------------------------------------*/

FLOAT betacf(FLOAT a, FLOAT b, FLOAT x) {
    FLOAT am = 1.0,
          bm = 1.0,
          az = 1.0,
          qab = a+b,
          qap = a+1.0,
          qam = a-1.0,
          bz  = 1.0 - qab * x / qap;
    FLOAT em, tem, d, ap, bp, app, bpp, aold;
    long m=1;

    do {
        em = m; tem = em+em;
        d = em * (b-m) * x / ((qam+tem) * (a+tem));
        ap = az + d*am; bp = bz + d*bm;
        d = -(a+em) * (qab+em) * x / ((a+tem) * (qap+tem));
        app = ap + d*az; bpp = bp + d*bz;
        aold = az;
        am = ap/bpp; bm = bp/bpp; az = app/bpp;
        bz = 1.0;
    } while (fabs(az-aold) >= stat_eps * fabs(az) && m++ <= _stat_iter);

    if (m > _stat_iter)
        _stat_err = _ERR_STAT_EPS;

    return az;
} /* betacf() */
/* eof inbet.c */