0

I need to make the following code more efficient. It takes a bit too long to run.

       def aFib(n: Int): BigInt = {
         var nn:   Int = n
         var sign: Int = 0
          if (nn < 0) {
              nn = -nn
              sign = 2 * (nn % 2) - 1
          } else {
              sign = 1
          }
          var a: BigInt = 1
          var b: BigInt = 0
          var p: BigInt = 0
          var q: BigInt = 1
          while (nn != 0) {
            if (nn % 2 == 1) {
                var t  = (b + a) * q + a * p 
                b = b * p + a * q 
                a = t
            }
            var t = p * p + q * q
            q =(2 * p * q) + q * q
            p = t
            nn /= 2
          }
          sign * b
    }

I've already played around with different approaches (iterative, recursive, etc.) and settled on the algorithm embodied in the code. Cognoscenti will recognise it as a well-known way to calculate positive and negative fibonacci numbers. I've written the code myself and put in BigInt. There does not appear to be a fast implementation readily available. Since Scala is a complex language and I have limited experience, my gut-feeling is that there are ways to make the code better - reducing the elapsed time. All suggestions are welcome.

1
  • Have you tried this "fast doubling" formula from the last paragraph? It seems to do the same as your square-and-multiply matrix multiplication, but requires slightly fewer operations. Commented Sep 13, 2018 at 16:20

3 Answers 3

1

First of all, look at this for profiling: https://developer.lightbend.com/blog/2018-04-09-profiling-JVM-applications/; secondly BigInt wraps java.math.BigInteger: https://github.com/scala/scala/blob/v2.12.3/src/library/scala/math/BigInt.scala#L1 which is an arbitrary precision integer: https://docs.oracle.com/javase/7/docs/api/java/math/BigInteger.html

So, probably the best thing you can do while staying in Scala is to switch to native integer types, and only use this specific code when the numbers get too big for longs.

In this code, nn /= 2 might be better represented as a bitshift.

The best thing you can do might be to use a numerical computing library, and as much as possible express these are matrix computations, which can be executed in parallel on a gpu: https://github.com/scalanlp/breeze

Update2: It doesn't look like Breeze actually engages the GPU in any way :(

Update: Probably the biggest single win would be to memoize your results, so you can compute fibonnacci from the predecessor values (if already computed). Store those results, look them up in a table, and as @tim suggests, seed the table with the first few thousand numbers.

Sign up to request clarification or add additional context in comments.

1 Comment

The numbers overflow Long very quickly (n<100) and below that value you might as well use a lookup table, so you need some sort of high-precision integer.
0

The BigInt operations are going to dominate the performance of this code, so the only way to make a substantial improvement would be to reduce the number of these operations. You can avoid computing q*q twice for a slight performance gain, but otherwise you are going to have to find a better algorithm or a better implementation of BigInt.

However you can make your code better by making it more idiomatic Scala that does not use any mutable values. Performance measurement is difficult with the JVM, and it depends on which value of n you are interested in, but this appears to perform about the same as the original.

def aFib(n: Int): BigInt = {
  @tailrec
  def loop(nn: Int, a: BigInt, b: BigInt, p: BigInt, q: BigInt): BigInt =
    if (nn == 0) {
      b
    } else {
      val newP = p * p + q * q
      val newQ = (2 * p * q) + q * q

      if (nn % 2 == 1) {
        val newA = (b + a) * q + a * p
        val newB = b * p + a * q

        loop(nn / 2, newA, newB, newP, newQ)
      } else {
        loop(nn / 2, a, b, newP, newQ)
      }
    }

  val (nn, sign) =
    if (n <  0) {
      (-n, 2 * (n % 2) - 1)
    } else {
      (n, 1)
    }

  sign * loop(nn, 1, 0, 0, 1)
}

Comments

0

use lazy realization

import scala.math.BigInt
lazy val fibs: Stream[BigInt] = BigInt(0) #:: BigInt(1) #:: fibs.zip(fibs.tail).map { n => n._1 + n._2 }

fibs.take(5).last

@Fred Also, you can use something like this for negative and positive values

  def fib(value: Int): BigInt = {
  @tailrec
  def helper(cur: BigInt, prev: BigInt, n: BigInt): BigInt = {
    val zero = BigInt(0)
    if (n == zero) cur
    else if (n > 0) helper(cur + prev, cur, n - 1)
    else helper(prev, cur - prev, n + 1)
  }
  helper(0, 1, value)
}

3 Comments

Is this really faster than the code in the question?
It might be less in performance but at least third also functional way such as haskell, By the way, my code has correct answer of 333 Fibonacci number, but the code in topic wrong. And also you may use cats.Eval for none memorisation
I've tried to do negative fibonacci numbers by adapting your code. Not succeeded yet. Are you able to provide a solution?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.