美文网首页javascriptWeb前端之路让前端飞
使用缓存解决js递归调用性能问题

使用缓存解决js递归调用性能问题

作者: scarecrowlxb | 来源:发表于2017-08-30 11:58 被阅读93次

说明

这是在codewars.com上刷的一道js练习题,在此做个记录

问题描述

The Fibonacci sequence is traditionally used to explain tree recursion.

斐波那契序列通常是用来解释递归调用。

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

This algorithm serves welll its educative purpose but it's tremendously inefficient, not only because of recursion, but because we invoke the fibonacci function twice, and the right branch of recursion (i.e. fibonacci(n-2)) recalculates all the Fibonacci numbers already calculated by the left branch (i.e. fibonacci(n-1)).

这个算法以教学为目的,但非常低效的,不仅因为递归,而且两次调用fibonacci函数,在函数里面右侧调用的fibonacci(n-2) 在表达式左侧调用fibonacci(n-1)时就已完全计算过一遍。

This algorithm is so inefficient that the time to calculate any Fibonacci number over 50 is simply too much. You may go for a cup of coffee or go take a nap while you wait for the answer. But if you try it here in Code Wars you will most likely get a code timeout before any answers.

这个算法效率是如此之低,斐波纳契数超过50的实在太多了。你可以去喝杯咖啡或去睡午觉时等待答案。但如果你就用这个代码在codewars上很可能得到一个超时错误。

For this particular Kata we want to implement the memoization solution. This will be cool because it will let us keep using the tree recursion algorithm while still keeping it sufficiently optimized to get an answer very rapidly.

对于这个特定卡塔(类似打怪升级里面的级数),我们想实现缓存的解决方案。这是特别酷的,因为它将让我们继续使用递归算法,同时仍然保持足够迅速的得到一个答案。

The trick of the memoized version is that we will keep a cache data structure (most likely an associative array) where we will store the Fibonacci numbers as we calculate them. When a Fibonacci number is calculated, we first look it up in the cache, if it's not there, we calculate it and put it in the cache, otherwise we returned the cached number.

memoize的版本的诀窍是,保持一个缓存数据结构(最有可能的关联数组),将斐波纳契数列的值缓存。当获取一个斐波那契数列值时,首先在缓存中查找,如果有则直接返回值,如果没有,再计算并把它放进缓存。

Refactor the function into a recursive Fibonacci function that using a memoized data structure avoids the deficiencies of tree recursion Can you make it so the memoization cache is private to this function?

使用memoize的数据结构重构函数的递归Fibonacci以避免递归调用的缺陷。

分析

斐波那契数列里面不断的递归调用自身,列入输入的是 70,那么需要计算69和68的值。
在计算69的过程中又计算了 68、67、、、、、1。 计算 68的过程又计算了 67、66、、、、、、、1的值,如此重复计算的值太多了,花费的时间也就比较多。

缓存思想恰好可以减少不必要的重复计算。当第一遍计算69的值时就递归计算了 68、67、66、、、1的值,之后的每次都先查看是否有缓存,有就直接返回缓存值,避免了重复计算。

代码

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

性能测试

//没有缓存时
let tesetNum = 40;
console.time('NoCache');
function fibonacci1(n) {
    if(n==0 || n == 1)
        return n;
    return fibonacci1(n-1) + fibonacci1(n-2);
}
fibonacci1(tesetNum);
console.timeEnd('NoCache');

// 使用缓存时
console.time("HasCache");
let cache = {};
let fibonacci = function(n) {
    if(n==0 || n == 1)
        return n;
    if(cache[n]){
      return cache[n];
    }
    
    return cache[n] = fibonacci(n-1) + fibonacci(n-2);
}
fibonacci(tesetNum);
console.timeEnd('HasCache');

// 输出
// NoCache: 1717.834ms
// HasCache: 0.159ms

通过性能测试可以看到,当测试数是40时不适用缓存消耗的时间就是使用缓存的1700多倍(好可怕的数据),我试了下当测试数据是300时,,,,,,,,我就等不急它的执行了。

使用场景

当递归调用里有大量重复计算的情景,或者组件、数据等重复加载的情况下,使用缓存是个不错的选择(典型的以空间换时间)

相关文章

  • 使用缓存解决js递归调用性能问题

    说明 这是在codewars.com上刷的一道js练习题,在此做个记录 问题描述 The Fibonacci se...

  • Bean的循环依赖

    问题 Bean的实例化分三步 如何解决 为了解决单例的循环依赖问题,使用了三级缓存,递归调用发现Bean还在创建中...

  • JAVA编程基础之递归结构

    递归结构 递归是一种常见的解决问题的方法,即把问题逐渐简单化。 递归的基本思想就是 自己调用自己 ”,一个使用递归...

  • 递归

    1.递归是什么? 定义:程序调用自身的编程技巧称为递归。递归使用的是选择结构,对于解决同样问题的孪生兄弟:迭代,它...

  • 前端开发 -- 算法模式(递归和动态规划)

    递归 递归是一种解决问题的方法,它解决问题的各个小部分,直到解决最初的大问题,递归通常涉及到函数的自身调用。递归函...

  • 第二章 递归和回溯

    递归 递归的含义:任何调用自身的函数称为递归。用递归求解问题要点在于递归函数调用自身取解决一个规模比原始问题小一些...

  • Java--递归-1

      递归是一种常见的解决问题的方法,即把问题逐渐简单化。递归的基本思想就是“自己调用自己”,一个使用递归技术的方法...

  • C 递归

    C 允许函数调用自己,这种过程称为递归。 一般而言,可以使用循环的地方都可以使用递归。有时循环解决问题好,有时使用...

  • 4.递归

    首先所有的递归都可以使用循环来替代递归并不是一种高性能解决问题的方式,但使用递归可以使代码更易于理解与阅读,更优雅...

  • 31-方法递归调用

        方法的递归调用指的是一个方法自己调用自己的情况,利用递归调用可以解决一些重复且麻烦的问题。在进行方法递归调...

网友评论

    本文标题:使用缓存解决js递归调用性能问题

    本文链接:https://www.haomeiwen.com/subject/lhozdxtx.html