两个版本的导数计算精度有什么区别?

What's the accuracy difference between the two versions derivative calculation?

以下是Python代码,旨在计算给定函数f的导数。

  1. 版本一(解决方案)

    x[ix] += h # increment by h
    fxh = f(x) # evalute f(x + h)
    x[ix] -= 2 * h 
    fxnh = f(x)
    x[ix] += h
    numgrad = (fxh - fxnh) / 2 / h
    
  2. 版本二(我的版本)

    fx = f(x) # evalute f(x)
    x[ix] += h 
    fxh = f(x) # evalute f(x+h)
    x[ix] -= h
    numgrad = (fxh - fx) / h
    

它表明版本一给出了更好的准确性,谁能解释为什么会这样,两种计算有什么区别?

更新 我一开始没有意识到这是一个数学问题,我认为这是一个与浮动精度影响有关的问题。正如 MSeifert 所建议的,我同意浮点噪声很重要,当暴露在噪声中时,小幅度结果更容易受到影响。

你的解是"one-sided",你比较f(x+h) - f(x)一般的解是"two-sided"f(x+h) - f(x-h).

最好知道是什么

It has shown version one gives a better accuracy

意思是。因为那太笼统了。

但我想我有一个可能适合这里的例子:

def double_sided_derivative(f, x, h):
    x_l, x_h = x - h, x + h
    return (f(x_h) - f(x_l)) / 2 / h

def one_sided_derivative(f, x, h):
    x_h = x + h
    return (f(x_h) - f(x)) / h

h = 1e-8

def f(x):
    return 1e-6 * x

# difference to real derivate:
double_sided_derivative(f, 10, h) - 1e-6, one_sided_derivative(f, 10, h) - 1e-6
# (6.715496481486314e-14, 1.5185825954029317e-13)

注意双面结果更接近预期值。这甚至可能导致 catastrophic cancellation。然后你可能会得到一个主要由浮点噪声控制的结果。由于该值除以非常小的数字,因此进一步增强了这种效果。

通过使用两侧,您可以增加(取决于您的功能!)差异,从而增加可能发生取消的点。但在我看来,最大的优势是你考虑了两侧的坡度(稍微平均一下)。例如:

h = 1e-6

def f(x):
    return 4 + x + 5 * x**2

def fdx(x):
    return 1 + 10 * x

x = 10

double_sided_derivative(f, x, h) - fdx(x), one_sided_derivative(f, x, h) - fdx(x)
# (-2.7626811061054468e-08, 4.974594048690051e-06)

这比单边近似更接近真实值(两个数量级)。

这不是 Python 问题,而是一个纯算法问题。假设函数 f 有很好的属性,你可以看看它的 Taylor series 发展:

f(x+h) = f(x) + h f'(x) + h*h/2 f"(x) + h*h*h/6 f'''(x) + o(h3)

原来你的第一个表格给出了错误:

((f(x+h) - f(x)) /  h) - f'(x) = h/2 f"(x) + o(h)

这是h的量级误差

如果你使用第二种形式,你会得到:

((f(x+h) - f(x-h)) / 2*h) - f'(x) = h*h/3 f'''(x) + o(h2)

h中的项下降了,误差在h的数量级2

当然,只有存在所需的导数才有意义...