【问题标题】:Compute a derivative using discrete methods使用离散方法计算导数
【发布时间】:2009-03-09 16:49:09
【问题描述】:

我正在寻找一种使用离散且快速的方法来计算导数的方法。由于现在我不知道我所拥有的方程的类型,因此我正在寻找类似于我们可以找到的积分方法的离散方法,例如欧拉方法。

【问题讨论】:

    标签: discrete-mathematics derivative


    【解决方案1】:

    我认为您正在寻找以点计算的导数。 如果是这种情况,这里有一个简单的方法可以做到这一点。你需要知道一点的导数,比如a。它由 h->0 的差商的极限给出:

    您实际上需要实现限制功能。所以你:

    • 定义一个 epsilon,设置得越小越精确,越大越快
    • 计算起始h的差商,假设h=0.01,存入f1
    • 现在处于 DO-WHILE 循环中:

      1- 将 h 除以 2(或除以 10,重要的是使其更小)
      2- 用新的 h 值再次计算差商,将其存储在 f2
      3- 设置 diff = abs(f2-f1)
      4- 分配 f1 = f2
      5- 从第 1 点重复 while (diff>epsilon)

    • 您最终可以将 f1(或 f2)作为 f'(a) 的值返回

    记住: 您假设函数在 a 中是可微的。 由于您的计算机可以处理的有限十进制数字的错误,您将得到的每个结果都是错误的,这是无法逃脱的。

    python中的示例:

    def derive(f, a, h=0.01, epsilon = 1e-7):
        f1 = (f(a+h)-f(a))/h
        while True: # DO-WHILE
            h /= 2.
            f2 = (f(a+h)-f(a))/h
            diff = abs(f2-f1)
            f1 = f2
            if diff<epsilon: break
        return f2
    
    print "derivatives in x=0"
    print "x^2: \t\t %.6f" % derive(lambda x: x**2,0)
    print "x:\t\t %.6f" % derive(lambda x: x,0)
    print "(x-1)^2:\t %.6f" % derive(lambda x: (x-1)**2,0)
    
    print "\n\nReal values:"
    print derive(lambda x: x**2,0)
    print derive(lambda x: x,0)
    print derive(lambda x: (x-1)**2,0)
    

    输出:

    derivatives in x=0
    x^2:         0.000000
    x:       1.000000
    (x-1)^2:     -2.000000
    
    
    Real values:
    7.62939453125e-08
    1.0
    -1.99999992328
    

    我第一次得到“精确”值“因为只使用了结果的前 6 位数字,请注意我使用 1e-7 作为 epsilon。之后打印 REAL 计算值,它们显然是数学上的错误。选择多小的 epsilon 取决于您希望结果有多精确。

    【讨论】:

      【解决方案2】:

      在计算数值(“有限”)导数方面有相当多的理论(和既定实践)。让所有细节正确,让您相信结果,这并非易事。如果有任何方法可以获得函数的解析导数(使用笔和纸,或计算机代数系统,例如 MapleMathematicaSageSymPy),这是迄今为止最好的选项。

      如果你不能得到解析形式,或者你不知道函数(只是它的输出),那么数值估计是你唯一的选择。这个chapter in Numerical Recipies in C 是一个好的开始。

      【讨论】:

      • 请记住,Numerical Recipes 中的代码是[非公共领域][1] 或开源的。复制风险自负。 [1]:nr.com/public-domain.html
      • 是的,当然。我不打算在 C(或任何其他受版权保护的来源)中窃取 NR 的代码。不过,您可能会从阅读本章中学到足够的知识,以便您可以编写自己的代码;)
      【解决方案3】:

      一种简单的方法是针对您感兴趣的导数的每个点计算 f 在一个小值上的变化。例如,要计算 ∂f/∂x,您可以使用以下方法:

      epsilon = 1e-8
      ∂f/∂x(x, y, z) = (f(x+epsilon,y,z) - f(x-epsilon, y, z))/(epsilon * 2);
      

      其他部分在 y 和 z 中是相似的。

      为 epsilon 选择的值取决于 f 的内容、所需的精度、使用的浮点类型以及可能的其他因素。我建议你用你感兴趣的函数来试验它的值。

      【讨论】:

      • 除非您真的想深入研究您碰巧使用的任何语言的浮点实现,否则很难选择一个好的 epsilon。但是请发布您发现的奇怪的浮点错误!
      【解决方案4】:

      要进行数值微分,这始终是一个近似值,有两种常见的情况:

      1. 您有一种方法(算法、方程)来计算任何给定 x 处的 f(x) 的值,或者
      2. f(x) 的值位于一组等距的 x 值(f(1)、f(1.5)、f(2) 等):


      1. 如果你有算法,那么你最好看看Andrea Ambu's answer*:
        1. 从 f(a+h) 和 f(a-h) 的值开始,然后执行
          f'(a) = { f(a+h)-f(a-h) } / 2h
          这被称为中心差异。
        2. 根据 Andrea 的回答,减小偏移量 h 的大小直到 f'(a) 停止变化。
        • 注意不要在不检查是否大于 0 的情况下除以 2h,否则会出现除以零错误。
      2. 如果函数的值在 f(x) = f(xn) = fn 的一组值,那么我们可以做一些事情类似于上述方法,但我们的准确性将受到 xn 的值和它们之间的差距的限制。
      3. 第一个近似值只是使用与上面相同的中心差分算子;
        f'n = (fn+1 - fn-1)/2h,
        其中 h 是 xn 的值之间的相等间距。
      4. 接下来是使用five-point stencil,尽管它只有 4 个系数,如该维基百科页面所述。这使用从 fn-2 到 fn+2 的值: f'n = (-fn+2 + 8fn+1 - 8fn-1 +fn-2)/12h.
      5. 存在使用更多点的更高阶近似值,但随着收益递减,计算变得越来越困难,并且在数值上变得更加不稳定。
      6. **注意**:这些有限差分公式依赖于 f 与 xn-1≤ x ≤n+1 范围内的多项式的形状大致相同子>。对于像正弦波这样的函数,它们在计算导数方面可能非常糟糕。

      * Andrea 的答案使用前向差分算子 {f(a+h) - f(a)}/h 而不是中心差分算子 {f(a+h) - f(a-h)}/2h,但前向差分算子在数值解中精度较低。

      【讨论】:

        【解决方案5】:

        不使用像 Maple 这样的符号数学语言,您能做的最好的事情就是在各个点近似导数。 (然后插值,如果你需要一个函数。)

        如果你已经有了想要使用的功能,那么你应该使用backward divided-difference forumlaRichardson extrapolation来改善你的错误。

        还要记住,这些方法适用于一个变量的函数。但是,每个变量的偏导数将其他变量视为常数。

        【讨论】:

        • richardson 外推法很有用,但为什么是向后除法而不是居中?
        • 这些似乎是两个变量方法,而不是问题中的多变量方法。我错过了什么吗?如果不是,我不建议将两个变量方法用于更多变量。这真的很容易出错。
        【解决方案6】:

        Automatic differentiation 是做这种事情的最准确和概念上最棒的方式。稍微复杂一点。

        【讨论】:

          【解决方案7】:

          正式地说,不。您正在描述离散函数的(偏)导数,或者您正在寻求一种数值方法来逼近连续函数的(偏)导数。

          离散函数没有导数。如果您查看导数的 epsilon-delta 定义,您会发现您需要能够在接近您想要导数的点处评估函数。如果函数的值只有 x、y 和 z 的整数值,这没有任何意义。所以没有办法找到一个离散函数对任何fast值的导数。

          如果您想要一种数值方法来精确计算连续函数的导数,那么您也很不走运。导数的数值方法是启发式的,而不是算法的。没有数值方法可以保证精确解。幸运的是,存在许多好的启发式方法。 Mathematica 默认使用 Brent's principle axis method 的专用版本。我建议你使用GNU Scientific Library,它很好地实现了布伦特的方法。我的一门数学课程的全部成绩都归功于 GSL。如果这是你的事,红宝石绑定非常好。如有必要,大多数数值微分库都有一些可用的不同方法。

          如果你真的想要,我可以拿出一些示例代码。告诉我。

          【讨论】:

          • 您好,非常感谢您提供所有信息,我希望找到一些简单的东西,因为我的问题似乎没有简单的东西,我将采用任何其他不使用衍生品的方法。不过非常感谢。
          【解决方案8】:

          我假设您的函数比您发布的简单函数更复杂,因为封闭式解决方案太简单了。

          当您使用“离散”一词时,我认为您需要“有限差异”。您需要一些离散化来计算近似值。

          Df/Dx ~ (f2-f1)/(x2-x1)等

          【讨论】:

            【解决方案9】:

            我希望这可能会有所帮助NUMERICAL DIFFERENTIATION.

            【讨论】:

              【解决方案10】:

              如果函数如您所指出的那样是线性的,那么导数是微不足道的。关于“x”的导数是“a”;关于“y”的导数是“b”,关于“z”的导数是“c”。如果方程是更复杂的形式,并且您需要一个表示解的公式而不是经验解,那么请提交更复杂的方程形式。

              问候

              【讨论】:

                猜你喜欢
                • 2012-04-30
                • 1970-01-01
                • 2023-03-31
                • 2017-05-13
                • 1970-01-01
                • 2017-04-14
                • 1970-01-01
                • 2017-04-05
                相关资源
                最近更新 更多