Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
用Scala编写斐波那契函数最快的方法是什么?_Scala_Recursion_Fibonacci - Fatal编程技术网

用Scala编写斐波那契函数最快的方法是什么?

用Scala编写斐波那契函数最快的方法是什么?,scala,recursion,fibonacci,Scala,Recursion,Fibonacci,我已经看过了从a到的Scala中Fibonacci函数的一些实现 我不完全确定哪一个是最快的。我倾向于这样一种印象,即使用回忆录的速度更快,但我想知道为什么Scala没有本地回忆录 有谁能告诉我写斐波那契函数的最佳、最快(最干净)的方法吗?最快的版本是在某种程度上偏离了通常加法方案的版本。计算速度非常快,某种程度上类似于基于以下公式的快速二进制指数运算: F(2n-1) = F(n)² + F(n-1)² F(2n) = (2F(n-1) + F(n))*F(n) 下面是一些使用它的代码: d

我已经看过了从a到的Scala中Fibonacci函数的一些实现

我不完全确定哪一个是最快的。我倾向于这样一种印象,即使用回忆录的速度更快,但我想知道为什么Scala没有本地回忆录


有谁能告诉我写斐波那契函数的最佳、最快(最干净)的方法吗?

最快的版本是在某种程度上偏离了通常加法方案的版本。计算速度非常快,某种程度上类似于基于以下公式的快速二进制指数运算:

F(2n-1) = F(n)² + F(n-1)²
F(2n) = (2F(n-1) + F(n))*F(n)
下面是一些使用它的代码:

def fib(n:Int):BigInt = {
   def fibs(n:Int):(BigInt,BigInt) = if (n == 1) (1,0) else {
     val (a,b) = fibs(n/2)
     val p = (2*b+a)*a
     val q = a*a + b*b
     if(n % 2 == 0) (p,q) else (p+q,p)
   }
   fibs(n)._1
}

尽管这不是非常优化(例如,内部循环不是尾部递归),但它将击败通常的加法实现。

Scala确实以流的形式进行了记忆

val fib: Stream[BigInt] = 0 #:: 1 #:: fib.zip(fib.tail).map(p => p._1 + p._2)

scala> fib take 100 mkString " "
res22: String = 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 ...
Stream
是一个
LinearSeq
,因此如果要执行大量的
fib(42)
类型调用,可能需要将其转换为
IndexedSeq

然而,我会质疑您的fibbonaci函数的用例是什么。它将在不到100个术语中溢出,因此较大的术语对任何东西都没有多大用处。如果速度是最重要的,你可以把较小的术语放在一张表中查找。因此,计算的细节可能并不重要,因为对于较小的项,它们都很快


如果你真的想知道非常大的术语的结果,那么这取决于你是否只想要一次性的值(使用Landei的解决方案),或者,如果你打了足够多的电话,你可能想要预先计算整个批次。这里的问题是,例如,第100000个元素的长度超过20000位。因此,我们讨论的是千兆字节的BigInt值,如果您试图将它们保存在内存中,它们将使JVM崩溃。你可以牺牲准确性,让事情变得更容易管理。你可以有一个部分记忆策略(比如说,每100学期记忆一次),它可以做出适当的记忆/速度权衡。什么是最快的没有明确的答案:它取决于您的使用情况和资源。

对我来说,最简单的定义是递归内尾函数:

def fib: Stream[Long] = {
  def tail(h: Long, n: Long): Stream[Long] = h #:: tail(n, h + n)
  tail(0, 1)
}

这不需要为zip构建任何元组对象,并且在语法上很容易理解。

这可能会起作用。计算一个数字需要O(1)个空间O(n)个时间,但没有缓存

object Fibonacci {
    def fibonacci(i : Int) : Int = {      
        def h(last : Int, cur: Int, num : Int) : Int = {
            if ( num == 0) cur
            else h(cur, last + cur, num - 1)
        }

        if (i < 0) - 1
        else if (i == 0 || i == 1) 1
        else h(1,2,i - 2)
   }

   def main(args: Array[String]){
      (0 to 10).foreach( (x : Int) => print(fibonacci(x) + " "))
   }
}
对象斐波那契{
def fibonacci(i:Int):Int={
def h(last:Int,cur:Int,num:Int):Int={
如果(num==0)cur
else h(当前,最后+当前,数值-1)
}
如果(i<0)-1
如果(i==0 | | i==1)1
else h(1,2,i-2)
}
def main(参数:数组[字符串]){
(0到10).foreach((x:Int)=>print(fibonacci(x)+“”)
}
}

一个更简单的尾部递归解决方案,可以计算n的大值的斐波那契。Int版本速度更快,但在出现
n>46
整数溢出时受到限制

def tailRecursiveBig(n :Int) : BigInt = {

      @tailrec
       def aux(n : Int, next :BigInt, acc :BigInt) :BigInt ={

         if(n == 0) acc
          else aux(n-1, acc + next,next)
       }

      aux(n,1,0)
    }

这已经得到了回答,但希望我的经验对你有所帮助。我在scala infinite streams上花了很多心思。然后,我看到他给出了非常好的建议:(1)首先用基本列表实现您的解决方案,然后如果您要用参数化类型来泛化您的解决方案,请使用简单类型(如Int's first)创建一个解决方案

使用这种方法,我想出了一个真正简单的解决方案(对我来说,是易于理解的解决方案):

为了实现上述解决方案,根据Paul的建议,我首先创建了基于简单列表的“for dummy”版本:

  def fib(h: Int, n: Int) : List[Int] = {
    if (h > 100) {
      Nil
    } else {
      h :: fib(n, h + n)
    }
  }

请注意,我短路了列表版本,因为如果我不短路,它将永远运行。。但是谁在乎?;^)因为它只是一段探索性的代码

使用
Stream
(包括已接受的答案)的答案非常简短且惯用,但不是最快的。流会记忆它们的值(这在迭代解决方案中不是必需的),即使您不保留对流的引用,也会分配大量内存,然后立即进行垃圾回收。一个很好的替代方法是使用
迭代器
:它不会导致内存分配,风格实用,简短易读

def fib(n: Int) = Iterator.iterate(BigInt(0), BigInt(1)) { case (a, b) => (b, a+b) }.
                           map(_._1).drop(n).next

下面的代码速度很快,能够使用高输入索引进行计算。在我的电脑上,它在不到两秒钟内返回第10^6个斐波那契数。该算法采用函数式,但不使用列表或流。相反,它是基于黄金比率的等式φn=F_{n-1}+F_n*\φ。(这是“比奈公式”的一个版本。)使用此等式的问题是\phi是无理的(涉及五的平方根),因此如果使用浮点数天真地解释,它将因有限精度算术而发散。然而,由于\phi^2=1+\phi,对于a和b整数,使用形式为a+b\phi的数字很容易实现精确计算,下面的算法就是这样做的。(幂函数有一点优化,但实际上只是“mult”的迭代——对这些数字的乘法。)


我记得我在一次线性代数练习中推导了这个公式。这是本课程中最有趣的练习。如果你想走这条路,为什么不使用封闭公式?因为比奈公式的范围受到根的小数精度的限制,因为还不清楚计算实数的n次方是否比上面计算整数的公式快。我赞成这个解决方案,据我所知,在尽可能简化矩阵时,它确实是总运算方面最快的解决方案。谢谢你的回答。您的代码实际上非常类似于Stream.Scala()行中Scala对fib的实现:47到51。我同意这取决于我对斐波那契数的使用,我完全同意兰迪的解
def fib(n: Int) = Iterator.iterate(BigInt(0), BigInt(1)) { case (a, b) => (b, a+b) }.
                           map(_._1).drop(n).next
    type Zphi = (BigInt, BigInt)

    val phi = (0, 1): Zphi

    val mult: (Zphi, Zphi) => Zphi = {
            (z, w) => (z._1*w._1 + z._2*w._2, z._1*w._2 + z._2*w._1 + z._2*w._2)
    }

    val power: (Zphi, Int) => Zphi = {
            case (base, ex) if (ex >= 0) => _power((1, 0), base, ex)
            case _                       => sys.error("no negative power plz")
    }

    val _power: (Zphi, Zphi, Int) => Zphi = {
            case (t, b, e) if (e == 0)       => t
            case (t, b, e) if ((e & 1) == 1) => _power(mult(t, b), mult(b, b), e >> 1)
            case (t, b, e)                   => _power(t, mult(b, b), e >> 1)
    }

    val fib: Int => BigInt = {
            case n if (n < 0) => 0
            case n            => power(phi, n)._2
    }
import spire.implicits._
import spire.algebra._

case class S(fst: BigInt, snd: BigInt) {
  override def toString = s"$fst + $snd"++"φ"
}

object S {
  implicit object SRing extends Ring[S] {
    def zero = S(0, 0): S
    def one = S(1, 0): S
    def plus(z: S, w: S) = S(z.fst + w.fst, z.snd + w.snd): S
    def negate(z: S) = S(-z.fst, -z.snd): S
    def times(z: S, w: S) = S(z.fst * w.fst + z.snd * w.snd
                            , z.fst * w.snd + z.snd * w.fst + z.snd * w.snd)
  }
}

object Fibo {

  val phi = S(0, 1) 
  val fib: Int => BigInt = n => (phi pow n).snd

  def main(arg: Array[String]) {
    println( fib(1000000) )
  }

}