斐波那契数列的各种优化:尾递归(递归不爆栈),记忆化搜索,动态规划

目录

1.普通的递归斐波那契数列

2.尾递归:在使用递归的情况下,不爆栈

3.记忆化搜索:减少不必要的重复计算,自上而下

 4.动态规划:自上而下,非递归


1.普通的递归斐波那契数列

function fac($n){
    if($n == 1 || $n == 2) return 1;
    else return fac($n-2) + fac($n-1)
}

2.尾递归:在使用递归的情况下,不爆栈

普通的递归,运行栈会被函数的递归调用占满了

因此在要求使用递归的情况下,可以使用尾递归

每次调用函数时不生成新的运行栈,利用上一次的结果 

/**
 * $a充当收集器,收集上一次运行栈的返回值,之后栈空间会被回收
 * $a和$b参与每次的计算
 * $n是斐波那契数列执行的次数
 */
function fac1($a,$b,$n){
    if($n>2) return fac1($a+$b,$a,$n-1);
    return $a;
}

3.记忆化搜索:减少不必要的重复计算,自上而下

在原本的斐波那契递归中,总会像如下图一样递归到最后再返回结果

其中,例如 以5为例,3 往下的部分,就会被重复计算两次,2往下的部分会被重复计算3次

如果数据量大的情况下,记忆化搜索减少的计算量是十分可观的

斐波那契数列的各种优化:尾递归(递归不爆栈),记忆化搜索,动态规划_第1张图片

用该方法可以大大优化斐波那契数列解决速度 

class Solution{
    private $memory = [];            //记录当前数字是否已经求出解
    public function fac($n){
        if($n == 0) return 0;
        if($n == 1) return 1;
        if(empty($this->memory[$n])) //若没有对应的解,则继续进行递归
            $this->memory[$n] = $this->fac($n-1) + $this->fac($n-2);
        return $this->memory[$n];    //有解则直接返回该斐波那契数列
    }
}
$q = new Solution();
echo $q->fac(40);

 4.动态规划:自上而下,非递归

非递归,减少了系统栈的建立,比记忆化搜索还快

将原问题拆解成若干子问题,同时保存子问题的答案,使得每个子问题只求解一次,最终获得原问题的答案

function fac($n){
    if($n == 1 || $n == 2) return 1;
    $a = 1;
    $b = 1;
    $res = 0;
    for($i = 3;$i<=$n;++$i){
        $res = $a + $b;
        $a = $b;
        $b = $res;
    }
    return $res;
}

 

你可能感兴趣的:(▼,算法)