如何在Java中生成正常的累积分布?它的逆cdf?对数正常怎么样?

时间:2012-02-11 18:35:55

标签: java statistics probability

我是Java的新手,第二天!我想生成正态分布的样本。我正在使用逆变换。

基本上,我想找到逆正态累积分布,然后找到它的逆。并生成样本。

我的问题是:是否有反向正常cdf的内置函数?或者我必须手工编写代码吗?

我见过人们在apache commons上引用this。这是内置的吗?或者我必须下载它吗?

如果我必须自己做,你能给我一些提示吗?如果我下载,我的教授是否还必须安装“软件包”或特殊文件?

提前致谢!

编辑:刚发现我不能使用库,也听说有更简单的方法使用弧度转换正常。

4 个答案:

答案 0 :(得分:2)

正如提到here

  

Apache Commons - Math有你想要的东西。

     

更具体地说,请查看NormalDistrubitionImpl类。

如果您向他提供所有需要的图书馆,那么您的教授不需要下载内容。

更新:

如果您想手动编码(我不知道实际公式),您可以查看以下链接: http://home.online.no/~pjacklam/notes/invnorm/

有两个人在java中实现它:http://home.online.no/~pjacklam/notes/invnorm/#Java

答案 1 :(得分:2)

我遇到了同样的问题并找到了解决方案,下面的代码将给出累积分布函数的结果,就像excel一样:

 private static double erf(double x)
{
    //A&S formula 7.1.26
    double a1 = 0.254829592;
    double a2 = -0.284496736;
    double a3 = 1.421413741;
    double a4 = -1.453152027;
    double a5 = 1.061405429;
    double p = 0.3275911;
    x = Math.abs(x);
    double t = 1 / (1 + p * x);
    //Direct calculation using formula 7.1.26 is absolutely correct
    //But calculation of nth order polynomial takes O(n^2) operations
    //return 1 - (a1 * t + a2 * t * t + a3 * t * t * t + a4 * t * t * t * t + a5 * t * t * t * t * t) * Math.Exp(-1 * x * x);

    //Horner's method, takes O(n) operations for nth order polynomial
    return 1 - ((((((a5 * t + a4) * t) + a3) * t + a2) * t) + a1) * t * Math.exp(-1 * x * x);
}
public static double NORMSDIST(double z)
{
    double sign = 1;
    if (z < 0) sign = -1;

    double result=0.5 * (1.0 + sign * erf(Math.abs(z)/Math.sqrt(2)));
    return result;
}

答案 2 :(得分:0)

从数学上讲,这是一个难题,您可能会考虑一些解决方案。

免责声明:前面的数学术语。

您可能已经知道, normalcdf 函数用于计算正常随机变量的概率。由于正态分布是连续的,因此相应的概率密度函数( normalpdf )本身不会给出概率(与诸如 binomial geometry 的离散分布相反) em>分布)。取而代之的是,曲线下的区域给出了正常随机变量落在值的范围内的可能性。因此,您要查找的 normalcdf 函数是 normalpdf 函数的一部分下面的区域。

在数学上,找到连续曲线下的面积是微积分的基本问题。这种类型的问题的解决方案称为 integral integrating 一个在一定范围内的函数,这意味着找到曲线下的面积以及在该范围内的最小值之间。最高的。

在大多数情况下,我们可以集成 pdf 函数以获得 cdf 函数,然后在任何需要的地方对其进行评估。问题的核心,也是Java中的算法不像人们想象的那么简单的原因,是 normalpdf 函数没有封闭形式整数,它是值不能以任何有限的步数进行计算。因此,normalcdf函数的值特别难以捉摸。

有两种主要的解决方案。

1。数值积分技术

数字积分技术通过几何近似曲线下的面积来解决该问题。该区域分为矩形或宽度相等或变化的其他形状,每个高度由 pdf 函数指定。矩形的面积之和是曲线下面积的近似值,即相应的概率。这些技术可用于以任意精度计算值,但在计算上比第2类更高。使用更好的近似值(例如Simpson规则)可提高计算效率。下面是一种简单的数值积分方法。

public static double normCDF(double z)
{   double LeftEndpoint = -100;
int nRectangles = 100000;
    double runningSum = 0;
    double x;
    for(int n = 0; n < nRectangles; n++){
    x = LeftEndpoint + n*(z-LeftEndpoint)/nRectangles;
        runningSum += Math.pow(Math.sqrt(2*Math.PI),-1)*Math.exp(-Math.pow(x,2)/2)*(z-LeftEndpoint)/nRectangles;
    }
    System.out.println(runningSum);
    return runningSum;
}

2。分析技术

分析技术利用了这样一个事实,即 normalpdf 不具有闭合形式的整数,而 pdf 可以“转换”为称为< em> Taylor系列,然后逐项集成。基本上,它将 pdf 转换成无数个简单函数的总和,然后解析地集成每个函数,然后将所有积分加在一起。由于这是一个解析过程,因此程序员只需在计算系数之后就将整数序列包括在程序中即可。结果的精度仅取决于您在计算中包括的总和的多少项,并且往往比数值积分技术要早得多地接近精确值。例如,Mohammad Aldefrawy的解决方案仅计算五个系数。下面是一种包含系数计算的方法,因此您可以以任意精度计算值(实际上, normalcdf 系列不是直接计算的。相反,相关的 error的系数函数进行计算,然后通过线性变换进行转换)。但是,由于系数的计算涉及阶乘函数,因此对于大量的系数会遇到存储问题。值得庆幸的是,该方法在上一类解决方案中所需的迭代次数中,返回的精度要高得多,以产生相似的结果。

public static double normalCDF(double x){
    System.out.println(0.5*(1+erf(x/Math.sqrt(2))));
    return 0.5*(1+erf(x/Math.sqrt(2)));
}

public static double erf(double z)
{
    int nTerms = 315;
    double runningSum = 0;
    for(int n = 0; n < nTerms; n++){
        runningSum += Math.pow(-1,n)*Math.pow(z,2*n+1)/(factorial(n)*(2*n+1));
    }
    return (2/Math.sqrt(Math.PI))*runningSum;
}

static double factorial(int n){
    if(n == 0) return 1;
    if(n == 1) return 1; 
    return n*factorial(n-1);
}

其他功能

对于逆函数,由于我们在normalCDF方法中使用了误差函数,因此可以类似的方式使用逆误差函数。再次,我们通过解析获得反误差函数的系数,然后根据需要在方法中进行计算。

public static double invErf(double z)
{
    int nTerms = 315;
    double runningSum = 0;
    double[] a = new double[nTerms + 1];
    double[] c = new double[nTerms + 1];
    c[0]=1;
    for(int n = 1; n < nTerms; n++){
        double runningSum2=0;
        for (int k = 0; k <= n-1; k++){
            runningSum2 += c[k]*c[n-1-k]/((k+1)*(2*k+1));
        }
        c[n] = runningSum2;
        runningSum2 = 0;
    }
    for(int n = 0; n < nTerms; n++){
        a[n] = c[n]/(2*n+1);
        runningSum += a[n]*Math.pow((0.5)*Math.sqrt(Math.PI)*z,2*n+1);
    }
    return runningSum;
}

public static double invNorm(double A){
    return (2/Math.sqrt(2))*invErf(2*A-1);
}

我没有对数正态函数的方法,但是您可以使用相同的想法获得一个方法。

答案 3 :(得分:-1)

我从未尝试过,但来自algo团队的人正在使用Colt,他们对结果感到满意。