【问题标题】:Overflow: math range error in a recursive function溢出:递归函数中的数学范围错误
【发布时间】:2014-05-31 03:27:14
【问题描述】:

我正在尝试计算 tanh(x) 的根作为练习。

我正在使用 Newton-Raphson 方法,该算法要求进行初始猜测

该算法假定对于大于约 1 的初始猜测不会收敛。但在它到达之前我得到了一个数学范围错误。

这是我正在使用的代码

from math import *
def f(x):#define the function
    return tanh(x)

def fdiv(x):#define its derivative
    return 4*(cosh(x))**2/(cosh(2*x)+1)**2

def Raphson(rx0):
    return (rx0-f(rx0)/fdiv(rx0))#according to the Newton Raphson Method

def Q1_6_Raphson(rx0,Iter=1):
    if Iter > 30:#maximum iterations allowed is 30
        print("Newton Raphson Algorithim did not converge after 30 Trials, try other initial         guesses")
        return
    elif fdiv(rx0)==0:
        print("The initial guess you chose leads to diving by zero in the Newton-Raphson method. Choose another guess")
        return
    print(Iter, 'Newton-Raphson ' +str(rx0) +' error ' +str(rx0-(0)))
    if rx0==0:
        return
    else:
        return Q1_6_Raphson(Raphson(rx0),Iter=Iter+1) # call the function recursively

例如,当我尝试运行 Q1_6Raphson(5) 时,我得到:

Traceback (most recent call last):
  File "<pyshell#101>", line 1, in <module>
Q1_6_Raphson(5)
  File "C:\Users\AsafHaddad\Documents\סמסטר 8\חישובית\Targil_3\Question1.6.py", line 40, in Q1_6_Raphson
    return Q1_6_Raphson(Raphson(rx0),Iter=Iter+1) # call the function recursively
  File "C:\Users\AsafHaddad\Documents\סמסטר 8\חישובית\Targil_3\Question1.6.py", line 33, in Q1_6_Raphson
    elif fdiv(rx0)==0:
  File "C:\Users\AsafHaddad\Documents\סמסטר 8\חישובית\Targil_3\Question1.6.py", line 21, in fdiv
    return 4*(cosh(x))**2/(cosh(2*x)+1)**2
OverflowError: math range error

根据我的阅读,当数字太大时会发生数学范围错误。但我不明白的是,我的代码中调用的每个函数都可以使用 5 作为输入:

>>> f(5)
0.9999092042625951
>>> fdiv(5)
0.00018158323094380672
>>> Raphson(5)
-5501.616437351696

那么问题是什么?什么触发了数学范围错误?

【问题讨论】:

  • 在fdiv(711)或以上(或-711及以下)开始抛出特定异常。
  • 您的Raphson(5) 函数为rx0 = 5 返回-5501.61643,然后将其反馈给您的递归函数,然后将新值传递给fdiv(); -5501 远低于 -711。
  • 是的 - 这就是我要问的。如果 5 的所有内容都没有给出很大的数字,为什么整个算法会因数学范围错误而失败。
  • 因为递归中的下一步给出了一个很大的(负)数。
  • 好的,伙计,我明白你的意思了。我只是认为它至少会打印值为 -5501.61643 的行,然后 then 给出错误。但我猜想在打印新行之前评估整个函数。谢谢

标签: python python-3.x newtons-method


【解决方案1】:

Raphson(5) 调用返回一个很大的负数:

>>> Raphson(5)
-5501.616437351696

这被传递给递归调用:

return Q1_6_Raphson(Raphson(rx0),Iter=Iter+1)

所以Q1_6_Raphson() 是用-5501.616437351696 作为rx0 参数调用的。然后将其传递给fdiv():

elif fdiv(rx0)==0:

抛出异常,因为该数字大于math.cosh() 可以处理的数字:

>>> fdiv(-5501.616437351696)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fdiv
OverflowError: math range error

任何超出范围 [-710, +710] 的值都会抛出该异常;在那个范围内你会得到一个不同的例外:

>>> fdiv(-710)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fdiv
OverflowError: (34, 'Result too large')

因为您仍然超出平台浮点支持的限制。

只有 [-177, +177] 范围内的值才会给出结果。

【讨论】:

    【解决方案2】:

    长评:您能否详细说明您是如何获得衍生品的?通过导数的商公式的正常方法只是简单地给出了

    d/dx tanh(x)=(cosh(x)**2-sinh(x)**2)/cosh(x)**2
                =1-tanh(x)**2         or
                =1/cosh(x)**2
    

    自从

    cosh(2x)=cosh(x)**2+sinh(x)**2=2*cosh(x)**2-1, 
    

    您的衍生金额为

    4*(cosh(x))**2/(cosh(2*x)+1)**2 = 1/cosh(x)**2
    

    这样它会给出正确的结果,但这是一个不必要的复杂公式。


    注意:在这个特定的例子中,牛顿迭代可以简化为

    xnext = x - 0.5*sinh(2*x)
    

    这个的导数是

    d(xnext)/dx = 1 - cosh(2*x) = -2*sinh(x)**2

    因此,收缩域由 cosh(2*x)

    |x|<0.5*ln(2+sqrt(3))=ln(1+sqrt(3))-0.5*ln(2)
    

    【讨论】:

    • 是的,伙计,你是对的。导数来自 wolframalpha 的惰性复制粘贴。事实上,使用简单的表示,溢出错误之前的最大初始值现在约为 355(而不是 178)。谢谢。
    • 是的,我看到它是作为“替代形式”给出的。然而,这种形式的基本原理是什么,我不知道。对于所有实数 x,cosh(x)>=1,因此不需要正则化或技巧。并且没有给出通常的替代形式 tanh**2-1...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-02
    • 1970-01-01
    • 2017-06-18
    • 2016-06-25
    • 2017-09-05
    相关资源
    最近更新 更多