복붙노트

[SCALA] 스칼라에서 피보나치 함수를 작성하는 가장 빠른 방법은 무엇입니까?

SCALA

스칼라에서 피보나치 함수를 작성하는 가장 빠른 방법은 무엇입니까?

나는 더 복잡한 것들에, 아주 간단한 일부터 스칼라에서 피보나치 기능의 몇 가지 구현을 통해 봤는데.

나는 완전히 확실 하나는 빠른 인 아니에요. 그러나 나는 내가 스칼라 네이티브 메모이 제이션을하지 않는 이유를 궁금해하는 사람이 사용의 메모이 제이션이 빠르게 인상쪽으로 기울고 있어요.

사람은 피보나치 함수를 작성하는 가장 빠른 (그리고 깨끗한) 방식으로 가르치 려 수 있습니까?

해결법

  1. ==============================

    1.가장 간단한 정의하는 나를 위해 재귀 내부 꼬리 기능 :

    가장 간단한 정의하는 나를 위해 재귀 내부 꼬리 기능 :

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

    이것은 모든 튜플은 우편 개체 및 구문 이해하기 쉽게 구축 할 필요가 없습니다.

  2. ==============================

    2.가장 빠른 버전은 어떤 식 으로든 일반적인 추가 방식에서 벗어나는 것들입니다. 매우 빠른 이러한 수식을 기반으로 빠른 이진 지수에 어떻게 든 비슷한 계산은 다음과 같습니다

    가장 빠른 버전은 어떤 식 으로든 일반적인 추가 방식에서 벗어나는 것들입니다. 매우 빠른 이러한 수식을 기반으로 빠른 이진 지수에 어떻게 든 비슷한 계산은 다음과 같습니다

    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
    }
    

    이 매우 (예를 들어, 내부 루프는 꼬리 재귀가 아닌)에 최적화되지 않은 경우에도, 그것은 일반적인 첨가제 구현을 상회한다.

  3. ==============================

    3.스칼라는 스트림의 형태로 메모이 제이션을 가지고있다.

    스칼라는 스트림의 형태로 메모이 제이션을 가지고있다.

    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 ...
    

    당신은 당신이 FIB (42) 유형의 통화를 많이하고있는 경우 IndexedSeq로 변환하는 것 같아서, 그래서 스트림은 LinearSeq이다.

    그러나 나는 당신의 사용 사례는 fibbonaci 기능을 위해 무엇인지 질문한다. 큰 조건은 아무것도에 대한 많은 사용되지 않도록이 100 개 미만의 측면에서 롱 오버 플로우됩니다. 작은 용어는 그냥 테이블에 스틱과 속도가 중요 경우를 찾아 볼 수 있습니다. 계산의 세부 그래서 아마 많은 그들은 모두 빠른있어 작은 용어 때문에 문제가되지 않습니다.

    당신이 정말로 매우 큰 용어에 대한 결과를 알고 싶은 경우에 당신이 전화 충분한 수의을하는 경우, 다음, 당신은 단지 일회성 값 (사용 Landei의 솔루션을) 여부에 따라 또는, 당신은-계산을 미리 할 수 ​​있습니다 전체 많이. 문제는 여기 즉, 예를 들어, 100,000 요소는 이상 20,000 자리이다. 우리의 BIGINT 값의 기가 바이트 얘기하고 그래서 당신이 메모리에 유지하려고하면 이는 당신의 JVM을 충돌합니다. 당신은 정확성을 희생하고 일을 더 관리 할 수 ​​있습니다. 당신은 적절한 메모리 / 속도 트레이드 오프를 만드는 부분 메모이 제이션 전략을 (예를 들어, 모든 100 용어 memoize)있을 수 있습니다. 가장 빠른 무엇인지에 대한 명확한 anwser가 없습니다 : 그것은 당신의 사용과 자원에 따라 달라집니다.

  4. ==============================

    4.이 일할 수 있습니다. 그 숫자를 계산하는 O (1) 공간 O (n)의 시간이 소요되지만 캐싱 없다.

    이 일할 수 있습니다. 그 숫자를 계산하는 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) + " "))
       }
    }
    
  5. ==============================

    5.N의 큰 값 피보나치을 계산할 수 있습니다 약간의 간단한 꼬리 재귀 솔루션입니다. 지능 버전 빠르게하지만, N> (46)의 정수 오버 플로우가 발생하면, 한정

    N의 큰 값 피보나치을 계산할 수 있습니다 약간의 간단한 꼬리 재귀 솔루션입니다. 지능 버전 빠르게하지만, 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)
        }
    
  6. ==============================

    6.(허용 대답 포함) 스트림을 사용하여 대답은 매우 짧고 관용적,하지만 그들은하지 가장 빠른 있습니다. 스트림은 (반복적 인 솔루션을 필요가 없습니다) 그 값을 memoize, 당신은 스트림에 대한 참조를 유지하지 않는 경우에도, 메모리를 많이 할당 할 수 있으며, 즉시 쓰레기 ​​수집 된. 좋은 대안은 Iterator를 사용하는 것입니다 : 그것은, 메모리 할당 발생하지 않습니다, 스타일 기능 짧고 읽을 수 있습니다.

    (허용 대답 포함) 스트림을 사용하여 대답은 매우 짧고 관용적,하지만 그들은하지 가장 빠른 있습니다. 스트림은 (반복적 인 솔루션을 필요가 없습니다) 그 값을 memoize, 당신은 스트림에 대한 참조를 유지하지 않는 경우에도, 메모리를 많이 할당 할 수 있으며, 즉시 쓰레기 ​​수집 된. 좋은 대안은 Iterator를 사용하는 것입니다 : 그것은, 메모리 할당 발생하지 않습니다, 스타일 기능 짧고 읽을 수 있습니다.

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

    7.이것은 이미 답을했다, 그러나 희망 당신은 내 경험이 도움이됩니다. 나는 스칼라 무한 스트림 주위에 내 마음을 점점 많은 문제가 있었다. 당신이 파라미터 유형의 솔루션을 generify 위하여려고하는 경우에 다음, 지능의 첫 번째와 같은 단순한 유형의 솔루션을 만들고, (1) 첫 번째 기본 목록과 솔루션을 구현 : 다음, 나는 그가 아주 좋은 제안을했다 폴 AGRON의 프리젠 테이션을 보았다.

    이것은 이미 답을했다, 그러나 희망 당신은 내 경험이 도움이됩니다. 나는 스칼라 무한 스트림 주위에 내 마음을 점점 많은 문제가 있었다. 당신이 파라미터 유형의 솔루션을 generify 위하여려고하는 경우에 다음, 지능의 첫 번째와 같은 단순한 유형의 솔루션을 만들고, (1) 첫 번째 기본 목록과 솔루션을 구현 : 다음, 나는 그가 아주 좋은 제안을했다 폴 AGRON의 프리젠 테이션을 보았다.

    이 방법을 사용하여 나는 (쉬운 솔루션을 이해하고, 저와에 대한) 진정한 간단한 함께했다 :

      def fib(h: Int, n: Int) : Stream[Int] = { h  #:: fib(n, h + n) }
      var x = fib(0,1)
      println (s"results: ${(x take 10).toList}")
    

    간단한 목록을 기반으로 바울의 조언은 "대한-더미의"버전에 따라, 내가 처음 만든 위의 솔루션을 얻으려면 :

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

    공지 사항 나는 짧은 사람 걱정을 내가하지 않았다 경우 영원히 실행 때문에, 목록 버전을 단락 ..하지만 .. 있다고? ^)는 단지 코드의 탐색 비트이기 때문이다.

  8. ==============================

    8.아래의 코드는 빠르고 높은 입력 지수로 계산할 수 모두 있습니다. 2 초 미만에서 일 피보나치 번호 : 내 컴퓨터는 10 ^ 6을 반환합니다. 알고리즘은 기능적인 스타일에 있지만 목록 또는 스트림을 사용하지 않습니다. 그보다는 항등 \ 피 ^ N = F_ {N-1} + F_n * \ 피 대한 \ 피 황금비에 기초한다. (이것은 "비네의 공식"의 버전입니다.)이 평등을 사용하는 문제는 \ 피가 불합리하다는 것이다 (오의 제곱근을 포함)은 순진 플로트-번호를 사용하여 해석하는 경우를 arithmetics 정밀 유한 한으로 인해 분기 있도록. 그러나, 이후 \ 파이 ^ 2 = 1 + \는 a와 b 정수의 형태 A의 번호 + B \ 피와 정확한 계산을 쉽게 구현할 수 있으며,이 아래의 알고리즘은 무엇이며 피. ( "전원"기능은에 최적화의 비트를 가지고 있지만 정말 같은 번호의 "멀티 포트"-multiplication 단지 반복입니다.)

    아래의 코드는 빠르고 높은 입력 지수로 계산할 수 모두 있습니다. 2 초 미만에서 일 피보나치 번호 : 내 컴퓨터는 10 ^ 6을 반환합니다. 알고리즘은 기능적인 스타일에 있지만 목록 또는 스트림을 사용하지 않습니다. 그보다는 항등 \ 피 ^ N = F_ {N-1} + F_n * \ 피 대한 \ 피 황금비에 기초한다. (이것은 "비네의 공식"의 버전입니다.)이 평등을 사용하는 문제는 \ 피가 불합리하다는 것이다 (오의 제곱근을 포함)은 순진 플로트-번호를 사용하여 해석하는 경우를 arithmetics 정밀 유한 한으로 인해 분기 있도록. 그러나, 이후 \ 파이 ^ 2 = 1 + \는 a와 b 정수의 형태 A의 번호 + B \ 피와 정확한 계산을 쉽게 구현할 수 있으며,이 아래의 알고리즘은 무엇이며 피. ( "전원"기능은에 최적화의 비트를 가지고 있지만 정말 같은 번호의 "멀티 포트"-multiplication 단지 반복입니다.)

        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
        }
    

    편집 : 더 효율적이고도 더 관용적은 숫자 계산과 추상 대수학에 대한 Typelevel의 첨탑 라이브러리를 기반으로하는 의미에서 구현입니다. 하나는 (우리는 전체 링 구조를 필요하지 않습니다하지만 난의 "도덕적으로 올바른"을 포함하는 생각) 수학 인수에 훨씬 더 가까이하는 방법으로 위의 코드를 의역 할 수 있습니다. 다음 코드를 실행 해보십시오 :

    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) )
      }
    
    }
    
  9. from https://stackoverflow.com/questions/7388416/what-is-the-fastest-way-to-write-fibonacci-function-in-scala by cc-by-sa and MIT license