• 从菲波那切数列看尾部调用优化


    1、菲波那切数列

      在数学上,斐波那契数列以如下被以递归的方法定义:F0=0,F1=1,Fn=Fn-1+Fn-2(n>=2,n∈N*),用文字来说,就是斐波那契数列列由 0 和 1 开始,之后的斐波那契数列系数就由之前的两数相加。形如:

    0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233……
    

      数学上的计算公式是:

       

      读书时候还有利用线性方程推算过这个公式,不过现在都忘了差不多了 ~~泪奔~~。

      其实用代码描述兔子生娃的故事也没少干,常见算法有递归法和递推法。

    2、递归

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

    说明: 

      递归造成了大量的重复计算,使用递归计算大数字时,性能会特别低。

      函数调用会在内存形成一个"调用记录",又称"调用帧"(call frame),保存调用位置和内部变量等信息。如果在函数A的内部调用函数B,那么在A的调用记录上方,还会形成一个B的调用记录。等到B运行结束,将结果返回到A,B的调用记录才会消失。如果函数B内部还调用函数C,那就还有一个C的调用记录栈,以此类推。所有的调用记录,就形成一个"调用栈"(call stack)。因而,当递归层数过大之后,就可能造成调用栈占用内存过大或者溢出。

    3、递推法

    function fibonacci(n) {
        let current = 0;
        let next = 1;
        for(let i = 0; i < n; i++){
            [current, next] = [next, current + next];
        }
        return current;
    }

    4、 尾调用优化

     尾调用优化是指某个函数的最后一步是调用另一个函数。

    最简单模式:

    function f(x){
      return g(x);
    }

      结合上面的递归说明,尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用记录,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用记录,取代外层函数的调用记录就可以了。   

    菲波那切数列改写成尾调用写法:

    'use strict'
    function fibonacci(n, current = 0, next = 1) {
    	if(n === 1) return next;
    	if(n === 0) return 0;
    	return fibonacci(n - 1, next, current + next);
    }
    

    5、动态规划 

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

     参考:阮一峰--《尾调用优化》

     

  • 相关阅读:
    "use strict"
    jquery.is()
    $.proxy
    windows检测文件夹是否更新.bat脚本 windows循环检查文件夹
    linux下串口多线程通信 ,多串口收发数据错乱问题解决办法
    linux串口配置详解
    linux socket 程序被ctrl+c或者异常终止,提示:bind error:Address already in use,解决办法
    linux下包含自定义.c文件 调用报错未定义解决办法
    linux下对一个文件写完马上读取,读到空白
    linux 下socket通信,client断开service退出解决办法
  • 原文地址:https://www.cnblogs.com/leaf930814/p/8644370.html
Copyright © 2020-2023  润新知