递归函数中的数学范围错误

0 投票
2 回答
676 浏览
提问于 2025-04-18 02:57

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

我使用的是牛顿-拉夫森方法,这个算法需要一个初始猜测值。

这个算法应该在初始猜测值大于大约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

那么问题出在哪里呢?是什么导致了数学范围错误?

2 个回答

0

长评论:你能详细说明一下你是怎么得到这个导数的吗?通常用导数的商法则来计算,结果只是简单的

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)<2 定义的,这等同于

|x|<0.5*ln(2+sqrt(3))=ln(1+sqrt(3))-0.5*ln(2)
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] 这个范围内的值才会得到结果。

撰写回答