我有一个程序应该使用牛顿法找到第 ** 次埃尔米特多项式的根,但运行该程序需要很长时间。我对 C 很陌生,所以我不知道我的错误在哪里,或者这是否只是暴力破解这个问题的本质。我也遇到了获取准确根的问题,但到目前为止,很难找到那个错误,因为我只能每 5-10 分钟运行一次测试用例
代码已删除
我 100% 确定 Newton-Raphson 没有充分的理由花这么多时间。在某些情况下,它可能会出现问题,因为这种方法不能保证收敛。但在您的具体情况下 - 应该没有问题。
一件很清楚的事情是你过度使用递归。仅用 n=37计算你hermite
的递归是一个复杂的递归,它有点像 37 个斐波那契数的总和,大约是40 百万。
现在,认为您的newton
方法应该重复调用hermite
以及h_deriv
(具有相同数量级的递归),直到 in 收敛到10^-12
. 听起来像几十次互动。
而且,这还不够,您还可以newton
递归地实现!世界上真的没有理由这样做。(lisp/scheme 是你的第一门编程语言吗?)
这是您应该做的以提高性能:
修复你的hermite
. 您应该计算 37 个系数,这可以递归完成。一旦完成 - 您应该使用它们在正常时间内计算多项式的值。
导数也一样。只需计算 36 个系数。
可选择修复您的newton
. 据我所知 - 你不会获得太多的性能:你的“递归”是一个尴尬的循环。但是它看起来会更好,并且消耗更少的堆栈。
编辑:
阅读评论后,我花时间尝试构建和运行它。而且,我必须承认,我低估了问题的复杂性。
事实证明,由递归关系计算的系数增长迅速,并且舍入误差似乎占主导地位。因此,通过蛮力解决这个问题具有不可避免的影响,并且使用预先计算的系数(并将它们按直接顺序求和)产生相同的结果并不明显。
尽管如此,还是有一种方法可以在不改变计算逻辑的情况下摆脱荒谬的递归:
const int N = 37;
double g_pHermiteValues[N+1];
void CalcHermiteAt(double x)
{
double x2 = x*2;
g_pHermiteValues[0] = 1.;
g_pHermiteValues[1] = x2;
for (int n = 2; n <= N; n++)
g_pHermiteValues[n] =
g_pHermiteValues[n - 1] * x2 -
g_pHermiteValues[n - 2] * 2*(n - 1);
}
double CalcHermiteDerivAt()
{
return g_pHermiteValues[N - 1] * 2*N;
}
double newton(double x_0)
{
const double tolerance = 1E-12;
while (true)
{
CalcHermiteAt(x_0);
if (abs(g_pHermiteValues[N]) < tolerance)
return x_0;
x_0 -= g_pHermiteValues[N] / CalcHermiteDerivAt();
}
}
也就是说,我们使用相同的递归关系。只是为了计算 Hermite 多项式在给定点的值,我们对直到 n=37 的所有多项式进行迭代计算,并将结果存储在全局数组中。然后它的顶部元素保存所需的结果,并且导数也从末尾的第二个数组元素推导出来。
因为在 Newton-Raphson 算法的每一步中,我们都需要在同一点上的值和导数——这是有效的。
PS但是到目前为止,我无法找到解决方案。Newton-Raphson 对于我试图开始的点并没有收敛。
我相信对于这样的问题,可以使用更稳健的方法,例如中值搜索。