简体   繁体   English

斐波那契数词在Java中

[英]Fibonacci for large numbers in Javascript

I have the following code: 我有以下代码:

function fib(n) {

  let first=BigInt(0);
  let snd=BigInt(1);
  let currentNumber;
  let countMax=Math.abs(n)+1;
  let counter=2;
  if(n==0){

    return first;
  } 
  else if (n==1||n==-1){

    return snd;
  }
  while(counter<countMax)
  {
        currentNumber=first+snd;
        first=snd;
        snd=currentNumber;
        counter++;
  }
  if((n<0) && (n % 2 ==0))
  {
    return -currentNumber;
  }
  return currentNumber;
}

That returns the fibonacci number for the given (n). 返回给定(n)的斐波那契数。

My issue is that I have to improve the performance of this code. 我的问题是我必须提高此代码的性能。 I tried to use different fibonacci formulas (exponential ones) but I lose a lot of precision cause phi number has infinite decimals, so I have to truncate and for big numbers I lost a lot of precision. 我尝试使用不同的斐波那契公式(指数公式),但由于phi数字具有无限的小数位数,所以我失去了很多精度,因此我必须截断,并且对于大数我失去了很多精度。

When I execute for instance fib(200000) I get the huge number but the code spends more than 12000 ms. 例如,当我执行fib(200000)时,我得到了巨大的数字,但是代码花费了超过12000 ms。

For other hand I tried using recursion but the performance decreases. 另一方面,我尝试使用递归,但性能下降。

Could you provide me an article or clue to follow? 您能给我一篇文章或线索吗?

Thanks & Regards. 感谢和问候。

If you just add the previous value to the current one and then use the old current value as the previous one you get a significant improvement in performance. 如果仅将前一个值添加到当前值,然后将旧的当前值用作前一个值,则性能将得到显着改善。

 function fib(n) { var current = 1; var previous = 0; while (--n) { var temp = current; current += previous; previous = temp; } return current; } console.log(fib(1)); // 1 console.log(fib(2)); // 1 console.log(fib(3)); // 2 console.log(fib(4)); // 3 console.log(fib(5)); // 5 

You can also use an array in the parent scope to store the previous values to avoid redoing the same calculations. 您还可以在父作用域中使用一个数组来存储以前的值,以避免重做相同的计算。

 var fibMap = [1, 1]; function fib(n) { var current = fibMap[fibMap.length - 1]; var previous = fibMap[fibMap.length - 2]; while (fibMap.length < n) { var temp = current; current += previous; previous = temp; fibMap.push(current); } return fibMap[n - 1]; } console.log(fib(1)); // 1 console.log(fib(2)); // 1 console.log(fib(3)); // 2 console.log(fib(4)); // 3 console.log(fib(5)); // 5 

Benchmark for getting the 1000th number 3 times 3次获得第1000个数字的基准

First of all, you can refer the answer here which says that 首先,您可以参考此处的答案

Fib(-n) = -Fib(n) Fib(-n)= -Fib(n)

Here's the recursive implementation which is not efficient as you mentioned 这是递归实现,您提到的效率不高

function fib(n) {
    // This is to handle positive and negative numbers
    var sign = n >= 0 ? 1 : -1;
    n = Math.abs(n);

    // Now the usual Fibonacci function
    if(n < 2)
        return sign*n;
    return sign*(fib(n-1) + fib(n-2));
}

This is pretty straightforward and I leave it without explaining because if you know Fibonacci series, you know what the above code does. 这非常简单,我无需赘述,因为如果您了解斐波那契数列,您就会知道上述代码的作用。 As you already know, this is not good for very large numbers as it recursively calculate the same thing again and again. 如您所知,这对于很大的数不是很好,因为它递归地一次又一次地计算相同的事物。 But we'll use it in our approach later on. 但是稍后我们将在我们的方法中使用它。

Now coming towards a better approach. 现在正在寻求更好的方法。 See the below code similar to your code just a bit concise. 看到下面与您的代码相似的代码有点简洁。

function fib(n) {
    if(n == 0)
        return 0;
    var a = 1;
    var b = 1;
    while(n > 2) {
        b = a + b;
        a = b - a;
    }
    // If n is negative then return negative of fib(n)
    return n < 0 ? -1*b : b;
}

This code is better to use when you want to call this function only a few times. 当您只想调用此函数几次时,最好使用此代码。 But if you want to call it for frequently, then you'll end up calculating the same thing many times. 但是,如果您想经常调用它,那么最终您将多次计算同一件事。 Here you should keep track of already calculated values. 在这里,您应该跟踪已经计算出的值。

For example, if you call fib(n) it will calculate n th Fibonacci number and return it. 例如,如果您调用fib(n) ,它将计算第n个斐波那契数并返回它。 For the next time if you call fib(n) it will again calculate it and return the result. 下次调用fib(n) ,它将再次计算它并返回结果。 What if we store this value somewhere and next time retrieve it whenever required? 如果我们将此值存储在某个地方,下次需要时再次检索该值怎么办?

This will also help in calculating Fibonacci numbers greater than n th Fibonacci number. 这也将有助于计算大于第n个斐波那契数的斐波那契数。 How? 怎么样?

Say we have to calculate fib(n+1), then by definition fib(n+1) = fib(n) + fib(n-1) . 假设我们必须计算fib(n + 1),然后根据定义fib(n+1) = fib(n) + fib(n-1) Because, we already have fib(n) calculated and stored somewhere we can just use that stored value. 因为,我们已经计算出了fib(n)并将其存储在某个地方,我们只能使用该存储的值。 Also, if we have fib(n) calculated and stored, we already have fib(n-1) calculated and stored. 另外,如果我们已经计算并存储了fib(n) ,那么我们已经已经计算并存储了fib(n-1) Read it again. 再次阅读。

We can do this by using a JavaScript object and the same recursive function we used above (Yes, the recursive one!). 我们可以通过使用JavaScript对象和上面使用的相同递归函数(是的,递归函数!)来做到这一点。 See the below code. 请参见下面的代码。

// This object will store already calculated values
// This should be in the global scope or atleast the parent scope
var memo = {};

// We know fib(0) = 0, fib(1) = 1, so store it
memo[0] = 0;
memo[1] = 1;

function fib(n) {
    var sign = n >= 0 ? 1 : -1;
    n = Math.abs(n);

    // If we already calculated the value, just use the same
    if(memo[n] !== undefined)
        return sign*memo[n];

    // Else we will calculate it and store it and also return it
    return sign*(memo[n] = fib(n-1) + fib(n-2));
}

// This will calculate fib(2), fib(3), fib(4) and fib(5). 
// Why it does not calculate fib(0) and fib(1) ? 
// Because it's already calculated, goto line 1 of this code snippet
console.log(fib(5)); // 5

// This will not calculate anything 
// Because fib(-5) is -fib(5) and we already calculated fib(5)
console.log(fib(-5)); // -5

// This will also not calculate anything
// Because we already calculated fib(4) while calculating fib(5)
console.log(fib(4)); // 3

// This will calculate only fib(6) and fib(7)
console.log(fib(7)); // 13

Try out some test cases. 试用一些测试用例。 It's easy to understand why this is faster. 很容易理解为什么这样做更快。

Now you know you can store the already calculated values, you can modify your solution to use this approach without using recursion as for large numbers the recursive approach will throw Uncaught RangeError . 现在您知道可以存储已经计算出的值,可以修改解决方案以使用此方法而无需使用递归,因为对于较大的数量,递归方法将抛出Uncaught RangeError I leave this to you because it's worth trying on your own! 我将其留给您,因为值得您自己尝试!

This solution uses a concept in programming called Dynamic Programming. 该解决方案使用了称为动态编程的编程概念。 You can refer it here . 您可以在这里参考。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM