普通型母函数
定义:
对于任意数列a0,a1,a2…an 即用如下方法与一个函数联系起来:
~G(x) = a0 + a1x + a2x*2 + a3x^3 +….+ anx^n
则称G(x)是数列的生成函数(generating function)
例子:
比较典型的是:A(x) = (1+x)^n~C(n,0),C(n,1),C(n,2),C(n,3),…..,C(n,n)
生成函数是说,构造这么一个多项式函数g(x),使得x的n次方系数为f(n)。 如:序列{0,1,2,3,4,5…n}的生成函数为:f(x)=0+x+2x^2+3x^3+4x^4+…+nx^n
生成函数最绝妙的是,某些生成函数可以化简为一个很简单的函数。也就是说,不一定每个生成函数都是用一长串多项式来表示的。比如,这个函数f(n)=1 (n当然是属于自然数的),它的生成函数就应该是g(x)=1+x+x^2+x^3+x^4+…(每一项都是一,即使n=0时也有x^0系数为1,所 以有常数项)。再仔细一看,这就是一个有无穷多项的等比数列求和嘛。如果-1
举一个例子说明:
考虑这个问题:从二班选n个MM出来有多少种选法。学过简单的排列与组合的同学都知道,答案就是C(4,n)。也就是说。从n=0开始,问题的答案分别是 1,4,6,4,1,0,0,0,…(从4个MM中选出4个以上的人来方案数当然为0喽)。那么它的生成函数g(x)就应该是g(x)=1+4x+6x^2+4x^3+x^4。这不就是……二项式展开吗?于是,g(x)=(1+x)^4。
你或许应该知道,(1+x)^k=C(k,0)x^0+C(k,1)x^1+…+C(k,k)x^k;但你或许不知道,即使k为负数和小数的时候, 也有类似的结论: (1+x)^k=C(k,0)x^0+C(k,1)x^1+…+C(k,k)x^k+C(k,k+1)x^(k+1)+C(k,k+2)x^(k+2)+…
广义的组合数C(k,i)就等于
k(k-1)(k-2)(k-i+1)/i!,比如C(4,6)=4*3*2*1*0*(-1)/6!=0,再比如C(-1.4,2)=(-1.4)*(-2.4)/2!=1.68,当k为整数时,所有i>k时的C(k,i)中分子都要“越过”0这一项,因此后面C(k,k+1),C(k,k+2)之类的都为0了,与我们的经典二项式定理结论相同;不同的是,牛顿二项式定理中的指数k可以是任意实数.
再举一个例子说明一些更复杂的生成函数。n=x1+x2+x3+…+xk有多少个非负整数解?这道题是学排列与组合的经典例题了。把每组解的每个数都 加1,就变成n+k=x1+x2+x3+…+xk的正整数解的个数了。教材上或许会出现这么一个难听的名字叫“隔板法”:把n+k个东西排成一排,在 n+k-1个空格中插入k-1个“隔板”。答案我们总是知道的,就是C(n+k-1,k-1)。它就等于C(n+k-1,n)。它关于n的生成函数是 g(x)=1/(1-x)^k。这个生成函数是怎么来的呢?其实,它就是(1-x)的-k次方,.。事实上,我们有一个纯组合数学的 更简单的解释方法。因为我们刚才的几何级数1+x+x^2+x^3+x^4+…=1/(1-x),那么 (1+x+x^2+x^3+x^4+…)^k就等于1/(1-x)^k。仔细想想k个(1+x+x^2+x^3+x^4+…)相乘是什么意思。 (1+x+x^2+x^3+x^4+…)^k的展开式中,n次项的系数就是我们的答案,因为它的这个系数是由原式完全展开后k个指数加起来恰好等于n的项合并起来得到的。
现在我们引用《组合数学》上暴经典的一个例题。很多书上都会有这类题。
我们要从苹果、香蕉、橘子和梨中拿一些水果出来,要求苹果只能拿偶数个,香蕉的个数要是5的倍数,橘子最多拿4个,梨要么不拿,要么只能拿一个。问按这样的要求拿n个水果的方案数。
结合刚才的k个(1+x+x^2+x^3+x^4+…)相乘,我们也可以算出这个问题的生成函数。
引用内容
g(x)=(1+x^2+x^4+…)(1+x^5+x^10+..)(1+x+x^2+x^3+x^4)(1+x)
半都约掉了)
=(1-x)^(-2)=C(1,0)+C(2,1)x+C(3,2)x^2+C(4,3)x^3… (参见刚才对1/(1-x)^k的展开)
=1+2x+3x^2+4x^3+5x^4+….
于是,拿n个水果有n+1种方法。我们利用生成函数,完全使用代数手段得到了答案!
1/(1-x)=1+x+x^2+x^3+x^4+…是前面说过的。我们对这个式子等号两边同时求导数。于是,1/(1-x)^2=1+2x+3x^2+4x^3+5x^4+….。;一步就得到了我们所需要的东西!不断地再求导数,我们同样可以得到刚才用复杂的牛顿二项式定理得到的那个结论。生成函数还有很多其它的处理手段,比如等式两边同时乘以、除以常数(相当于等式右边每一项乘以、除以常数),等式两边同时乘以、除以一个x(相当于等式右边的系数“移一位”),以及求微分积分等。
我们用两种方法得到了这样一个公式:1/(1-x)^n=1+C(n,1)x^1+C(n+1,2)x^2+C(n+2,3)x^3+…+C(n+k-1,k)x^k+…。这个公式非常有用,是把一个生成函数还原为数列的武器。而且还是核武器。
母函数可分为很多种,包括普通母函数、指数母函数、L级数、贝尔级数和狄利克雷级数。对每个序列都可以写出以上每个类型的一个母函数。构造母函数的目的一般是为了解决某个特定的问题,因此选用何种母函数视乎序列本身的特性和问题的类型。
这里先给出两句话,不懂的可以等看完这篇文章再回过头来看:“把组合问题的加法法则和幂级数的t的乘幂的相加对应起来”
“母函数的思想很简单—就是把离散数列和幂级数一一对应起来,把离散数列间的相互结合关系对应成为幂级数间的运算关系,最后由幂级数形式来确定离散数列的构造. “
我们首先来看下这个多项式乘法:
1.x的系数是a1,a2,…an 的单个组合的全体。
2. x2的系数是a1,a2,…a2的两个组合的全体。
………
n. xn的系数是a1,a2,….an的n个组合的全体(只有1个)。
由此得到:如有图
母函数的定义:
对于序列a0,a1,a2,…构造一函数:
:母函数详解
图三
称函数G(x)是序列a0,a1,a2,…的母函数
G( x ) = a[0] + a[1] * x + a[2]* x^2 + ······
这里先给出2个例子,等会再结合题目分析:第一种:
有1克、2克、3克、4克的砝码各一 枚,能称出哪几种重量?每种重量各有几种可能方案?
考虑用母函数来解决这个问题:
我们假设x表示砝码,x的指数表示砝码的重量,这样:
1个1克的砝码可以用函数1+x表示,
1个2克的砝码可以用函数1+x∧2表示,
1个3克的砝码可以用函数1+x∧3表示,
1个4克的砝码可以用函数1+x∧4表示,
我们拿1+x来说,前面已经说过,x表示砝码,x的指数表示砝码的重量!即这里就是一个质量为2的砝码,那么前面的1表示什么?按照上面的理解,1其实应该写为:1*x^0,即1代表重量为2的砝码数量为0个。(理解!)
“把组合问题的加法法则和幂级数的t的乘幂的相加对应起来“
1+x表示了两种情况:1表示质量为2的砝码取0个的情况,x表示质量为2的砝码取1个的情况。这里说下各项系数的意义:
在x前面的系数a表示相应质量的砝码取a个,而1就表示相应砝码取0个,这里可不能简单的认为相应砝码取0个就该是0*x(想下为何?结合数学式子)。
所以,前面说的那句话的意义大家可以理解了吧?几种砝码的组合可以称重的情况,可以用以上几个函数的乘积表示:
(1+x)(1+x∧2)(1+x∧3)(1+x∧4)
= (1+x+x+x)(1+x+x+x)
=1+x+x+2x+2x+2x+2x+2x+x+x+x
从上面的函数知道:可称出从1克到10克,系数便是方案数。(!!!经典!!!)
例如右端有2x项,即称出5克的方案有2:5=3+2=4+1;同样,6=1+2+3=4+2;10=1+2+3+4。
故称出6克的方案有2,称出10克的方案有1 。
接着上面,接下来是第二种情况:求用1分、2分、3分的邮票贴出不同数值的方案数:
大家把这种情况和第一种比较有何区别?第一种每种是一个,而这里每种是无限的。
G( x ) = ( 1 + x + x^2 +····) * (1 + x^2 + x^4 +····)* (1 + x^3 + x^6 +·····)
母函数详解
以展开后的x为例,其系数为4,即4拆分成1、2、3之和的拆分数为4;
即 :4=1+1+1+1=1+1+2=1+3=2+2
这里再引出两个概念整数拆分和拆分数:
所谓整数拆分即把整数分解成若干整数的和(相当于把n个无区别的球放到n个无标志的盒子,盒子允许空,也允许放多于一个球)。
整数拆分成若干整数的和,办法不一,不同拆分法的总数叫做拆分数。
现在以上面的第二种情况每种种类个数无限为例,给出模板:
// Author: Tanky Woo
// 母函数详解
G( x ) = ( 1 + x + x^2 +····) * ( 1 + x^2 + x^4 + ····) * ( 1 + x^3 + x^6)
2.“母函数的思想很简单 — 就是把离散数列和幂级数一 一对应起来,把离散数列间的相互结合关系对应成为幂级数间的运算关系,最后由幂级数形式来确定离散数列的构造. “
就可以稍微理解了。
代码:
#include
#include
#define Max 3
#define Max1 9000
int num[Max];
int value[Max] = {1, 2, 5};
int c1[Max1], c2[Max1];
int main() {
int i, j, k, n;
while(scanf("%d%d%d", &num[0], &num[1], &num[2]) != EOF) {
if(!num[0] && !num[1] && !num[2])
return 0;
memset(c1, 0, sizeof(c1));
memset(c2, 0, sizeof(c2));
n = num[0] * value[0] + num[1] * value[1] + num[2] * value[2];
for(i = 0; i <= value[0] * num[0]; i += value[0])
c1[i] = 1;
for(i = 1; i <= 2; i++) {
for(j = 0; j <= n; j++)
for(k = 0; k <= num[i] * value[i]; k += value[i]) {
c2[k+j] += c1[j];
}
for(k = 0; k <= n; k++) {
c1[k] = c2[k];
c2[k] = 0;
}
}
for(i = 0; i <= n + 1; i++)
if(c1[i] == 0) {
printf("%d\n", i);
break;
}
}
}