动态编程中的第n个Fibonacci数

时间:2013-11-30 12:58:26

标签: java algorithm scala

每个人都知道斐波那契系列的逻辑

Fib0 = 0
Fib1 = 1
Fibn = Fibn-1 + Fibn-2 , n > 1

我的问题是我必须根据fib(n)%(100000000+7)计算n输出

喜欢for n=0 output 1

for n=5 output 5

for n=10 output 55

for n=100 output 24278230

我在tail recursion

中使用scala成功编码了它
def fi( n : Int) : Long = { 
  def fib_tail( n: Int, a:Int, b:Int): Int = n match {
    case 0 => a 
    case _ => fib_tail( n-1, b, (a+b))
  }
  return fib_tail( n, 0, 1)
}


   l.map(x=> println(fi(x)%((math.pow(10, 8)).toInt +7  )))

它适用于0,1,5,10,但它为100 {i} 24278230提供了错误输出

任何人都会给我一些想法来获得这个输出

3 个答案:

答案 0 :(得分:4)

我的回答是linear recurrence sequences的一个通用解决方案。你需要一些基本的代数知识才能完全理解它。

我们有一个载体

我们将它与矩阵相乘

我们会收到:

因此,当我们将向量与此矩阵相乘时,我们会收到下一个斐波纳契数。但是如果我们将矢量乘以T 2

会发生什么

通过这种方式,我们在下一个(第(n + 3)个)之后构造了斐波那契数。现在,如果我们从这个向量中的前两个斐波纳契数开始并将其与T n-1 相乘,我们会得到什么?

因此,通过将我们的向量与矩阵T相乘,提高到第(n-1)次幂,我们可以得到第n个斐波纳契数。我们可以通过exponentiation by squaring在时间O(log n)中计算T n-1 。当然,我们应该以模10 8 +7进行所有计算。

以下是我的实施链接(使用Java):http://pastie.org/8519742

对于n <2> 10 8 的所有正值,该算法应该能够很好地快速运行。

该方法的示例运行时间(使用与Peter Lawrey的answer相同的时间测量):

fib(1,000,000,000,000,000,000) is 8,465,404 took us 1022.8 to calculate
fib(100,000,000,000,000,000) is 60,687,801 took us 325.7 to calculate
fib(10,000,000,000,000,000) is 9,115,009 took us 247.2 to calculate
fib(1,000,000,000,000,000) is 8,361,917 took us 233.3 to calculate
fib(100,000,000,000,000) is 11,279,600 took us 218.3 to calculate
fib(10,000,000,000,000) is 72,758,000 took us 6027.7 to calculate
fib(1,000,000,000,000) is 82,461,898 took us 184.2 to calculate
fib(100,000,000,000) is 60,584,292 took us 180.4 to calculate
fib(10,000,000,000) is 68,453,509 took us 162.0 to calculate
fib(1,000,000,000) is 90,703,191 took us 145.4 to calculate
fib(100,000,000) is 21 took us 131.3 to calculate
fib(10,000,000) is 60,722,758 took us 112.0 to calculate
fib(1,000,000) is 72,117,251 took us 99.8 to calculate
fib(100,000) is 33,178,829 took us 92.3 to calculate
fib(10,000) is 49,520,320 took us 70.8 to calculate
fib(1,000) is 95,802,669 took us 60.1 to calculate
fib(100) is 24,278,230 took us 39.3 to calculate
fib(10) is 55 took us 27.0 to calculate
fib(1) is 1 took us 16.3 to calculate

然而,尽管如此,这不是是您问题的最快算法。众所周知,斐波那契数在某些模块下具有周期性残基。在斐波那契数字上引用the wikipedia entry

  

可以看出,如果Fibonacci序列的成员采用mod n,则得到的序列必须是周期性的,周期最多为n 2 -1。

换句话说,如果您找到此期间(例如,tortoise and hare algorithm - 线性复杂度),您还可以找到每个斐波纳契数模10 的结果8 7

答案 1 :(得分:1)

这是我所知道的用于计算fib%mod的最快方法,但你只能看到大约1000+的差异

public static void main(String[] args) {
    long mod = 100_000_007;
    for (int i = 100_000_000; i > 0; i /= 10) {
        long start = System.nanoTime();
        final long l = fibMod3(i, mod);
        long time = System.nanoTime() - start;
        System.out.printf("fib(%,d) %% %,d is %,d took %.1f us to calculate%n", i, mod, l, time / 1e3);
    }
}

// use a simple loop and % each time.
public static long fibMod(int n, long mod) {
    long a = 1, b = 1;
    if (n <= 2) return 1;
    while (n-- > 2) {
        long c = a + b;
        a = b;
        b = c % mod;
    }
    return b;
}

// mod is very expensive so only do this on every third iteration.
public static long fibMod3(int n, long mod) {
    long a = 1, b = 1;
    if (n <= 2) return 1;
    while (n > 5) {
        long c = a + b;
        a = b + c;
        b = (c + a) % mod;
        n -= 3;
    }
    while (n > 2) {
        long c = a + b;
        a = b;
        b = c;
        n--;
    }
    return b % mod;
}

打印

fib(100,000,000) % 100,000,007 is 21 took 546460.1 us to calculate
fib(10,000,000) % 100,000,007 is 60,722,758 took 54079.6 us to calculate
fib(1,000,000) % 100,000,007 is 72,117,251 took 5274.9 us to calculate
fib(100,000) % 100,000,007 is 33,178,829 took 506.0 us to calculate
fib(10,000) % 100,000,007 is 49,520,320 took 50.8 us to calculate
fib(1,000) % 100,000,007 is 95,802,669 took 5.2 us to calculate
fib(100) % 100,000,007 is 24,278,230 took 0.7 us to calculate
fib(10) % 100,000,007 is 55 took 0.3 us to calculate
fib(1) % 100,000,007 is 1 took 0.3 us to calculate

当代码已经预热时,fib(100)需要0.7微秒。如果你循环增加大小,它甚至不编译,大约需要3微秒。

答案 2 :(得分:1)

我用C ++编写代码,效果很好。

LL fi(int n, LL a, LL b) {
   if (n == 0) {
     return a;
  } else {
     return fi(n-1, b, (a+b) % 100000007);
   }
}

你应该在这里修改:fib_tail(n-1,b,(a + b)%MOD),否则结果将超出Long的范围。