我问了一个关于将Excel的BetaInv函数移植到.NET的问题:BetaInv function in SQL Server
现在我设法用纯依赖减去C#代码编写该函数,并且我得到的结果与MS Excel中的结果相同,最多6或7位逗号,结果对我们来说很好,问题是这样的代码是嵌入式的在SQL CLR函数中,从存储过程调用数千次,并使整个过程的执行速度降低50%,如果我使用该函数,则从30秒到一分钟。
这里有一些代码,我不是要深入分析,但有没有人在我进行此计算的方式中看到任何重大性能问题?比如使用其他数据类型而不是双打或任何......?
private static double betacf(double a, double b, double x)
{
int m, m2;
double aa, c, d, del, h, qab, qam, qap;
qab = a + b;
qap = a + 1.0;
qam = a - 1.0;
c = 1.0; // First step of Lentz’s method.
d = 1.0 - qab * x / qap;
if (System.Math.Abs(d) < FPMIN)
{
d = FPMIN;
}
d = 1.0 / d;
h = d;
for (m = 1; m <= MAXIT; ++m)
{
m2 = 2 * m;
aa = m * (b - m) * x / ((qam + m2) * (a + m2));
d = 1.0 + aa * d; //One step (the even one) of the recurrence.
if (System.Math.Abs(d) < FPMIN)
{
d = FPMIN;
}
c = 1.0 + aa / c;
if (System.Math.Abs(c) < FPMIN)
{
c = FPMIN;
}
d = 1.0 / d;
h *= d * c;
aa = -(a + m) * (qab + m) * x / ((a + m2) * (qap + m2));
d = 1.0 + aa * d; // Next step of the recurrence (the odd one).
if (System.Math.Abs(d) < FPMIN)
{
d = FPMIN;
}
c = 1.0 + aa / c;
if (System.Math.Abs(c) < FPMIN)
{
c = FPMIN;
}
d = 1.0 / d;
del = d * c;
h *= del;
if (System.Math.Abs(del - 1.0) < EPS)
{
// Are we done?
break;
}
}
if (m > MAXIT)
{
return 0;
}
else
{
return h;
}
}
private static double gammln(double xx)
{
double x, y, tmp, ser;
double[] cof = new double[] { 76.180091729471457, -86.505320329416776, 24.014098240830911, -1.231739572450155, 0.001208650973866179, -0.000005395239384953 };
y = xx;
x = xx;
tmp = x + 5.5;
tmp -= (x + 0.5) * System.Math.Log(tmp);
ser = 1.0000000001900149;
for (int j = 0; j <= 5; ++j)
{
y += 1;
ser += cof[j] / y;
}
return -tmp + System.Math.Log(2.5066282746310007 * ser / x);
}
答案 0 :(得分:3)
对我来说唯一突出的是内存分配,而且通常是性能损失。我不知道调用gammln
的频率,但您可能希望将double[] cof = new double[] {}
移动到静态一次性分配。
答案 1 :(得分:0)
双通常是最好的类型。特别是因为Math
中的函数占用了两倍。不幸的是,我发现你的代码没有明显的改进。
有可能使用查找表来获得更好的第一次估计,但是由于我不知道你正在做什么背后的数学,我不知道在这个特定情况下是否可能
显然,较大的epsilons会提高性能。因此,在满足您的准确性要求的同时,尽可能选择它。
如果使用相同的参数重复调用该函数,则可以缓存结果。
有一点看起来很奇怪的是你强制c,d,......的小值到FPMIN
的方式。我的直觉是,这可能会导致步长不够理想。
答案 2 :(得分:0)
我所拥有的只是在j
中展开gammln
循环,但它最多只会产生微小的差异。
更激进的想法是在纯T-SQL中重写,因为它包含您使用的所有内容:+ - * / abs log
全部可用。