生成函数 应用

来源:互联网 发布:fft算法 c语言 编辑:程序博客网 时间:2024/05/18 22:40

引言

生成函数有普通型生成函数和指数型生成函数两种,其中普通型用的比较多。形式上说,普通型生成函数用于解决多重集的组合问题,而指数型母函数用于解决多重集的排列问题。

因水平有限,此博客涉及的均是生成函数的初级应用,用于解决相关的算法竞赛题目。


斐波那契函数的通项公式

考虑一个经典问题:
求斐波那契函数的通项。
此问题的结论了解斐波那契函数的人都知道,那就是:

F(n)=15[(1+52)n(152)n]

此结论初看好像在算法竞赛中没什么用,因为对于求斐波那契函数第n项的值,存在一个经典的算法,那就是矩阵快速幂。而且因浮点数存在误差,故计算机并不能精确计算出该公式的值。

但通过对此公式的深入了解和推导证明,我们将对斐波那契函数有更深的理解,并将矩阵快速幂和生成函数两个算法进行联系。

下面我们试着来推导这个公式。

首先从熟悉的矩阵快速幂来入手。

由递推关系式,可以构造矩阵:

[0111][F(n1)F(n)]=[F(n)F(n+1)]

通过观察已知,这其实也是一个递推式,逐层递推易得:

[0111]n[F(0)F(1)]=[F(n)F(n+1)](1)

假设:

Mn=[0111]n

故问题的关键便在于如何求解Mn

在算法竞赛中,常用的手段是利用计算机的强大计算能力和快速幂的思想来求解。
但实际上,在数学层面还有一个更加常用的方法来求解方阵的n次方,那就是矩阵的相似对角化。
由定义:
M=QAQ1

Mn=QAQ1QAQ1QAQ1....
因:
QQ1=E

故推出:
Mn=QAnQ1(2)

下面我们便来尝试将M矩阵相似对角化:
det(MλE)=0==>λ=1+52152

λ代入MλE 求出基础解系后推出

Q=[21+5215]

将上述矩阵代入(2)式,得:

Mn=[21+5215](1+52)n00(152)n[21+5215]1

然后将Mn代入(1)式,便可推出:

F(n)=15[(1+52)n(152)n]


当然本博客主要是讲解生成函数,所以接下来我们会通过生成函数的角度重新推导一遍该通项公式,但实质上两个方法是相似而又存在联系的。

生成函数我个人理解是通过代数的方法来解决组合数学的问题。

首先由生成函数的定义,可以得出斐波那契数列的生成函数为:
G(x)=x+x2+2x3+3x4+5x5+8x6+...
下面考虑如何化简该无穷级数。

考虑G(x)的第n项 F(n)xn
由递推关系式F(n)=F(n1)+F(n2)
又因为G(x)的第n-1项和第n-2项分别是:
F(n1)xn1F(n2)xn2
故第n-1项乘上x加上第n-2项乘上x2刚好便是第n项的值。

故推出:

x2G(x)+xG(x)=G(x)x
(减去多出来的第一项 x)
移项后得出:
G(x)=x1xx2(3)

接下来再将G(x)展开为幂级数,那么xn 的系数便是我们需要求出的通项公式了

因已知

11x=1+x+x2+x3+x4+....

故我们尝试着将G(x)裂项展开:

利用待定系数法:
假设:

G(x)=a1rx+b1sx

化简得出:
G(x)=(a+b)(as+br)x1(s+r)x+srx2

对比(3)式,解方程得出:
r=1+52s=152

对比矩阵法,这个r和s恰好就是M矩阵的两个特征值

另外解得:

a=15b=15

综上所述:

G(x)=a1rx+b1sx=a(1+rx+(rx)2+...)+b(1+sx+(sx)2+...)

故第n项的系数为:

F(n)=arn+bsn=15[(1+52)n(152)n]


求组合方案数

(1)买水果

其实这类题的解法都很类似,主要是对于生成函数求解方案数原理的深刻理解。

才接触母函数或叫做生成函数时,很多人都会被x的各种次方吓住,其实我们构造指数型的生成函数,xn只是一个记号。

比如我们考虑一个买水果的问题:

现有三种水果:苹果,梨子和西瓜。其中买苹果的数量必须为偶数个,梨子的数量必须为3的倍数,西瓜的数量不多于4个,问买n个水果,有多少种方案数?

首先考虑苹果,记F1(n)=买n个苹果的方案数。
已知

F(n)=1n0n

现在我们希望能方便地统一表示n的所有取值对应的方案数。
我们构建一个幂级数,其中xn的系数便是当买n个苹果的方案数,此处xn没有任何意义,仅仅只是一个标记,代表其系数的数学意义。

设该幂级数为G1(x)
G1(x)=x0+x2+x4+x6...
此时G1(x)便称为生成函数或母函数。

同理,对于梨子:
G2(x)=x0+x3+x6+x9+...

而对于西瓜:
G3(x)=x0+x1+x2+x3+x4

我们构建一个新的函数G(x)=G1G2G3
先直接说结论:G(x)xn的系数便是我们所要求的买n个水果的方案数。

我们可以这么来理解这件事。
假如我们买3个水果,此时的方案数一定包含两个苹果和一个西瓜。
两个苹果的方案数对应在G1里面是 x2的系数。
一个西瓜的方案数对应在G3里面是x的系数。
两个系数对应相乘便是买两个苹果和一个西瓜的方案数。同时G1x2和G3的x相乘便是G中的x3

例题 HDU 2152
代码:

#include<cstdio>#include<cstring>#include<cstdlib>#include<queue>using namespace std;typedef long long ll;const int A = 100 + 10;int c1[A],c2[A];int l[A],r[A];int main(){    int n,m;    while(~scanf("%d%d",&n,&m)){        for(int i=1 ;i<=n ;i++){            scanf("%d%d",&l[i],&r[i]);        }        memset(c1,0,sizeof(c1));        memset(c2,0,sizeof(c2));        for(int i=l[1] ;i<=r[1]&&i<=m ;i++) c1[i] = 1;  //初始化        for(int i=2 ;i<=n ;i++){                        //枚举其他水果            for(int j=0 ;j<=m ;j++){                    //模拟多项式乘法过程                     for(int k=l[i] ;k<=r[i] && j+k<=m ;k++){                    c2[j+k] += c1[j];                                  }            }            for(int j=0 ;j<=m ;j++){                c1[j] = c2[j];                c2[j] = 0;            }        }        printf("%d\n",c1[m]);    }    return 0;}

(2)砝码称重问题

考虑一个称重问题:

假设我们现在要称重一个质量为n的物体,现在有三个砝码,分别重为a1,a2,a3,砝码可放在天平的左侧或右侧。问称出这个质量为n的物体有多少种不同方案。

此问题跟上一个问题类似,我们同样可以构建三个生成函数:
G1(x)=x0+xa1+xa1
G2(x)=x0+xa2+xa2
G3(x)=x0+xa3+xa3
然后相乘之后求xn的系数就好了。

(3)整数的划分

整数的划分问题也是求方案数,下面举一个例子:

对于3,有3 = 1 + 1 + 1 = 1 + 2 = 3
故有三种划分方案。

对于求整数n的划分数
可以看成是有n个无区别的小球,放进任意个非空的盒子里,求方案数

此题也可以用生成函数来求解,我们来考虑整数1-n的贡献。
对于数字1:每次可以贡献1
G(x)=x0+x1+x2+...
对于数字2:每次可以贡献2
G(x)=x0+x2+x4+...
以此类推

然后将1-n所有数字的生成函数乘起来,求xn的系数即可。

当然,敏锐的人能够发现,当n很大时,计算量可以说是爆炸的。
所以实际上,这个经典问题可以考虑DP求解。

dp[i][j]是将j个无区别的物品划分成不超过i组的方案数。
dp[i][j]=dp[i][ji]+dp[i1][j]
这个递推表达式的由来不是本博客的重点= = 有兴趣的可以自己百度

拓展:上述问题是看成无区别的物品划分,若是有区别的物品划分方案数,则是一个经典数论知识:Bell数。Bell数的生成函数G(x)为:
G(x)=eex1


(待更新)

参考资料:
知乎问答
百度百科
Matrix67博客
参考博客

原创粉丝点击