【问题标题】:nth Fibonacci number in dynamic programming动态规划中的第 n 个斐波那契数
【发布时间】:2013-11-30 12:58:25
【问题描述】:

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

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

我还成功地在scala 中使用tail recursion 对其进行了编码

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 提供错误的输出,我想要 24278230100

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

【问题讨论】:

  • 看看这里:stackoverflow.com/questions/11285558 以获取类似(但不相同)问题的答案
  • 您想用 Java 获得答案吗?如果您使用迭代,它会快得多。您应该能够在第一次不到 5 毫秒的时间内计算 fib(100),之后更快。
  • 是的@PeterLawrey 我想要java解决方案

标签: java algorithm scala


【解决方案1】:

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

让我们有一个向量

然后我们将它与矩阵相乘

我们会收到:

因此,当我们将向量与该矩阵相乘时,我们会得到下一个斐波那契数。但是如果我们将向量乘以 T2 会发生什么?

这样,我们在下一个(第(n+3)个)之后构造了斐波那契数。现在,如果我们从这个向量中的前两个斐波那契数开始并乘以 Tn-1,我们会得到什么?

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

这是我的实现链接(Java 中):http://pastie.org/8519742

这个算法应该适用于 n 的所有正值,直到 2108

该方法的示例运行时间(使用与 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

可以看出,如果斐波那契数列的成员取 mod n,则所得数列必须是周期最多为 n2-1 的周期。

换句话说,如果您找到这个周期(例如,tortoise and hare algorithm - 线性复杂度),您还可以找到 every 斐波那契数模 108+7。

【讨论】:

  • +1 当你说快速时,你能包括时间吗?我会对 n=10^x 感兴趣,就像我在回答中所做的那样。
  • @PeterLawrey 我对算法进行了一些修改(为了让 n 尽可能长地传递)并使用您的时间测量代码运行它。你可以在我的回答中看到结果。当我有更多时间时,我将修改方法以使用 n 作为 BigInteger,并给出另一个详细的表格,其中包含更大的 n 值的运行时间。
  • 很好,你可以看到它的扩展性更好,比 fib(100,000) 更快
【解决方案2】:

这是我所知道的计算 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 微秒。

【讨论】:

  • 仍然需要 O(N) 时间。更快的方法是使用矩阵的快速幂算法,计算 fib(N) 需要 O(lgN)。参考这个链接sites.google.com/a/rdaemon.com/rdaemon/home/…
  • @notbad 我怀疑这意味着比long 更大的值,这意味着使用 BigInteger 所以成本可能是 O((lg n)^2) 作为运算随着计算值的大小而增加。
  • @yasen 那么发布更快的解决方案应该很容易。我对您更快的解决方案感兴趣(我并不是说它不能完成,但对我来说并不明显)
  • @PeterLawrey 是的,我在评论您的帖子后立即着手解决问题。但是,停电了,我(几乎)完全失去了答案。我会尝试再做一次,但至少对于它的初始版本,我希望它比丢失的版本提供的信息要少得多......
【解决方案3】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-16
    • 1970-01-01
    • 2022-12-10
    • 2016-08-07
    相关资源
    最近更新 更多