在Python

时间:2016-11-11 21:48:37

标签: algorithm python-2.7 fibonacci

此问题的目标是计算 F [n] mod m 。这里的输入是 n m ,其中 n 代表斐波那契数的索引,比如F [0] = 0,F [ 1] = 1,F [2] = 1,F [3] = 2且 m 代表 F [n] 的分割数。约束是:

  • n&gt; = 1且n <= 10 ^ 18
  • m> = 2且m <= 10 ^ 5

到目前为止,我已经解决了这个问题并能够生成此问题的确切输出,除非我将100000作为 m 的值,它超出了时间限制。时间限制是5秒。如果在2到99999之间给出 m 的值,我的程序会在时间限制内生成正确的输出。任何有助于解决这个问题的人都会受到高度赞赏。

代码:

def fibonacci(n):
    if ( n == 0 ):
        return (0, 1)
    else:
        a, b = fibonacci(n/2)
        c = a * (2* b - a)
        d = a**2 + b**2
        if ( n % 2 ):
            return (d, c + d)
        else:
            return (c, d)


def findRemainders(m):
    remainderList = ["0", "1"]
    i = 2
    while(1):
        firstFib, secondFib = fibonacci(i)
        if ( (firstFib % m) == 0 and (secondFib % m) == 1 ):
            break
        remainderList.append( (firstFib % m) )
        remainderList.append( (secondFib % m) )
        i += 2

    return remainderList


def hugeFibonacciNumberModulo_m( n, m ):
    remainderList = findRemainders(m)
    length_of_period = len(remainderList)
    remainder = (n % length_of_period)
    out = remainderList[remainder]
    return out


inp = map(int, raw_input().split())
n, m = inp[0], inp[1]

if ( (n >= 1 and n <= 10**18) and (m >= 2 and m <= 10**5) ):
    out = hugeFibonacciNumberModulo_m( n, m )
print out

2 个答案:

答案 0 :(得分:2)

使用模幂运算可以非常快速地完成此操作。

考虑以下矩阵乘法:

| 0  1 |     | a |     |  b  |
|      |  x  |   |  =  |     |
| 1  1 |     | b |     | a+b |

如果ab是最后两个术语,您应该立即看到此乘法的结果是斐波纳契数列的下一次迭代。要获得执行此乘法n次的结果,您需要计算2x2矩阵n-th(mod m)的(0,1;1,1)幂。这可以通过将此矩阵提高到2的连续幂来非常快速地完成。

例如,要计算此矩阵的10次幂:

                   | 0  1 |     | 0  1 |     | 1  1 |
A x A  =  A**2  =  |      |  x  |      |  =  |      |
                   | 1  1 |     | 1  1 |     | 1  2 |

                      | 1  1 |     | 1  1 |     | 2  3 |
A**4 =  (A**2)**2  =  |      |  x  |      |  =  |      |
                      | 1  2 |     | 1  2 |     | 3  5 |

                      | 2  3 |     | 2  3 |     | 13  21 |
A**8 =  (A**4)**2  =  |      |  x  |      |  =  |        |
                      | 3  5 |     | 3  5 |     | 21  34 |

将矩阵平方三次后,我们现在具有A**8A**2的值。将这些相乘,得到A**10

          | 13  21 |     | 1  1 |     | 34  55 |
A**10  =  |        |  x  |      |  =  |        |
          | 21  34 |     | 1  2 |     | 55  89 |

这些数字在常规算术中会迅速变得庞大,但如果你以m为模进行所有乘法运算,那么这不是问题。最后,将矢量(0; 1)乘以得到的矩阵以得到答案(或者,等效地,只选择矩阵顶行中的第二个数字)。

所需的乘法次数大约为log(n),所以所需的时间应该非常短,即使m是万亿或更多。

See Wikipedia for more information about modular exponentiation of matrices.

答案 1 :(得分:1)

我不明白你在findRemainders(m)尝试做什么或为什么需要它。您已经在使用Fibonacci-by-doubleling算法,它类似于(并且通常来自)矩阵求幂算法。可以修改指数以通过在每个步骤基本上修改部分结果来有效地处理模幂运算。

def fibmod(n, m):
    assert 1 <= n <= 10**18, n
    assert 2 <= m <= 10**5, m

    def f(n):
        if n == 0:
            return 0, 1
        else:
            a, b = f(n // 2)
            c = a * (2*b - a) % m
            d = (a**2 + b**2) % m

            if n % 2 == 0:
                return c, d
            else:
                return d, (c + d) % m

    return f(n)[0]

您可以进一步细分cd的表达式,并在每次中间乘法,加法和减法后应用% m以防止溢出(但这不是真的Python中的问题)。