N的第N个Fibonacci数大到10 ^ 19?

时间:2015-02-16 18:58:25

标签: python python-2.7 dynamic-programming fibonacci

我正在尝试制作一个程序来找到第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

我的代码似乎不适用于大数字。我得到无效的响应错误。 有什么建议吗?

4 个答案:

答案 0 :(得分:9)

当N为10 ^ 19时获得第N个斐波纳契数,如果你以天真的方式做到这一点并不适合(至少我猜它不会起作用)。

更好的方法。而这种技术适用于许多系列&#39;像这样。它被称为Fibonacci Q Matrix

enter image description here

哪里

enter image description here

这样想:

你有一些矩阵可以将矢量A转换为B:

enter image description here

填写这些条目很容易。特殊的部分是现在这是一个矩阵运算符,所以如果我们想要第1000个Fibonacci数,我们只需要进行矩阵乘法。

你可以通过一个循环来完成这个任务,但它需要花费你很长时间才能达到10 ^ 19,并进行10 ^ 19次矩阵乘法(即使它们是&#39;小的)也会采取公平的态度。

相反,我们采取另一种捷径。 x ^ N可以重写为幂的乘积,它们总和为N,即

x**100 == x**90 * x**10

因此,目标是在没有进行大量计算的情况下在指数中获得大数字:

x**2x*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)来获得更好的效果。

最终,n10**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