基于递归调用的斐波那契数列和汉诺塔问题

这章博主主要介绍基于递归调用的斐波那契数列和汉诺塔问题的求解,如果同学对递归调用不了解的话请先看博主之前发的一篇《关于递归调用》的文章。

首先我介绍一下斐波那契数列,这个数列可以说是非常神奇的数列,数列的表示是:0,1,1,2,3,5,8,13,21,……一直无穷无尽,这个数列神奇的地方在于,从第三个数字开始,每个数字是由前两个数字之和组成的,而且越往后,后一个数与前一个数的比值越来越接近黄金分割0.618。

我们看到这个数列,就能够明白他与递归调用之间的关系。看过我之前的《关于递归调用》文章的同学就知道,递归调用其实应该从后往前看,要有一种倒推的思想。而斐波那契数列正好是符合这样的特征。

每个数字由前两个数字之和组成,N就是由N-1和N-2组成,想要求N就要求N-1和N-2,而N-1是由N-2和N-3组成,N-2是由N-3和N-4组成,以此类推。

因此我们可以以这样的形式看这个数列

0,1,1
1,1,2
1,2,3
2,3,5
3,5,8
……

所以我们不难写出这个函数的关系式

这里写图片描述

请同学们做一个例题,求斐波那契数列的第N项的值是多少?

下面我贴出C语言版供同学们参考。

#include<stdio.h>
int Fibonacci(int n)
{
    if( n == 0 )
        return 0;
    else if( n == 1)
        return 1;
    else
        return Fibonacci(n-1)+Fibonacci(n-2); 
}
int main()
{
    int n;
    printf("please input n: ");
    scanf("%d",&n);
    printf("Result: %d\n",Fibonacci(n));
    return 0;
}

再请同学们做一道例题:试求出斐波那契数列前N项和是多少?N需要自己手动输入。

下面我贴出C语言版供同学们参考。

#include<stdio.h>
int F1(int n)
{
    if( n == 0 )
        return 0;
    else if( n == 1)
        return 1;
    else
        return F1(n-1)+F1(n-2); 
}
int F2(int n)
{
    int sum=0;
    if( n == 0 )
        return 0;
    else if( n == 1)
        return 1;
    else
        sum=F1(n)+F2(n-1);
    return sum;
}
int main()
{
    int n;
    printf("please input n: ");
    scanf("%d",&n);
    printf("Result: %d\n",F2(n));
    return 0;
}

现在重点介绍一下汉诺塔问题,有兴趣的同学可以看一下。

汉诺塔问题是个很古老的数学问题,是一个只能用递归方法解决的经典例子。问题是这样的:有一个塔,塔里面有3个座,分别是A,B,C,开始时A座上有64个盘子,盘子从小到大垒起来,小的在上,大的在下。有一个和尚想把这64个盘子从A座移动到C座,但规定每次只允许移动一个盘,且在移动的过程中在3个座上的盘子都始终保持大盘在下,小盘在上。移动过程中可以借助B座。试问这样移动盘子的步骤是什么?

首先我们先将复杂的问题简化一下,看看少量的盘子应该如何移动,假设A座上有3个盘子,要让这3个盘子移动到C座,我们应该进行怎么样的步骤。

1、 首先将小盘子和中盘子想办法移动到B座。
2、 然后将最底下的大盘子移动到C座。
3、 最后将小盘子和中盘子移动到C座。

在第一步中,我们可以通过三次转移完成,A—>C(小盘子从A到C),A—>B(中盘子从A到B),C—>A(小盘子从C到A)
在第二步中,我们可以通过一次转移完成,A—>C(大盘子从A到C)
在第三步中,我们可以通过三次转移完成,B—>A(小盘子从B到A),B—>C(中盘子从B到C),A—>C(小盘子从A到C)

这样我们就将3个盘子从A座成功的转移到了C座

现在我们将这样的过程拓展为一般情况,如果要将N个盘子从A移动到C可以分解为三个步骤。

1、 将A上的N-1个盘子移动到B
2、 将A上的第N个盘子移动到C
3、 将B上的N-1个盘子借助A移动到C

通过这样的分析我们可以明白在这个问题中第一个和尚只需要这样做:

1、 让第二个和尚将63个盘子从A移动到B
2、 自己将最后一个盘子从A移动到C
3、 让第二个和尚将63个盘子从B移动到C

那么这样问题又来了,第二个和尚怎么将63个盘子从A借助C移动到B,再从B借助A移动到C呢?其实不管是从A移动到B还是从B移动到C,本质上都是从一个座移动到另一个座上,虽然移动的名称不同但是使用的方法都是相同的。因此可以看出汉诺塔问题在这里需要进行两次自身的调用。

为了解决这个问题第二个和尚只需这样做:

1、 让第三个和尚将62个盘子从A移动到C
2、 自己将一个盘子从A移动到B
3、 让第三个和尚将62个盘子从C移动到B

这样他就完成了将63个盘子从A移动到B的过程,至于再从B移动到C也是通过同样的方法。如此层层递归下去,直到后来找到第64个和尚,让他完成最后一个盘子的移动过程。至此,全部工作都已经完成了。

现在让我们试着写一下汉诺塔函数的函数体

首先这个函数我们需要4个参数,一个参数N用来表示我们有几个盘子,剩下三个参数用来表示我们移动的座位。在这里我用英文数字一、二、三表示用来对应A座B座C座。

void Hanoi(int n,char one,char two,char three)       //表示第n个数,从第one座借助two座移动到three座

在这里我们需要确定非递归函数的初始值,第64个和尚所需要移动的盘子的位置目前看来是不可知的,而表面上第1个和尚所需要做的事情其实是整个汉诺塔问题里面最后一个完成的步骤。因此我们得出了这个非递归函数的初始值。

当n==1时,我们需要将第64个盘子从one移动到three

另外我们还需要写一个移动盘子的方法,这个方法只需要两个参数——移动的两个座

void move(char x,char y)
{
    printf(“%c—>%c\n”,x,y);
}

现在我们需要构思这个调用的问题,我们之前将整个汉诺塔问题分成了三步骤。

第一步将one上的n-1个盘子移动到two
第二步将第n个盘子移动到three
第三步将two上的n-1个盘子移动到three

写成代码的形式就是:

第一步:hanoi(n-1,one,three,two)    //将n-1个盘子从one座借助three座移动到two座
第二步:move(one,three)
第三步:hanoi(n-1,two,one,three)    //将n-1个盘子从two座借助one座移动到three座

到这里我相信很多同学能够弄清楚汉诺塔的核心问题了。请同学们自行补出完整的代码。下面我贴出C语言版供同学参考。

#include<stdio.h>
void move(char x,char y)
{
    printf("%c—>%c\n",x,y);
}
void hanoi(int n,char one,char two,char three)
{
    if( n == 1)
        move(one,three);
    else
    {
        hanoi(n-1,one,three,two);
        move(one,three);
        hanoi(n-1,two,one,three);
    }
}
int main()
{
    int m;
    printf("input the number of diskes:");
    scanf("%d",&m);
    printf("The step to move %d diskes:\n",m);
    hanoi(m,'A','B','C');
}

好了,基于递归调用的斐波那契数列和汉诺塔问题到这就结束了。

有什么意见或者建议可以在下方留言,有什么看不懂得地方可以私聊博主。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值