我是Java的新手,第二天!我想生成正态分布的样本。我正在使用逆变换。
基本上,我想找到逆正态累积分布,然后找到它的逆。并生成样本。
我的问题是:是否有反向正常cdf的内置函数?或者我必须手工编写代码吗?
我见过人们在apache commons上引用this。这是内置的吗?或者我必须下载它吗?
如果我必须自己做,你能给我一些提示吗?如果我下载,我的教授是否还必须安装“软件包”或特殊文件?
提前致谢!
编辑:刚发现我不能使用库,也听说有更简单的方法使用弧度转换正常。
答案 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,他们对结果感到满意。