【问题标题】:Algorithm for finding last 5 digits of Fibonacci number查找斐波那契数的最后 5 位的算法
【发布时间】:2020-05-24 14:37:50
【问题描述】:

我正在尝试实现一个迭代算法来计算第 N 个斐波那契数的最后 5 位数字。我找到第 n 个斐波那契数本身并只显示最后 5 位数字没有问题,但是,我的作业还要求找到我的程序在 1 分钟内运行的最大值 n。问题是,N 变得非常大,因此斐波那契数也很大。我应该只使用 BigInteger 来存储值并最终使用 % 运算符来显示最后 5 个数字吗?有没有办法利用我只需要最后 5 位数字来加速这个过程?我觉得我错过了任务的重点。

作业是这样说的: 使用 Java,实现计算第 n 个斐波那契数的最后 5 位的迭代算法。进行实验,找出程序在计算机上运行时间不到 1 分钟的最大 n 值。

我查找第 N 个斐波那契数的最后 5 位的代码:

public static int Fibonacci(int n){
    int a, b = 0, c = 1;
    for(int i = 1; i < n; i++){
        a = b;
        b = c;
        c = a + b;
    }
    return c % 100000;
}

我也很想知道是否有更好的迭代解决方案。

【问题讨论】:

  • 你可以使用int,因为中间结果只需要模100,000计算。但是你需要在循环中做余数运算。
  • 谢谢!但是这里的数学是如何工作的呢?我正在考虑这个解决方案,但由于某种原因没有尝试过,因为我真的不相信它会起作用。
  • 和的后 5 位仅取决于每个被加数的后 5 位,因为进位仅从右到左进行(左位没有机会影响自己右侧的数字) .
  • 如果执行时间很重要,那么如果不是绝对必要的话,永远不要使用BigIntegerBigDecimal,因为与原语甚至包装器相比,这些类非常慢。
  • 数学运算:两个数相加时,和的后5位只能受后5位的影响。所以代码只需要保留c的后5位即可。这意味着您不需要模运算符。 ab中你应该有的最大数字是99999。当你计算c时,它会在00000到199998之间。这意味着在计算c之后,你可以用@将其修剪为5位数987654330@

标签: java algorithm iteration fibonacci


【解决方案1】:

正如您已经注意到的,找到最后 5 位数字相当于计算结果以 100000 为模。要解决溢出问题,您可以对中间结果应用 % 100000 运算,因此不需要 BigInteger。应该是:c = (a + b) % 100000

理论上,您可以将解的时间复杂度从 O(N) 提高到 O(log(N))。该算法基于快速矩阵求幂。但是,您的作业需要迭代方法,所以我只是为了完整性而提及它。

【讨论】:

    【解决方案2】:

    Ardavel 的回答很好、清楚地解释了为什么在循环中取余数会给出正确的结果,这样您就不需要使用 BigInteger。所以为了你的任务,这个问题得到了回答;但是这个问题太有趣了,不能把它留在那里。你说:

    我也很想知道是否有更好的迭代解决方案。

    所以,这里是:

    矩阵乘法

    Ardavel 提到,您可以通过使用高效的矩阵幂算法计算 matrix power,在 O(log n) 时间内计算出相同的结果。本质上,第 n 个斐波那契数可以写成封闭形式:

    ( ... ) = ( 1 1 )n ( 1 )
    ( F_n )   ( 1 0 )  ( 0 )
    

    这个矩阵的n 的幂可以在 O(log n) 时间内计算出来,例如使用square and multiply 算法,该算法可以迭代实现——迭代版本比递归版本更高效。

    “O(1)时间”解

    实际上,使用以下观察可以做得更好:让我们将上面的矩阵方程称为A^n v,其中v 是初始向量。模 100,000 的二维整数向量只有有限个,2x2 整数矩阵也只有有限个。所以有一些有限的数t 使得A^t v = v

    这意味着A^n = A^(n % t),因此无论n 有多大,您最多只需要进行固定、恒定数量的矩阵乘法.原来t的值是150,000,所以我们可以通过在开头写n %= 150000;来改进任何算法。

    但是,这个解决方案并不是 O(1) 时间,因为对于任意大的 n,无法在恒定时间内找到余数模 t。假设我们允许输入任意大(其余的计算仍然可以使用ints 完成),那么即使以二进制格式读取输入所花费的时间也是 O(log n)。但这比 O(log n) 矩阵乘法要好得多。

    中国剩余定理

    我们可以更进一步。如果我们应用Chinese remainder theorem,那么找到模 25 的答案和模 55 的答案就足够了,因为它们唯一地确定了模 105 的答案。事实证明,循环长度t 在两种情况下都短得多:对于模数 25t 仅为 48,而对于 55,则为 12,500。

    这足够小,如果性能非常重要,我们可以预先计算长度分别为 48 和 12,500 的数组中较小模的结果。这些数字足够小,可以容纳 2 字节 shorts,因此数组占用大约 25KB 左右的内存,相比之下,如果您要预先计算 @987654341 的数组,则需要大约 600KB 来存储 150,000 个ints @ = 150,000。

    然后算法是“取 n 模 48 和 12,500,在两个数组中查找,并应用中国剩余定理”,这并不是真正的迭代,但至少你可以争辩说数组是使用迭代算法预先计算的。

    【讨论】:

    • 这很好,但你让任务变得更加困难。他们应该“找到你的程序在你的计算机上运行不到 1 分钟的最大 n 值”。现在,如果您可以立即解决几乎所有问题,您将如何做到这一点?!? :-D
    • 如果余数运算的计算效率很高,那么我估计它工作的最大数大约是2^(max heap size in bits - heap space already used),除非你的堆太大以至于不能在一分钟内迭代。
    • 听起来不错,但要找出可以使空闲堆空间有多大也很复杂。我认为明智的做法是在他们的循环中睡两分钟,然后答案就是 n=1。
    • 很好的答案。我只是想知道你是如何到达t = 150000 的。你是用暴力破解的吗?或者这背后有什么数学原理?
    • @JohannesKuhn 应该可以手动推导出 t = 150,000,但我认为暴力破解它会更容易,所以我这样做了(它花了几行 Python 代码,而且远远低于一秒钟运行)。不过,我的答案中缺少一些数学知识;来自有限性的论点仅有效,因为矩阵A 是可逆的。
    猜你喜欢
    • 2019-06-26
    • 2016-09-11
    • 2019-05-29
    • 2020-04-02
    • 2020-01-18
    • 1970-01-01
    • 1970-01-01
    • 2020-08-26
    • 1970-01-01
    相关资源
    最近更新 更多