斐波那契数列乞降的js计划以及优化

codewars上做了一道斐波那契数列乞降的题目,做完今后做了一些简朴的优化和用另一种要领完成。

题目

function fibonacci(n) {
    if(n==0 || n == 1)
        return n;
    return fibonacci(n-1) + fibonacci(n-2);
}

以上函数运用递归的体式格局举行斐波那契数列乞降,但效力非常低,许多值会反复求值。题目请求运用 memoization计划举行优化。

My Solution

memoization计划在《JavaScript形式》和《JavaScript设想形式》都有提到。memoization是一种将函数实行结果用变量缓存起来的要领。当函数举行盘算之前,先看缓存对象中是不是有次盘算结果,假如有,就直接从缓存对象中猎取结果;假如没有,就举行盘算,并将结果保存到缓存对象中。

let fibonacci = (function() {
  let memory = []
  return function(n) {
      if(memory[n] !== undefined) {
        return memory[n]
    }
    return memory[n] = (n === 0 || n === 1) ? n : fibonacci(n-1) + fibonacci(n-2)
  }
})()

运用闭包完成的memoization函数。测试通过今后,倏忽我有一个小疑问,假如将memory的范例由数组换成对象,它的运算效力会有什么变化?因而,我将memory的范例换成了对象,并写了一个函数测试两种数据范例的运算效力。

function speed(n) {
    let start = performance.now()
    fibonacci(n)
    let end = performance.now()
    console.log(end - start)
}

一切测试只在Chrome掌握台测试,而且测试次数不多,结果不严谨,请多多见谅。

memory范例为数组时(单元:毫秒):

speed(500)      // 0.8150000050663948
speed(5000)     // 3.1799999997019768
speed(7500)     // 4.234999991953373
speed(10000)    // 8.390000000596046

memory范例为对象时(单元:毫秒):

speed(500)      // 0.32499999552965164
speed(5000)     // 1.6499999985098839
speed(7500)     // 2.485000006854534
speed(10000)    // 2.9999999925494194

虽然测试历程不严谨,但照样能够申明一点题目的。memory范例为对象是显著比范例为数组时,运算速率快许多。至于为何对象操纵比数组操纵的速率快,请原谅我水平有限,临时答不上来。(先挖好坑,今后返来填坑,逃)如今返来填坑,比方我们挪用fibonacci(100),这时刻,fibonacci函数在第一次盘算的时刻会设置memory[100]=xxx,此时数组长度为101,而前面100项会初始化为undefined。正因为如此,memory的范例为数组的时刻比范例是对象的时刻慢。(这里药谢谢hsfzxjy的提示)

Best Solution

他人的处理计划给了我灵感,让我想出了一个缓存效力高许多的计划。

var fibonacci = (function () {
  var memory = {}
  return function(n) {
    if(n==0 || n == 1) {
      return n
    }
    if(memory[n-2] === undefined) {
      memory[n-2] = fibonacci(n-2)
    }
    if(memory[n-1] === undefined) {
      memory[n-1] = fibonacci(n-1)
    }
    return memory[n] = memory[n-1] + memory[n-2]
  }
})()

测试结果就不放了(因为我发如今Chrome掌握台中运转测试代码时,输出结果不稳定)。不过,这里的缓存效力的确是提高了,前面的计划,一次盘算最多缓存一个结果,而这个计划,一次盘算最多缓存三个结果。从这个方面斟酌,运算速率理论上是会比前面的计划快的。

动态计划处理计划

斐波那契数列乞降除了能够用递归的要领处理,还能够用动态计划的要领处理。因为我是算法渣,对动态计划相识不多,只懂一点点外相,所以这里就不诠释动态计划的概念了。(一不小心又挖了一个坑,逃)

直接贴代码好了:

function fibonacci(n) {
    let n1 = 1,
        n2 = 1,
        sum = 1
    for(let i = 3; i <= n; i += 1) {
        sum = n1 + n2
        n1 = n2
        n2 = sum
    }
    return sum
}

尾递归计划

在ES6范例中,有一个尾挪用优化,能够完成高效的尾递归计划。(谢谢李引证的提示)

'use strict'
function fibonacci(n, n1, n2) {
    if(n <= 1) {
        return n2
    }
    return fibonacci(n - 1, n2, n1 + n2)
}

ES6的尾挪用优化只在严厉形式下开启,一般形式是无效的。

通项公式计划

斐波那契数列是有通项公式的,但通项公式中有开方运算,在js中会存在偏差,而fib函数中的Math.round正式处理这一题目的。(谢谢公子的指点)

function fibonacci(n){
    var sum = 0
    for(let i = 1; i <= n; i += 1) {
        sum += fib(i)
    }
    return sum

    function fib(n) {
      const SQRT_FIVE = Math.sqrt(5);
      return Math.round(1/SQRT_FIVE * (Math.pow(0.5 + SQRT_FIVE/2, n) - Math.pow(0.5 - SQRT_FIVE/2, n)));
    }
}

结语

只需注重细节,我们的代码照样有很大的优化空间的。有时刻,你可能会迷惑,优化前后的机能没有显著的变化。我以为,那是你的运用范围或许数据量不够大罢了,当它们大到肯定水平的时刻,优化的结果就很显著了。优化照样要对峙的,万一哪一天我们接办大型运用呢?

    原文作者:chenBright
    原文地址: https://segmentfault.com/a/1190000007115162
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞