Переполнение: ошибка математического диапазона в рекурсивной функции

Я пытаюсь вычислить корень 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

Так в чем проблема? что вызывает ошибку математического диапазона?


person Shakashazaam    schedule 16.04.2014    source источник
comment
Конкретное исключение начинает выдаваться с fdiv(711) или старше (или для -711 и ниже).   -  person Martijn Pieters    schedule 16.04.2014
comment
Ваша функция Raphson(5) возвращает -5501.61643 вместо rx0 = 5, которые затем возвращаются обратно в вашу рекурсивную функцию, которая затем передает это новое значение fdiv(); -5501 намного ниже, чем -711.   -  person Martijn Pieters    schedule 16.04.2014
comment
Да, так вот о чем я и спрашиваю. если все для 5 не дает большого числа, почему весь алгоритм должен дать сбой с ошибкой математического диапазона.   -  person Shakashazaam    schedule 16.04.2014
comment
Потому что следующий шаг в рекурсии дает большое (отрицательное) число.   -  person Martijn Pieters    schedule 16.04.2014
comment
хорошо, чувак, я понимаю, что ты имеешь в виду. Я просто думал, что он хотя бы напечатает строку со значением -5501.61643 и затем выдаст ошибку. Но я предполагаю, что вся функция оценивается перед печатью новой строки. Спасибо   -  person Shakashazaam    schedule 16.04.2014
comment
Строка if fdiv(rx0) == 0 выполняется до вызова функции print().   -  person Martijn Pieters    schedule 16.04.2014
comment
@MartijnPieters Спасибо!   -  person Shakashazaam    schedule 16.04.2014


Ответы (2)


Вызов Raphson(5) возвращает большое отрицательное число:

>>> Raphson(5)
-5501.616437351696

Это передается рекурсивному вызову:

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

поэтому Q1rx0Raphson() вызывается с -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] дают результат.

person Martijn Pieters    schedule 16.04.2014

Длинный комментарий: не могли бы вы уточнить, как вы получили производную? Обычный подход с помощью формулы отношения для производных дает просто

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 - ch(2*x) = -2*sinh(x)**2

Таким образом, область сжимаемости определяется ch(2*x)‹2, что эквивалентно

|x|<0.5*ln(2+sqrt(3))=ln(1+sqrt(3))-0.5*ln(2)
person Lutz Lehmann    schedule 17.04.2014
comment
Да мужик, ты прав. Производное происходит от ленивого копипаста от wolframalpha. На самом деле, используя простое представление, максимальное начальное значение до ошибки переполнения теперь составляет ~355 (вместо 178). Спасибо. - person Shakashazaam; 17.04.2014
comment
Да, я вижу, что это дано как альтернативная форма. Однако, каково обоснование этой формы, ускользает от меня. ch(x)›=1 для всех действительных x, так что регуляризация или ухищрения не нужны. А обычная альтернативная форма tanh**2-1 не дается... - person Lutz Lehmann; 17.04.2014