OverflowError "численный результат вне диапазона" при генерации чисел Фибоначчи [дубликат]

Возможные Дубликаты:
обработка очень больших чисел в Python

у меня есть функция Python для генерации чисел Фибоначчи:

def fib(n):                                                                                                            
        return ((1+math.sqrt(5))**n - (1-math.sqrt(5))**n)/(2**n*math.sqrt(5))

Я могу кормить номера функций fib до 700, где он начинает

OverflowError: (34, 'Numerical result out of range')

мне нужно использовать специальный тип, как долго, чтобы обойти это?

4 ответов


проблема в том, что вы используете двойники для вычисления значения, и двойники переполнены. Двойники дают точные решения только около 85 числа Фибоначчи.

если вы хотите быстрый и точный расчет, вам лучше использовать алгоритм, основанный на лучшей рекуррентной связи, и использовать целые числа python bignum.

в частности, вы можете использовать:

 fib(2*n) = fib(n)^2 + fib(n-1)^2
 fib(2*n-1) = fib(n)*(2*fib(n-1)+fib(n))

или эквивалентная формула возведения в степень матрицы (извините уродливый форматирование)

 [ F_n     F_{n-1} ]      [ 1   1 ] ^N 
 [                 ]  =   [       ]
 [ F_{n-1} F_{n-2} ]      [ 1   0 ]

оба эти результата в алгоритмах, которые требуют O(log(N)) расчеты, а не O(N).

здесь полное решение в псевдо-коде


если вы хотите выполнить свои вычисления с использованием двойников и явных формул, то формулы можно настроить, чтобы дать что-то быстрее, что не переполняется до примерно 1500-го числа Фибоначчи, и остается той же точностью, что и ваша версия. IIRC it есть:

def fib(n):                                                                                                            
    return round( ((1+math.sqrt(5))/2)**n / math.sqrt(5) )

легко изолировать ошибку

>>> (1+math.sqrt(5))**700
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: (34, 'Numerical result out of range')

этот метод не будет работать, так как числа с плавающей запятой не хватает точности

например, здесь

>>> (1+math.sqrt(5))**600
1.024664165563927e+306

вы работаете только первые 15 или около того цифр. Остальные 291 будут рассматриваться как нули, когда вы делаете любую арифметику

посмотреть Википедия подробнее о проблемы точности с числами с плавающей запятой


вы всегда можете попробовать такой подход:

def fib(n, memo={0:0, 1:1}):
    if n not in memo:
        memo[n] = fib(n-1) + fib(n-2)
    return memo[n]

print fib(800)

выход:

69283081864224717136290077681328518273399124385204820718966040597691435587278383112277161967532530675374170857404743017623467220361778016172106855838975759985190398725


если вы действительно хотите использовать этот алгоритм, и вы хотите работать за пределами встроенный float, тогда да, вам нужен другой тип.

если все, что вы хотите, это получить приблизительный ответ вместо исключения, это легко; вы можете получить бесконечный диапазон из коробки. Но если вы также хотите устранить ошибки округления, вы не можете иметь бесконечную точность (это займет бесконечное время/пространство), поэтому вы должны знать, как разработать точность, необходимую для вашего диапазон входных данных. (Я оставлю это упражнение для читателя.)

стандартный тип библиотеки decimal.Decimal может быть все, что вам нужно. Он обеспечивает произвольную точность фиксированной или десятичной арифметики с плавающей запятой в соответствии со стандартом IEEE-854. Есть много случаев, для которых он непригоден, потому что он не предоставляет достаточно математических функций, но вам нужна только базовая арифметика и sqrt, которые в полном порядке. Он также может быть медленным для огромных чисел, но если вы просто хотите рассчитать fib на нескольких трехзначных числах этого более чем достаточно.

, когда Decimal недостаточно, существует ряд сторонних модулей, обычно обертывающих отраслевые стандартные библиотеки C, такие как gmp/mpfr, такие как bigfloat.

вот как получить бесконечный диапазон, но с ошибками округления примерно в том же масштабе, что и встроенный float:

>>> s5 = decimal.Decimal(5).sqrt()
>>> def fib(n):
...     return ((1+s5)**n - (1-s5)**n)/(2**n*s5)
>>> fib(800)
Decimal('6.928308186422471713629008226E+166')
>>> int(fib(800))
69283081864224717136290082260000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000L
>>> s5 = bigfloat.sqrt(5)
>>> def fib(n):
...     return ((1+s5)**n - (1-s5)**n)/(2**n*s5)
>>> fib(800)
BigFloat.exact('6.9283081864226567e+166', precision=53)
>>> int(fib(800))
69283081864226566841137772774650010139572747244991592044952506898599601083170460360533811597710072779197410943266632999194601974766803264653830633103719677469311107072L

но обратите внимание, что ни один из них на самом деле не ответ, который вы получите, если вы сделали математику отлично; вы потеряли 24 цифры из-за ошибок округления. (Причина значения отличается тем, что bigfloat округляется в базе 2,decimal в базе 10.)

чтобы исправить это, вам нужно больше точности. Все библиотеки предоставляют какой-то способ изменить точность; bigfloat имеет более удобные варианты, чем большинство, но ни один не слишком обременительны:

>>> decimal.getcontext().prec = 300
>>> s5 = decimal.Decimal(5).sqrt()
>>> def fib(n):
...     return ((1+s5)**n - (1-s5)**n)/(2**n*s5)
>>> fib(800)
69283081864224717136290077681328518273399124385204820718966040597691435587278383112277161967532530675374170857404743017623467220361778016172106855838975759985190398725.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000048
>>> def fibp(n, p):
...     with bigfloat.precision(p):
...         s5 = bigfloat.sqrt(5)
...         return ((1+s5)**n - (1-s5)**n)/(2**n*s5)
>>> fibp(800, 125)
BigFloat.exact('6.92830818642247171362900776814484912138e+166', precision=125)
>>> int(fibp(800, 125))
69283081864224717136290077681448491213794574774712670552070914552025662674717073354503451578576268674564384721027806323979200718479461097490537109958812524476157132800L