我正在尝试制作一个程序来找到第1个<的第n个Fibonacci数。 n< 10 ^ 19
这是我使用动态编程的代码。
memo = {}
def fib(n):
if n in memo:
return memo[n]
if n <= 2:
f = 1
else:
f = fib(n-1) + fib(n-2)
memo[n]=f
return f
print fib(input()) % 1000000007
我的代码似乎不适用于大数字。我得到无效的响应错误。 有什么建议吗?
答案 0 :(得分:9)
当N为10 ^ 19时获得第N个斐波纳契数,如果你以天真的方式做到这一点并不适合(至少我猜它不会起作用)。
更好的方法。而这种技术适用于许多系列&#39;像这样。它被称为Fibonacci Q Matrix。
哪里
这样想:
你有一些矩阵可以将矢量A转换为B:
填写这些条目很容易。特殊的部分是现在这是一个矩阵运算符,所以如果我们想要第1000个Fibonacci数,我们只需要进行矩阵乘法。
你可以通过一个循环来完成这个任务,但它需要花费你很长时间才能达到10 ^ 19,并进行10 ^ 19次矩阵乘法(即使它们是&#39;小的)也会采取公平的态度。
相反,我们采取另一种捷径。 x ^ N可以重写为幂的乘积,它们总和为N,即
x**100 == x**90 * x**10
因此,目标是在没有进行大量计算的情况下在指数中获得大数字:
x**2
和x*x
一样困难 - 它们需要相同的时间。但x*x*x*x
给出与(x**2)**2
相同的答案,同时需要额外的乘法。当你走向更高的权力时,收益会越来越多。因此,如果你将指数分解为2的幂(任何幂都有效,但这是最简单的情况),
X**100 == X**64 * X**32 * X**4
即
X**100 == (((((X**2)**2)**2)**2)**2)**2 + ...
所以你所做的就是计算你想要达到的总权力中的两个的幂,然后取Q
矩阵中两个幂的乘积。
这似乎对我有用:
fib_matrix = [[1,1],
[1,0]]
def matrix_square(A, mod):
return mat_mult(A,A,mod)
def mat_mult(A,B, mod):
if mod is not None:
return [[(A[0][0]*B[0][0] + A[0][1]*B[1][0])%mod, (A[0][0]*B[0][1] + A[0][1]*B[1][1])%mod],
[(A[1][0]*B[0][0] + A[1][1]*B[1][0])%mod, (A[1][0]*B[0][1] + A[1][1]*B[1][1])%mod]]
def matrix_pow(M, power, mod):
#Special definition for power=0:
if power <= 0:
return M
powers = list(reversed([True if i=="1" else False for i in bin(power)[2:]])) #Order is 1,2,4,8,16,...
matrices = [None for _ in powers]
matrices[0] = M
for i in range(1,len(powers)):
matrices[i] = matrix_square(matrices[i-1], mod)
result = None
for matrix, power in zip(matrices, powers):
if power:
if result is None:
result = matrix
else:
result = mat_mult(result, matrix, mod)
return result
print matrix_pow(fib_matrix, 10**19, 1000000007)[0][1]
然后,你可以更进一步 - 它只是一个2x2矩阵,所以我们可以对它进行对角化,然后得到第n个斐波纳契数的公式,就像n的函数一样 - 没有递归。现在对我来说已经很晚了,所以我现在不打算输出它。如果有人想看到这个,请在评论中告诉我,我会将其添加进去。
答案 1 :(得分:6)
在O(n)效率下,你永远不会到达那里。与代码无关,但Dijkstra's note "In honor of Fibonacci"描述了一种在O(log(n))效率中找到F(n)的方法。
F(2n-1)= F(n-1)^ 2 + F(n)^ 2
F(2n)=(2 * F(n-1)+ F(n))* F(n)
你不仅可以,而且还可以递归。
答案 2 :(得分:2)
Python的默认recursion limit为1000(通常)。要了解您的系统的确切限制:
>>> import sys
>>> sys.getrecursionlimit()
首先,如果你想以递归的方式编写这个,并且你正在使用Python 3.2及更高版本(它看起来不像你的print
语句)那么你可以使用{{3}像这样:
import functools
@functools.lru_cache()
def fib(n):
if n <= 2:
return 1
else:
return fib(n-1) + fib(n-2)
话虽如此,对于大数字来说,这仍然不会很快。更好的方法是编写迭代解决方案,在任何给定时间,“memoize”所需的只是最后2个数字。
您当然可以使用@functools.lru_cache(maxsize=128, typed=False)
来获得更好的效果。
最终,n
与10**19
一样大,你将很难编写任何在Python中运行的内容而不会给你OverflowError
。
答案 3 :(得分:1)
我不认为你可以用这个来达到1E19,但这里是你如何避免双溢出和递归深度限制:
import decimal
import operator
def decimal_range(start, stop, step=1):
"""Provides an alternative to `xrange` for very high numbers."""
proceed = operator.lt
while proceed(start, stop):
yield start
start += step
def fib(n):
"""
Computes Fibonacci numbers using decimal.Decimal for high
precision and without recursion
"""
a, b = decimal.Decimal(0), decimal.Decimal(1)
for i in decimal_range(0, n):
a, b = b, a + b
return a
在我的机器上,计算1E6需要26.5秒,但我无法保证结果的正确性:
In [26]: %time f2(n)
CPU times: user 26.4 s, sys: 130 ms, total: 26.5 s
Wall time: 26.5 s
Out[26]: Decimal('1.953282128707757731632014830E+208987')
迭代器取自this SO thread而且变化很小,而fib
函数可以找到in this other thread。