初识算法之斐波那契数列

初识算法之斐波那契数列

算法(Algorithm)是指用来操作数据、解决程序问题的一组方法。对于同一个问题,使用不同的算法,也许最终得到的结果是一样的,但在过程中消耗的资源和时间却会有很大的差别。

和大多数人一样,初识算法从阶乘和斐波那契数列开始,但是不一样的算法真的存在很大的差别吗?

怀着这样好奇心,于是乎用斐波那契数列做了一组小小的实验。

斐波那契数列(Fibonacci)

ps:以下都是js的简单代码。

斐波那契数列(Fibonacci sequence),又称[黄金分割]数列,因数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“[兔子数列]。指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)。

//以下实例皆略去0,从第一项1开始

//直观的递归调用方法function1
function norFibonacci1(n) {               
    if(n <= 2) {
        return 1;
    }
    return norFibonacci1(n - 1) + norFibonacci1(n - 2);
}

//直观的循环方法function2
function norFibonacci2(n) {
    let fir = 1;
    let sec = 1;
    if(n === 1) {
        return fir;
    }
    if(n === 2) {
        return sec;
    }
    for (let i = 0; i < n - 2; i++) {
        let temp = fir;
        fir = sec;
        sec = sec + temp;
    }
    return sec;
}

//休整过的递归方法function3
function norFibonacci3(a, b, n) {
    if(n === 1) {
        return a;
    }
    if(n === 2) {
        return b;
    }
    return norFibonacci3(b, a+b, n - 1);
}

//以下是计时器三个

function timer1(n) {
    let time1 = new Date().getTime();
    console.log(norFibonacci1(n));
    let time2 = new Date().getTime();
    console.log('耗时:' + (time2 - time1)/1000 + '秒')
}

function timer2(n) {
    let time1 = new Date().getTime();
    console.log(norFibonacci2(n));
    let time2 = new Date().getTime();
    console.log('耗时:' + (time2 - time1)/1000 + '秒')
}

function timer3(n) {
    let time1 = new Date().getTime();
    console.log(norFibonacci3(1,1,n));
    let time2 = new Date().getTime();
    console.log('耗时:' + (time2 - time1)/1000 + '秒')
}

timer1(45);
timer2(45);
timer3(45);

结果为:

> 1134903170
> 耗时:11.101> 1134903170
> 耗时:0> 1134903170
> 耗时:0.001

由于电脑比较老的原因,上到50的时候,function1已经突破100秒了,而function2和function3依旧耗时基本在1毫秒以内。

正所谓人理解迭代,神理解递归,递归第一个好慢嘞。why?于是乎又在上面的function里加入了计算次数统计。
let num1 = 0;
let num2 = 0;
let num3 = 0;
function norFibonacci1(n) {
    num1++;
    if(n <= 2) {
        return 1;
    }
    return norFibonacci1(n - 1) + norFibonacci1(n - 2);
}

//直观的循环方法function2
function norFibonacci2(n) {
    let fir = 1;
    let sec = 1;
    if(n === 1) {
        return fir;
    }
    if(n === 2) {
        return sec;
    }
    for (let i = 0; i < n - 2; i++) {
        num2++
        let temp = fir;
        fir = sec;
        sec = sec + temp;
    }
    return sec;
}

//休整过的递归方法function3
function norFibonacci3(a, b, n) {
    num3++
    if(n === 1) {
        return a;
    }
    if(n === 2) {
        return b;
    }
    return norFibonacci3(b, a+b, n - 1);
}

//以下是计时器三个

function timer1(n) {
    let time1 = new Date().getTime();
    console.log(norFibonacci1(n));
    let time2 = new Date().getTime();
    console.log('耗时:' + (time2 - time1)/1000 + '秒', '次数为:' + num1)
}

function timer2(n) {
    let time1 = new Date().getTime();
    console.log(norFibonacci2(n));
    let time2 = new Date().getTime();
    console.log('耗时:' + (time2 - time1)/1000 + '秒', '次数为:' + num2)
}

function timer3(n) {
    let time1 = new Date().getTime();
    console.log(norFibonacci3(1,1,n));
    let time2 = new Date().getTime();
    console.log('耗时:' + (time2 - time1)/1000 + '秒', '次数为:' + num3)
}

timer1(45);
timer2(45);
timer3(45);

结果为:

> 1134903170
> 耗时:26.135秒 次数为:2269806339
> 1134903170
> 耗时:0秒 次数为:43
> 1134903170
> 耗时:0秒 次数为:44

因为加入了执行次数计算,明显的时间又变长了。但是我们可以明显看到这个计算次数的差异,普通的递归方法计算次数极大,基本是指数型的增长方式,而循环迭代和休整过的递归方法计算程线性增长,次数少,耗时自然也就少了。为什么说是指数型和线性型。function2和function3很直观的可以看到,n为多少,基本就执行了多少次,计算次数是线性型的增长。而function1普通递归则不一样,请看下图:

在这里插入图片描述

计算的次数呢就是:1 + 2 + … + 2n-3 + 2 = 2n-2 + 1。

计算次数会随着n的增长,指数型的爆炸,所以耗时会越来越严重。到这里我们就要介绍下一个概念:算法的时间复杂度和空间复杂度。
有关部分请看下个内容:初识算法之时间空间复杂度与空间复杂度。

总结:
不同的算法,确实会有区别的存在,实际运用中,算法的选用十分的重要。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值