Python Newton Raphson Decimal Roots

时间:2016-12-23 20:35:30

标签: python decimal precision

我有这个功能(Newton-Raphson算法): "数字":所需的根精度

from scipy.misc import derivative

def newtonDigits(function,xstart,digits):


    xprev=0
    ncalls=0

    while abs(xstart-xprev) >= 0.5 * 10**(-digits):

        ncalls +=1
        print xstart

        x = xstart - (function(xstart)/derivative(function,xstart,dx=1e-6))


        xprev = xstart
        xstart = x


    return xstart,ncalls

输入是:

f = lambda x: 14*x*(math.e)**(x-2) - 12*(math.e)**(x-2) - 7 *(x**3) + 20*(x**2) - 26*x + 12

root = newtonDigits(f,1.9,6)
print "Root: {0:.6f}".format(root[0])
print "Number of loops: N=" + str(root[1])

输出是:

1.9
1.9347284759
1.9570567413
1.97161234354
1.98117864941
1.98749755734
1.99168484127
1.99446528727
1.99631400887
1.99754426317
1.9983635866
1.99890940938
1.9992730728
1.99951577542
1.99967709739
1.99978645669
1.99985605669
1.99990268169
1.9999354436
1.9999606936
1.9999726936
1.9999806936
1.9999846936
newtonr.py:55: RuntimeWarning: divide by zero encountered in double_scalars
  x = xstart - (function(xstart)/derivative(function,xstart,dx=1e-6))
mainfile.py:6: RuntimeWarning: invalid value encountered in double_scalars
  f = lambda x: 14*x*(math.e)**(x-2) - 12*(math.e)**(x-2) - 7 *(x**3) + 20*(x**2) - 26*x + 12
inf
Root: nan
Number of loops: N=24

我尝试使用Decimal,但我有浮点数和lambda函数的问题。 有什么想法吗?

提前致谢

2 个答案:

答案 0 :(得分:2)

Newton-Raphson对于导数为零的根不起作用。您的问题就是这种情况,如图所示。

enter image description here

事实上,任何导数为零的根都难以获得高精度,并且这适用于任何数字根寻找方法。你的情况更糟,因为一阶和二阶导数在根处都是零。这意味着函数的图形非常接近根部的直线水平线。鉴于此,难以获得比有效位总数的三分之一更好的精度。大多数计算机将提供双倍精度,大约15位数,因此难以超过5位有效数字。你的例程已经给出了这一点,所以不要期望从任何数字例程中获得更好的效果。

当您将导数的精度设为1e-6derivative()调用中的参数)时,尤其会出现问题,并且当x值在x中获得相同的精度时出现问题

你可以使用Newton-Raphson的一些变体来避免这个和其他问题 - 记住Newton-Raphson不能保证在一般情况下收敛并且有多个困难。看看 Numerical Recipes 一书中的rtsafe,作为N-R(Newton-Raphson)的安全用法。

如果你想非常接近N-R,你可以在计算下一个x值之前修改你的代码以检查导数的值。当导数变为零时,只需停止循环。

如果您真的希望在像您这样的情况下使用直线N-R更精确,则需要更好地计算导数。你使用的数字衍生物显然不是很好。使用评估点周围两点的对称差异,这个数值导数可能给出了二阶结果,这在这里还不够好。您可以使用更复杂的派生例程,该例程使用更多的点并提供更高阶的结果。但在你的情况下,这个函数非常简单,你可以编写自己的函数,为你的原始函数提供一个很好的导数。只需使用基本的微积分规则,直到产品规则。实际上,任何实际的Newton-Raphson例程都应该被赋予一个计算导数的函数,除非必要,否则你通常应该使用近似的数值导数。有人可能会争辩说,使用数字导数意味着你实际上并没有使用Newton-Raphson而你应该使用另一种方法来找到根 - 本书 Numeric Recipes 实际上就是这一点。

修改你的代码以使用定义的派生函数,以及清理你的一些风格,给出

import math

def newtonDigits(function, dfunction, xstart, digits):
    xprev=0
    ncalls=0
    while abs(xstart-xprev) >= 0.5 * 10**(-digits):
        ncalls +=1
        print(xstart)
        x = xstart - function(xstart) / dfunction(xstart)
        xprev = xstart
        xstart = x
    return xstart, ncalls

f = lambda x: 14*x*(math.e)**(x-2) - 12*(math.e)**(x-2) - 7*x**3 + 20*x**2 - 26*x + 12
df = lambda x: 14*x*(math.e)**(x-2) + 2*(math.e)**(x-2) - 21*x**2 + 40*x - 26

root = newtonDigits(f, df, 1.9, 6)
print("Root: {0:.6f}".format(root[0]))
print("Number of loops: N=" + str(root[1]))

打印

1.9
1.9347284749567717
1.9570567399626235
1.9716123428786936
1.9811786535860885
1.987497587688875
1.991684850749086
1.9944652840851569
1.9963140403238206
1.9975443983678638
1.9983636880398847
1.998909460162945
1.9992731216554642
1.9995154801019759
1.9996770218573676
1.999784687490283
1.9998564592822783
1.9999043185996854
1.9999363386081155
1.9999575985093117
1.9999719257797395
1.999982068293325
1.9999875928805002
2.00000490230375
Root: 2.000005
Number of loops: N=24

答案 1 :(得分:0)

如果您在每个步骤中打印xderivative的值,您会看到:

1.9347284759 0.0683501539811
1.95705673873 0.030810070939
1.97161234728 0.0138147377982
...
1.99997209174 1.42108547152e-08
1.99997909174 7.1054273576e-09
inf 0.0
nan nan

我们可以看到导数给出零,并且除以零给我们inf。

如果使用更大的dx收敛。对于dx=1e-3

...
1.99991975788 2.71786149142e-06
1.99992026519 2.7172077921e-06
1.99992075954 2.7165576455e-06
Root: 1.999921
Number of loops: N=95