• 由斐波那契数列所引发的性能优化


    Fibonacci 数列:一个 Fibonacci 数字是之前两个 Fibonacci 数字之和,最前面的两个数字是 0 和 1 。

    很显然要用一个递归函数来实现:

        var opt = {};
        opt.nLoop = 0;
        function fib(n){
            opt.nLoop++;
            return n < 2 ? n : fib(n-2) + fib(n-1);    
        }
        
        for(var i = 0; i <= 10; i++){
            log('<br>' + i + ':' +fib(i) + '-----------调用了fib函数 '  + opt.nLoop + ' 次');
        } 

    咋一看,没什么问题,但打印出的结果:

    0:0  -----------调用了fib函数 1 次
    1:1  -----------调用了fib函数 2 次
    2:1  -----------调用了fib函数 5 次
    3:2  -----------调用了fib函数 10 次
    4:3  -----------调用了fib函数 19 次
    5:5  -----------调用了fib函数 34 次
    6:8  -----------调用了fib函数 59 次
    7:13 -----------调用了fib函数 100 次
    8:21 -----------调用了fib函数 167 次
    9:34 -----------调用了fib函数 276 次
    10:55-----------调用了fib函数 453 次

    再看代码,我们只在 for 循环里调用了 fib 函数 11 次,而它自身却调用了自己 442 次。如果让该函数具有记忆功能,就可显著地减少运算量。在 js 里用对象或数组实现这种优化是非常方便的。

    我们用一个名为 memo 的数组里保存我们的存储结果,存储结果隐藏在闭包中。当函数被调用时,首先检查结果是否存在,如存在就立即返回它。

        opt.nMemo  = 0;
        //设置函数记忆
        var fibonacci = function (){
            var memo = [0,1];
            var fib = function(n){
                opt.nMemo++;
                var result = memo[n];
                if( typeof result !== 'number'){
                    result = fib(n-2) + fib(n-1);
                    //存储结果
                    memo[n] = result;
                }
                
                return result;
            }
            
            return fib;
        }();
        
        
        for(var i = 0; i <= 10; i++){
            log('<br>' + i + ':' +fibonacci(i) + '-----------调用了fib函数 '  + opt.nMemo + ' 次');
        }

    查看结果

    0:0  -----------调用了fib函数 1 次
    1:1  -----------调用了fib函数 2 次
    2:1  -----------调用了fib函数 5 次
    3:2  -----------调用了fib函数 8 次
    4:3  -----------调用了fib函数 11 次
    5:5  -----------调用了fib函数 14 次
    6:8  -----------调用了fib函数 17 次
    7:13 -----------调用了fib函数 20 次
    8:21 -----------调用了fib函数 23 次
    9:34 -----------调用了fib函数 26 次
    10:55-----------调用了fib函数 29 次

    一目了然,函数被调用的次数大大地减少了!好的想法必须推而广之才能发挥它的最大威力,如果把初始化的值(如 memo 数组)和 计算方式提取出来,就可以完成一个扩展性和复用性更好的组件:

    //通用组件
        var memoizer = function(memo, formula) {
            var recur = function(n) {
                var result = memo[n];
                
                if(typeof result !== 'number') {
                    result = formula(recur, n);
                    memo[n] = result;
                }
                
                return result;
            }
            
            return recur;
        };
        
        var fibonacci = memoizer([0,1], function(recur, n) {
            return recur(n-1) + recur(n-2);
        });
        
        for(var i = 0; i <= 10; i++){
            log('<br>' + i + ':' +fibonacci(i) + '-----------调用了result函数 ' + opt.nMemo + ' 次'); }

    通过设计这种产生另一个函数的函数,极大地减少了我们的工作量。例如,要产生一个可记忆的阶乘函数,只需要提供阶乘公式即可:

    var factorial = memoizer([1,1], function(recur, n) {
      return n * recur(n-1);
    }

    参考:javascript语言精髓

  • 相关阅读:
    你应该掌握的七种回归技术
    jar包运行
    Spark常见问题汇总,spark相关错误和解决方法,,,,,,,,,
    shell把字符串中的字母去掉,只保留数字
    excel求和结果不对
    Matlab实现PCA
    Spring3.0 AOP 具体解释
    POJ 1422 Air Raid(二分图匹配最小路径覆盖)
    TCP、UDP和HTTP
    2014世界互联网乌镇峰会 马云演讲实录
  • 原文地址:https://www.cnblogs.com/kuangliu/p/5131021.html
Copyright © 2020-2023  润新知