斐波那契数词在Java中

时间:2019-08-13 07:49:17

标签: javascript fibonacci

我有以下代码:

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;
}

这将返回给定(n)的斐波那契数。

我的问题是我必须提高这段代码的性能。我尝试使用不同的斐波那契公式(指数公式),但由于phi数字具有无限的小数位数,所以我失去了很多精度,因此我必须截断,并且对于大数我失去了很多精度。

例如,当我执行fib(200000)时,我得到了巨大的数字,但是代码花费了超过12000 ms。

另一方面,我尝试使用递归,但性能下降。

您能给我一篇文章或线索吗?

谢谢与问候。

2 个答案:

答案 0 :(得分:1)

如果仅将前一个值添加到当前值,然后将旧的当前值用作前一个值,则性能将得到显着改善。

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

您还可以在父范围中使用数组来存储以前的值,以避免重做相同的计算。

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

答案 1 :(得分:1)

首先,您可以参考答案here

  

Fib(-n)= -Fib(n)

这是递归实现,效率不如您提到

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));
}

这非常简单,我无需赘述,因为如果您了解斐波那契数列,您就会知道上述代码的作用。如您所知,这对于很大的数不是很好,因为它递归地一次又一次地计算相同的事物。但是稍后我们将在我们的方法中使用它。

现在正在寻求更好的方法。简洁一些,请参见下面的代码。

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;
}

当您只想多次调用此函数时,最好使用此代码。但是,如果您想经常调用它,那么最终您将多次计算同一件事。在这里,您应该跟踪已经计算出的值。

例如,如果您呼叫fib(n),它将计算出第n个斐波那契数并将其返回。下次如果您调用fib(n),它将再次计算它并返回结果。 如果我们将此值存储在某个地方,下次需要时再次检索该值怎么办?

这也将有助于计算大于第n个斐波那契数的斐波那契数。 怎么样?

假设我们必须计算fib(n + 1),然后根据定义fib(n+1) = fib(n) + fib(n-1)。因为,我们已经计算出fib(n)并将其存储在某个地方,所以我们只能使用该存储的值。另外,如果我们已经计算并存储了fib(n),那么我们已经已经计算并存储了fib(n-1)。再次阅读。

我们可以通过使用JavaScript对象和上面使用的相同递归函数(是的,递归函数!)来做到这一点。请参见下面的代码。

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

尝试一些测试用例。很容易理解为什么这样做更快。

现在您知道可以存储已经计算出的值,可以修改解决方案以使用此方法而无需使用递归,因为对于较大的数目,递归方法将抛出Uncaught RangeError。我把这个留给您,因为值得您自己尝试!

此解决方案在编程中使用一个称为动态编程的概念。您可以引用它here