【问题标题】:Fibonacci Recursive function takes forever斐波那契递归函数需要永远
【发布时间】:2015-08-05 04:15:40
【问题描述】:

我想得到斐波那契数列的第 48 个元素 我可以将其存储在 64 位整数中。我正在使用递归子例程,但要花很长时间才能完成。如果有人能找到我的递归子程序的问题,我将不胜感激。

Integer (Int8) :: n
Integer (Int64) :: fib64
n = Int (48, Int8)
Call fibonacci_genr (fib64, n) 

这是我的递归子程序

Recursive                  &
Subroutine fibonacci_genr  &
(                        &
  fb, n                  &
)

Integer (Int64), Intent (Out) :: fb
Integer (Int8), Intent (In) :: n

    Integer (Int64) :: fb1, fb2
    If (n < 2) Then 
      fb = Int (n, Int64) 
    Else 
      Call fibonacci_genr (fb1, n-1)
      Call fibonacci_genr (fb2, n-2)
      fb = fb1 + fb2
    End If
End Subroutine fibonacci_genr

【问题讨论】:

  • 您是否计算出递归计算第 48 个斐波那契数需要多少次调用? (提示:很多)
  • 只需将计数器放入您的子例程中并计算调用次数并打印出来 - 您会受到启发。
  • 您可以记住您的答案,无需查找两次。
  • 调用次数惊人

标签: recursion fortran fibonacci


【解决方案1】:

像这样递归计算斐波那契会导致重复计算 Recursion vs. Iteration (Fibonacci sequence) 。为避免这种情况,请使用迭代算法。

【讨论】:

  • 这是我遇到的
【解决方案2】:

Appologies 我不知道 fortran 我会尽力向您展示如何在 javascript 中加速它以及我在 fortran 解决方案中的最佳表现

var memo = [];
function fib(n) {
    if (memo[n-1]) { //check to see if you already calculated the answer
        return memo[n-1];
    }
    memo[n-1] = n <= 1 ? 1 : fib(n - 1) + fib(n - 2);
    return memo[n-1];
}

这是记忆的fortran

Integer (Int64) :: memo(48) = 0

Integer (Int64), Intent (Out) :: fb
Integer (Int8), Intent (In) :: n

Integer (Int64) :: fb1, fb2
If (memo(n) > 1) Then    ! if its in the array we just use that value
    fb = memo(n)
Else If (n <= 2) Then 
    memo(n) = Int (1, Int64) 
    fb = memo(n)
Else 
    Call fibonacci_genr (fb1, n-1)
    Call fibonacci_genr (fb2, n-2)
    memo(n) = fb1 + fb2
    fb = memo(n)
End If
End Subroutine fibonacci_genr

【讨论】:

  • 是否存在不需要存储所有斐波那契数的解决方案?
  • @Zeus 所以这是用计算时间换取空间。如果您不想每次都计算旧值,我很确定您需要一种“记住”旧值的方法。如果空间是一个问题,您可以将其调整为仅记住每 10 个 fib 数字左右。然后它会平均计算最后 5 个 fib 数字。
  • @Zeus 如果您可以存储数字,这会快得多。
  • 我发现我可以使用黄金分割率
  • @Zeus OH YEAH 你可以用一个有趣的方程计算恒定空间和时间中的 fib 数
【解决方案3】:

这是用 Python 编写的(同样没有 FORTRAN)。

def f(a):
  if (a < 2):
    return a;
  return _f(a-2, 2, 1)

def _f(a, n1 , n2) :
  if(a==0) :
    return n1+n2
  return _f(a-1, n1+n2, n1)

每个数字只计算一次而不是多次。 _f 是一个私有函数 f 是你调用的函数,

注意:这仍然是递归的,但只会调用自身 48 次(顺序 N)

【讨论】:

  • 我错过了什么吗?我的回答有什么问题?我没有足够的代表来评论其他帖子。但是你不需要把所有的值都缓存在一个数组中,
【解决方案4】:

鉴于 Int8=1Int64=8 以及显式接口,gfortran4.7.2 抱怨

call fibonacci_genr( fb1, n-1 )
                          1
Error: Type mismatch in argument 'n' at (1); passed INTEGER(4) to INTEGER(1)

如果实际参数被强制转换为Int8

Call fibonacci_genr (fb1, int( n-1, Int8 ) )

Int8 直接使用文字(感谢@francescalus)

Call fibonacci_genr (fb1, n - 1_Int8 )

代码似乎运行良好。但我认为使用integer :: n 而不是integer(Int8) :: n 要简单得多,因为n 没有溢出......

顺便说一句,我还测量了从n = 048 调用此例程的时间。 Xeon2.6GHz(x86_64) + gfortran4.7.2 -O2 为 91 秒。如果子程序被函数替换,时间减少到 72 秒。为了比较,我还在 Julia 中尝试了以下代码

function fibo( n::Int )  # Int defaults to Int64
    if n <= 1
        return n
    else
        return fibo( n-1 ) + fibo( n-2 )
    end
end

for inp = 0:48
    println( fibo( inp ) )
end

花了 118 秒,对于这个递归来说非常好。另一方面,直接迭代(没有递归调用)当然是超快的,只需要

【讨论】:

  • 使用 Fortran-5 不会出现问题。
  • @Zeus Fortran-5 是什么?
  • gfortran-5 是 gfortran 的最新版本
  • 啊,好的。不幸的是,我仍在使用 gfortran-4(最高 4.8.2)。顺便说一句,我搜索了 Fortran-5 并立即找到了这个 en.wikipedia.org/wiki/Fortran_5 嗯..
  • 在 Julia 版本中,我认为您甚至可以省略 ::Int 声明,因为它是一个类型稳定的函数;它在你包含它的同时运行。
【解决方案5】:

此解决方案为您提供线性时间的斐波那契数字(调用次数 == 斐波那契数字 -2,数字 1 和 2 只有 1 次调用)。这是通过使用返回序列的两位数字的递归函数来实现的,这样每次调用都可以计算下一个数字并重新使用前一个数字作为其返回值。如果您想调用它以仅接收新数字,这确实需要一个包装函数,但这是减少递归的一个小牺牲。

以下是函数:

  integer(kind=int64) pure function fibonacci(n)
    use iso_fortran_env
    implicit none
    integer, intent(in) :: n
    integer(kind=int64), dimension(2) :: fibo

    fibo = fib(int(n,int64))
    fibonacci = fibo(1)
  end function fibonacci

  recursive pure function fib(n) result(ret)
    use iso_fortran_env
    implicit none
    integer(kind=int64), intent(in) :: n
    integer(kind=int64), dimension(2) :: tmp,ret

    if (n == 1_int64) then
       ret = [1_int64, 0_int64]
    else if (n == 2_int64) then
       ret = [1_int64, 1_int64]
    else
       tmp = fib(n-1)
       ret = [sum(tmp), tmp(1)]
    end if
  end function fib

使用这些函数计算fibonacci(48) 的时间可以忽略不计。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-01
    • 2010-12-03
    • 2020-03-21
    • 2021-11-17
    • 2011-07-27
    • 2014-11-09
    相关资源
    最近更新 更多