计算机递推浅谈

  回想一下,又鸽了一个月呢,今天高产一波。

  看到“递推”这个名词,想必大家有一种亲切又陌生的感觉吧,这个词起源于数学,说的其实是一种数学思想。指的是后面一种状态与前面的状态之间的关系,例如一道小学奥数题

一,斐波那契数列

  斐波那契数列又名兔子数列,因为他的初始题目就是计算兔子的。假设一对刚出生的小兔一个月后就能长成大兔,再过一个月就能生下一对小兔,并且此后每个月都生一对小兔,一年内没有发生死亡,那么一对刚出生的兔子,在一年内繁殖成多少对兔子?

  第一个月两只小兔子,有一对兔子;

  第二个月小兔子长大了,但还没生出新的兔子,有一对兔子;

  第三个月两个大兔子生了两只小兔子,有两对兔子了;

  第四个月 两只小兔子长大,但还没生小兔子,两只大兔子又产了两只小兔子,有三对兔子;

  第五个月两对大兔子生了两对小兔子,那对小兔子长大了,还没生兔子,一共是五队兔子;

  以此类推,经过计算,每个月的兔子对数分别是1,1,2,3,5,8,13,21,34,55,89,144,所以一年内繁殖成了144对兔子!

  不得不说这一串数字很有点意思啊,经过观察,从第三位“2”开始,每个数,都是前两个数之和。

  这就是斐波那契数列的规律,f(n)=f(n-1)+f(n-2)。

  f(n)=f(n-1)+f(n-2)就是斐波那契数列的递推公式

  参考一下求斐波那契数列前50位的代码。

#include<bits/stdc++.h>
using namespace std;
long long a[105];
int main()
{
    a[1]=1;a[2]=1; //前两位是1 1
    cout<<a[1]<<" "<<a[2]<<" ";
    for(int i=3;i<=50;i++)
    {
        a[i]=a[i-1]+a[i-2];//递推公式
        cout<<a[i]<<" ";
    }
    return 0;
}

  结果:

1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 9227465 14930352 
24157817 39088169 63245986 102334155 165580141 267914296 433494437 701408733 1134903170 1836311903 2971215073 4807526976 7778742049 12586269025

  再来一道简单的递推题目:

  有n级台阶,一次可以上两级或者一级,问有几种上到第n级台阶有几种方法?

  可以先自行思考下……

  这道题其实做法不唯一,用深度优先搜索(https://www.cnblogs.com/qj-Network-Box/p/13910876.html)可以做,不过这不是今天的主题,我也不加赘述了。

  学过递推后当然要用递推做了

  首先我们考虑一下,要想到达第n级台阶,就得先到达第n-1级台阶或者第n-2级台阶(可以一次跨两级或一级台阶),自然到达第n级台阶的方法数就是到达第n-1级台阶和第n-2级台阶的方法数之和。

  即f(n)=f(n-1)+f(n-2)

  一看,这可不是斐波那契数列吗?唯一不同的是,这个数列开头的两个数是1和2。

  同上,利用这个递推公式,我们可以轻易地求出答案,代码如下。

#include<bits/stdc++.h>
using namespace std;
long long a[105];
int n;
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    cin>>n;
    a[1]=1;a[2]=2;// 开头两个数
    for(int i=3;i<=n;i++)
    {
        a[i]=a[i-1]+a[i-2];//递推公式
    }
    cout<<a[n];
    return 0;
}

 

二,递推进阶:汉诺塔问题

  汉诺塔(Tower of Hanoi),又称河内塔,是一个源于印度古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。 

  可以先动手试试这个汉诺塔小游戏,体验一下—— http://www.7k7k.com/swf/201271.htm

  接下来的问题就是我们如果要把n个圆盘全部移到另一个柱子上至少需要多少步?

  应该还是有不少人看到过用递归做的汉诺塔,而这里提供用递推的方法。事实上,在部分情况下递推和递归是可以互换的。

  根据上面两道例子我们应该可以看出递推的精髓在于找出递推公式,那么此题的递推公式该如何寻找呢。

  按照汉诺塔问题的规则,由于大的圆盘在下,小的圆盘在上,所以在我们的目标杆上一定是先把最大的圆盘挪上去才可能完成整套成功的操作。

  即要将第二大的圆盘到最小的圆盘全部移到一根杆上,将最大圆盘挪进目标杆,然后将其他圆盘一点一点挪进目标杆。

  即f(n)=f(n-1)+1+f(n-1)

  得出递推公式 f(n)=2*f(n-1)+1 

  代码如下

#include<bits/stdc++.h>
using namespace std;
long long a[105];
int n;
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    cin>>n;
    a[1]=1;// 只有一个圆盘的情况 
    for(int i=2;i<=n;i++)
    {
        a[i]=2*a[i-1]+1;//递推公式
    }
    cout<<a[n];
    return 0;
}

  是不是简洁轻松明了)))

三,递推高阶:Catalan数

  这个卡特兰数可非同一般,前几个例子看到卡特兰数简直就是小巫见大巫了。

  题面如下:给出一个n边形,使用n-3条对角线,求将这个n边形分成n-2个三角形的方法数。

  这道题也是个纯递推题目,找到递推公式就找到了突破口,代码什么的非常好写,但递推公式着实难以下手;

  我们先设答案为f(n),按照一定的顺序给n边形的定点标号——V1,V2,V3,V4……Vn(顺时针或逆时针),由于最后要将它分成多个三角形,所以我们可以保证v1vn最后都会属于一个△V1VnVk的某条边,那么我们就按照k的位置分类好了。

  稍加思考即可得出,三教乡V1VnVk的左边是一个k边形,右边是个n-k+1边形,根据乘法原理可以得出包含V1VnVk的方案数为f(k)*f(n-k+1)

  根据加法原理,f(n)=f(2)*f(n-1)+f(3)*f*(n-2)+……+f(n-1)*f(2),出现了,递推公式!!!

  当然,方法不唯一,我们也可以从由V1发出的对角线为切入点,对角线V1Vk把n边形分成两部分,根据乘法原理,包含对角线V1Vk的多边形有f(k)*f(n-k+2)个。根据对称性,考虑从V2V3一直到Vn出发的对角线也会有同样的结果,因此一共有n*(f(3)f(n-1)+f(4)f(n-2)+……+f(n-1)f(3))个部分;

  由于同一部分会被重复计算多次,我们需要找到他们是如何重复的

  每个方案会被计算2n-6次,有n-3条对角线,考虑到每条对角线的,每个端点时均被计算了一次。

  这样就能够得到f(n)的递推式f(n)=(f(3)(n-1)+f(4)f(n-2)+……+f(n-1)f(3))*n/(2n-6)

  经过推算得f(n+1)=((4n-6)/n)*f(n);求出了递推公式;

  卡特兰数的代码实现就交给大家了,难度不算大,但对于递推公式如何推出来的一定要多揣摩揣摩。

  好了如果您读了我的文章觉得还不错,请不要吝惜手中的赞和关注,有问题可以在评论区留言哦

posted @ 2021-07-12 15:33  球君  阅读(581)  评论(3编辑  收藏  举报
View Code