あいさか たいがblogAisaka_Taiga的博客
//https://img2018.cnblogs.com/blog/1646268/201908/1646268-20190806114008215-138720377.jpg

浅谈斐波那契数列和卡特兰数

Toretto·2023-05-24 21:18·191 次阅读

浅谈斐波那契数列和卡特兰数

斐波那契数列

斐波那契数列是我们较为熟悉的一类数列了,在学习递归和递推的时候我们就已经能求解 n 较小的情况了;斐波那契数列的定义如下:

{Fn=0n=0Fn=1n=1Fn=Fn1+Fn2n2

卢卡斯数列#

卢卡斯数列经常作为一个工具来研究斐波那契数列,所以这里也会提到一部分

其定义如下:

{Ln=2n=0Ln=1n=1Ln=Ln1+Ln2n2

斐波那契数列通项公式#

n 个斐波那契数列可以在 O(n) 的时间内用递推来解决,但我们有更快速的方式来计算。

例如我们有下面的公式

Fn=(1+52)n(152)n5

证明:

由上面我们知道斐波那契数列的递推公式为

Fn=Fn1+Fn2(n2)

我们设

FnλFn1=μ(Fn1λFn2)

为什么这么设呢,这是因为我们发现构造一个等差数列的话是很难的,所以我们尝试构造一个等比数列 bn=q×bn1,按照上面的设法,把 FnλFn1 看作 bn 即可发现我们构造了一个等比数列的公式,然后我们后面才能利用等比数列的通项公式进行求解。

拆开移项得到

{λ+μ=1λ×μ=1

解得

{λ=1+52μ=152{λ=152μ=1+52

将其带回原式子可以得到

{Fn1+52Fn1=152(Fn11+52Fn2)Fn152Fn1=1+52(Fn1152Fn2)

然后根据等比数列通项公式,我们得到

{Fn1+52Fn1=(152)n2(F21+52F1)Fn152Fn1=(1+52)n2(F2152F1)

然后上式乘以 152 ,下式乘以 1+52 化简就可以得到上面的通项公式了。

image

或者可以看看上面这位b站大佬的证明过程,比上面的方法更好理解。

需要注意的是,这个公式对于精度要求较高。

卢卡斯数列的通项公式#

其实他的通项公式和斐波那契的很像

Ln=(1+52)n+(152)n

事实上有:

Ln+Fn52=(1+52)n

其实还有一个式子:

Ln25Fn2=4

矩阵加速求斐波那契数列#

我们在之前的题目遇见的求斐波那契数列第 n 项的值范围都是很小的,因为递归的速度太慢,如果数据范围到达了 1018 那么我们递推也是一定 TLE 的,所以这个时候就需要用到我们的矩阵加速递推。

Fib(n) 表示一个 1×2 的矩阵 [FnFn+1] 。我们希望依据 Fib(n1)=[Fn1Fn2] 推出 Fib(n)

试着来推导一个矩阵 base,使 Fib(n1)×base=Fib(n),也就是 [Fn1Fn2]×base=[FnFn1]

因为 Fn=Fn1+Fn2,所以 base 矩阵第一列一定是 [11],这样才能在进行乘法运算的时候才能令 Fn1Fn2 相加,从而得出 Fn。同理,为了得出 Fn1,矩阵 base 的第二列应该为 [10]

综上所述,base=[1110] ,原式化为 [Fn1Fn2]×[1110]=[FnFn1]

定义初始矩阵 ans=[F2F1]=[11]base=[1110]。那么,Fn 就等于 ans×basen2 这个矩阵的第一行第一列的元素,也就是 [11]×[1110]n2 的第一行第一列的元素。

注意矩阵乘法不满足交换律,所以不能将两个矩阵反过来,另外,对于 n2 的情况,可以直接输出 1

P1962斐波那契数列 - 洛谷

参考代码:

Copy
#include<bits/stdc++.h> #define int long long #define P 1000000007 #define N 110 using namespace std; int n; struct sb{int m[N][N];}ans,base; inline sb cheng(sb a,sb b,int ok) { sb c; for(int i=1;i<=ok;i++) { for(int j=1;j<=ok;j++) { c.m[i][j]=0; for(int k=1;k<=ok;k++) c.m[i][j]=(c.m[i][j]+a.m[i][k]*b.m[k][j])%P; } } return c; } inline sb jzksm(sb x,int y) { sb res=x;y--; while(y) { if(y&1)res=cheng(res,x,2); x=cheng(x,x,2); y>>=1; } return res; } signed main() { cin>>n; if(n==1||n==2){puts("1");return 0;} ans.m[1][1]=1;ans.m[1][2]=1; base.m[1][1]=1;base.m[1][2]=1; base.m[2][1]=1;base.m[2][2]=0; base=jzksm(base,n-2); ans.m[1][1]=(base.m[1][1]+base.m[1][2])%P; cout<<ans.m[1][1]<<endl; return 0; }

快速倍增法#

我们可以用上面的方法得到下面两个等式:

F2k=Fk(2Fk+1Fk)

F2k+1=Fk+12+Fk2

于是我们可以通过这样的方法快速计算两个相邻的斐波那契数(常数比矩阵法小)。返回值是一个二元组 (Fn,Fn+1)

性质#

这里只列出一部分。

  1. 卡西尼性质:Fn1Fn+1Fn2=(1)n

  2. 附加性质:Fn+k=FkFn+1+Fk1Fn

  3. 性质二中 k=n,我们得到 F2n=Fn(Fn+1+Fn1)

  4. 由性质三可以归纳证明,kN,FnFnk

  5. 上述性质可逆,即 FaFb,ab

  6. GCD 性质:gcd(Fm,Fn)=Fgcd(n,m)

斐波那契数列和卢卡斯数列#

不难发现有个上面提到的式子和三角函数公式很像:

Ln+Fn52=(1+52)n

cosnx+isinnx=(cosx+isinx)n

上面两个式子很像。

Ln25Fn2=4

cos2x+sin2x=1

这两个式子也很像。

那么我们大胆推测一下,是不是卢卡斯数列构成的图像很像余弦函数,斐波那契数列构成的图像很像正弦函数?

根据:

(1+52)m(1+52)n=(1+52)n+m

可以得到两下标之和的等式:

2Lm+n=5FnFm+LnLm

2Fm+n=FmLn+LmFn

于是推论就有二倍下标的等式:

L2n=Ln22(1)n

F2n=FnLn

这也是一种快速倍增下标的办法。

模意义下周期性#

考虑模 p 意义下的斐波那契数列,可以容易地使用抽屉原理证明,该数列是有周期性的。考虑模意义下前 p2+1 个斐波那契数对(两个相邻数配对):

(F1,F2),(F2,F3),...,(Fp2+1,Fp2+2)

p 的剩余系大小为 p,意味着在前 p2+1 个数对中必有两个相同的数对,于是这两个数对可以往后生成相同的斐波那契数列,那么他们就是周期性的。

卡特兰数

卡特兰数也算是比较常见的一种

其问题灵活多变,较为经典的有:

  • 在圆上选 2n 个点,将这些点成对连接起来使得所得到的 n 条线段不相交的方案数。

  • 一个栈的进栈序列为 1,2,3,,n 有多少个不同的可能的出栈序列。

  • n 个节点可以构造多少个不同的二叉树?

如果是给定两种操作,一个操作的个数不超过另一种操作的个数,或者两种操作没有交集,求合法操作方案的总数,那么一般就是卡特兰数。

其对应的序列为 1,1,2,5,14,42,132...

递推式#

为了防止冲突,用 H(i) 来表示第 i 个卡特兰数。

该递推关系的解为:

Hn=C2nnn+1(n2)

Hn={1n=01n=1i=1nHi1Hnin2

Hn=Hn1(4n2)n+1

实际上最常用的是第一个公式的变形:

Hn=C2nnC2nn1

例题:P1044[NOIP2003 普及组] 栈 - 洛谷

直接套用公式二即可。

参考代码:

Copy
#include<bits/stdc++.h> #define int long long #define N 1000100 using namespace std; int n,c[N]; signed main() { c[0]=1; cin>>n; for(int i=1;i<=n;i++) c[i]=(c[i-1]*(4*i-2))/(i+1); cout<<c[n]<<endl; return 0; }

封闭形式#

卡特兰数的递推式我们前面说过了,也就是这个:

Hn=i=0n1HiHni1(n2)

其中 H0=1,H1=1 设它的普通生成函数为 H(x)

我们发现卡特兰数的递推式与卷积的形式很相似,因此我们用卷积来构造关于 H(x) 的方程:

H(x)=n0Hnxn

=1+n1i=0n1HixiHni1xni1x

=1+xi0Hixin0Hnxn

=1+xH2(x)

解得:

H(x)=1±14x2x

那么这就产生了一个问题:我们应该取哪一个根呢?我们将其分子有理化:

H(x)=2114x

代入 x=0,我们得到的是 H(x) 的常数项,也就是 H0。当 H(x)=21+14x 的时候有 H(0)=1,满足要求。而另一个解会出现分母为 0 的情况,舍去。

因此我们得到了卡特兰数生成函数的封闭形式:

H(x)=114x2x

参考自OIWIKI

posted @   北烛青澜  阅读(191)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示
目录