我在 Scala 中编写了这个函数来计算给定特定索引 n 的斐波那契数:
def fibonacci(n: Long): Long = {
if(n <= 1) n
else
fibonacci(n - 1) + fibonacci(n - 2)
}
但是,在使用大索引进行计算时效率不高。因此我需要使用元组实现一个函数,这个函数应该返回两个连续的值作为结果。
有人可以给我任何提示吗?我以前从未使用过 Scala。谢谢!
这个问题也许应该去数学。
斐波那契数列有一个明确的公式。如果您需要计算 n 的斐波那契数而不需要前面的数,这要快得多。你可以在这里找到它(比内公式):http ://en.wikipedia.org/wiki/Fibonacci_number
这是一个简单的尾递归解决方案:
def fibonacci(n: Long): Long = {
def fib(i: Long, x: Long, y: Long): Long = {
if (i > 0) fib(i-1, x+y, x)
else x
}
fib(n, 0, 1)
}
您发布的解决方案需要指数级的时间,因为它在每一步创建两个递归调用树 (fibonacci(n - 1)
和)。fibonacci(n - 2)
通过简单地跟踪最后两个数字,您可以递归地计算答案而无需任何重复计算。
你能解释一下中间部分,为什么
(i-1, x+y, x)
等等。对不起,如果我问得太多,但我讨厌在不知道它是如何工作的情况下复制和粘贴代码。
这很简单——但我对变量名的错误选择可能会让人感到困惑。
i
只是一个计数器,说明我们还剩下多少步。如果我们正在计算第M 个 (我正在使用M
,因为我已经n
在我的代码中使用过)斐波那契数,然后告诉我们在达到第M 个i
项之前还有多少项需要计算。x
是斐波那契数列中的第 m项,或F m(其中m = M - i
)。y
是m-1th
斐波那契数列中的项,或F m-1。所以,在第一次调用fib(n, 0, 1)
时,我们有i=M
, x=0
, y=1
. 如果您查看双向斐波那契数列,您会看到 F 0 = 0 和 F -1 = 1,这就是为什么x=0
和y=1
这里。
在下一次递归调用中,我们作为下一个值fib(i-1, x+y, x)
传递。这直接来自定义:x+y
x
F n = F n-1 + F n-2
我们x
作为下y
一项通过,因为我们当前的 F n-1与下一项的 F n-2相同。
每一步我们都会递减i
,因为我们离最终答案更近了一步。
更新
这是另一个解决方案,再次使用Streams
如下(免费获取Memoization),但更直观(又名:在 fibs Stream 上不使用 zip/tail 调用):
val fibs = Stream.iterate( (0,1) ) { case (a,b)=>(b,a+b) }.map(_._1)
产生与以下相同的输出:
fibs take 5 foreach println
Scala 通过Streams支持记忆化,Streams是惰性列表的一种实现。这非常适合 Fibonacci 实现,它实际上在Scala Api for Streams中作为示例提供。在这里引用:
import scala.math.BigInt
object Main extends App {
val fibs: Stream[BigInt] = BigInt(0) #:: BigInt(1) #:: fibs.zip(fibs.tail).map { n => n._1 + n._2 }
fibs take 5 foreach println
}
// prints
//
// 0
// 1
// 1
// 2
// 3
我假设您没有从以前的计算中保存值。如果是这样,使用黄金比例而不是递归定义的直接公式会更快。该公式可以在Wikipedia 页面中找到斐波那契数:
floor(pow(phi, n)/root_of_5 + 0.5)
哪里phi = (1 + sqrt(5)/2)
。
我对 Scala 编程一无所知。我希望 SO 上的某个人将我的伪代码升级为实际的 Scala 代码。