母函数

定义:在数学中,某个序列的母函数(Generating function,又称生成函数)是一种形式幂级数,其每一项的系数可以提供关于这个序列的信息。用母函数可以将序列的性质问题转化为“形式上的”初等代数运算。


母函数一般分为普通型与指数型。

如:给定数列a0,a1...an...,构造一个函数F(x)=a0*f0(x)+a1*f1(x)+...+an*fn(x)+...,那么F(x)为数列a0,a1,...an...的母函数,而f0(x),f1(x),...fn(x)...为标志函数,对于普通型其标志函数为x^n,指数型标志函数为x^n/n!。

普通型与指数型区别在于有序无序 ,即对应序列转化的结果要不要求是有序的,比如用三个分别是1克,2克,5克的砝码去合成一个大砝码,那么就是无序的。比如用3个“1”、2个“6”、1个“8”能组成多少个四位数,那么结果就是有序的。普通型最后是组成一个总重量或总个数等等的一个“值”,而指数型最后是组成一个排列,排列结果中不同类型个体可以区分(但是同一个类型个体之间是相同“类型”的,所以会有x^n/n!,n!就是是同一个类型内部个体的排列),而前者是不再区分类型。形式上说,普通型生成函数用于解决多重集的组合问题,而指数型母函数用于解决多重集的排列问题。所以指数型母函数的标志函数可以如下理解:对于x^j/j!,表示在一个方案中某个元素出现了j次,而在不同的位置中的j出现是相同的,所以在计算总排列数时,只应算作1次。有排列组合知识知最后结果要除j!。注意我们大多数用的是普通型,但其实两者区别不大。


下面是《信息学奥赛之数学一本通》中两者定理的描述:

普通型:设从n元集合S={a1,a2...an}中取k个元素的组合中,若限定限定元素ai出现的次数的集合为Mi(1<=i<=n),则该组合数序列的母函数为: ∏i从1到n(∑ m∈Mi (x^m))
指数型:设从多重集合M={∞*a1,∞*a2...∞*an}中取k个元素的排列中,若限定限定元素ai出现的次数的集合为Mi(1<=i<=n),则该组合数序列的母函数为: ∏i从1到n(∑ m∈Mi (x^m/m!))


多重集有如下定义:多重集或多重集合是数学中的一个概念,是集合概念的推广。在一个集合中,相同的元素只能出现一次,因此只能显示出有或无的属性。在多重集之中,同一个元素可以出现多次。{1,2,3}是一个集合,{1,1,1,2,2,3}而不是一个集合,而是一个多重集。


先看个概念:整数拆分——整数n拆分成r个整数的方案数即是下列方程的非负整数解的个数,即C(n+k-1,k-1).

                                            x1+x2+x3+…+xk=n.

证明:先看如下问题,我们n个相同的蓝色球放入标号非别为1,2,3…k的k个盒子里,有多少种方案数?可以将k个盒子排成一排,再在盒子前面将蓝色球排成一排,然后在两个相邻的盒子中间插入一个红色的球(共插入k-1个红球),这样就把n+k-1个球排成了一排,那么红球就将蓝球分为了k组,其意义就是对应两个红球之间(以及最两端)的蓝球放入同一个盒子,其实这就是一种方案。显然,总方案数即C(n+k-1,k-1).


 

具体序列向母函数转化常见如下类型的转化:

有重量为g1克、g2克、g3克的砝码各a1,a2,a3枚,能称出哪几种重量?每种重量各有几种可能方案?

考虑用母函数来解决这个问题:

我们假设x表示砝码,x的指数表示砝码的重量,这样:

第一种砝码可得多项式(1+x^(g1*1)+ x^(g1*2)+…+ x^(g1*a1)),其中1代表取0个g1克的砝码(这是1在后面母函数展开之后的作用来体现的) ,x^(g1*i)代表取i个g1克的砝码(形成一个重量为g1*i克的砝码)

第二种砝码可得多项式(1+x^(g2*1)+ x^(g2*2)+…+ x^(g2*a2))

第三种砝码可得多项式(1+x^(g3*1)+ x^(g3*2)+…+ x^(g3*a3))

那么可得母函数:G(x)= (1+x^(g1*1)+ x^(g1*2)+…+ x^(g1*a1))*(1+x^(g2*1)+ x^(g2*2)+…+ x^(g2*a2))*(1+x^(g3*1)+ x^(g3*2)+…+ x^(g3*a3)),(正好对应加法法则和乘法法则:同一砝码不同取法方案数相加,不同砝码取法方案数相乘)。

对其展开会得到:G(x)=b0+b1*x^1+b2*x^2+…+bn*x^n

对于这里的一项bi*x^i,它代表的意义如下:i即为合成后总重量为i的砝码,对应系数bi就是由g1g2g3三种砝码合成重量为i的砝码的方案数。若bi=0说明不能合成总重量为i的砝码。

那么其实母函数解决这个问题最终也是通过类似枚举的方法实现。体现在代码上就是通过一层一层展开每一个多项式,最后得到G(x)=b0+b1*x^1+b2*x^2+…+bn*x^n然后通过查询某个x^i的bi来得到结果。


有题目:

题目意思是有1分,2分,5分的硬币各a,b,c枚,求算不能组成的总钱数的最小值。

http://acm.hdu.edu.cn/showproblem.php?pid=1085

//母函数为g(x)=(1+x^1+x^2+...+x^num[0])*(1+x^2+x^4+...+x^(2*num[1]))*(1+x^5+x^10+...+x^(5*num[2]))
#include<iostream>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<queue>
#include<cstring>
#include<map>
#include<vector>
using namespace std;
typedef long long ll;
#define M 8005
 
int num[3],cent[3]={1,2,5}; //num存放硬币个数,cent是硬币面值即多项式中幂的步长
int c[M],tempc[M];          //c[i]即是结果中幂为i的项的系数,也是我们可以组成面值i的方案数,tempc是c与当前多项式相乘的结果
 
int main()
{
   int i,j,k;
 
   while(scanf("%d%d%d",&num[0],&num[1],&num[2])!=EOF)
    {
       if(num[0]==0&&num[1]==0&&num[2]==0)
           break;
 
       int maxv=num[0]*cent[0]+num[1]*cent[1]+num[2]*cent[2]; //母函数的最高幂次数
 
       memset(c,0,sizeof(c));
       memset(tempc,0,sizeof(tempc));
 
       for(i=0;i<=num[0]*cent[0];i+=cent[0]) //第一个多项式形成的系数(第一步)
       {
           c[i]=1;
       }
 
       for(i=1;i<3;i++) //总共3个多项式,要再乘其余2个(第二三步)
       {
           for(j=0;j<=maxv;j++)  //枚举累计相乘的每一项x^j去乘枚举的x^k
           {
               for(k=0;k+j<=maxv&&k<=num[i]*cent[i];k+=cent[i]) //k用来枚举第i个多项式的每一项的幂
                {
                    tempc[k+j]+=c[j];   //项相乘等于幂相加
                }
            }
 
           for(j=0;j<=maxv;j++)  //更新c数组
           {
                c[j]=tempc[j];
                tempc[j]=0;
           }
       }
 
       for(i=0;i<=maxv;i++)
       {
           if(c[i]==0)
                break;
       }
       printf("%d\n",i);
    }
 
   return 0;
}
 
//比如有(1+x)(1+x^2)(1+x^3),第一步,i=0指向第一个表达式(1+x);第二步,i=1指向表达式(1+x^2),j指向的是1和x的系数,k指向的是1和x^2的系数,执行完之后变为
 
//(1+x+x^2+x^3)(1+x^3),第三步,i=2指向表达式(1+x^3),j指向的是1、x、x^2、x^3的系数,k指向的是1和x^3的系数,执行完之后变为(1+x+x^2+2*x^3+x^4+x^5+x^6)


对于另一种常见类型就是这里的a1,a2,a3为无穷大:


求用1分、2分、3分的硬币组成不同数值的方案数,每种硬币有无数个:

大家把这种情况和第一种比较有何区别?第一种每种是一个,而这里每种是无限的。

我们不难得到这个问题的生成函数:g(x)=(1+x+x^2+x^3+...)(1+x^2+x^4+...)(1+x^5+x^10+..)

以下代码参考博客:http://blog.csdn.net/vsooda/article/details/7975485

#include <iostream>
using namespace std;
// Author: Tanky Woo
// www.wutianqi.com
const int _max = 10001; 
// c1是保存各项质量砝码可以组合的数目
// c2是中间量,保存每一次的情况
int c1[_max], c2[_max];   
int main()
{	//int n,i,j,k;
	int nNum;   // 
	int i, j, k;

	while(cin >> nNum)
	{
		for(i=0; i<=nNum; ++i)   // ---- ①
		{
			c1[i] = 1;
			c2[i] = 0;
		}
		for(i=2; i<=nNum; ++i)   // ----- ②
		{

			for(j=0; j<=nNum; ++j)   // ----- ③
				for(k=0; k+j<=nNum; k+=i)  // ---- ④
				{
					c2[j+k] += c1[j];
				}
				for(j=0; j<=nNum; ++j)     // ---- ⑤
				{
					c1[j] = c2[j];
					c2[j] = 0;
				}
		}
		cout << c1[nNum] << endl;
	}
	return 0;
}

我们来解释下上面标志的各个地方:(***********!!!重点!!!***********)

①  、首先对c1初始化,由第一个表达式(1+x+x^2+..x^n)初始化,把质量从0到n的所有砝码都初始化为1.

②  、 i从2到n遍历,这里i就是指第i个表达式,上面给出的第二种母函数关系式里,每一个括号括起来的就是一个表达式。

③、j 从0到n遍历,这里j就是(前面i個表达式累乘的表达式)里第j个变量,如(1+x)(1+x^2)(1+x^3),j先指示的是1和x的系数,i=2执行完之后变为(1+x+x^2+x^3)(1+x^3),这时候j应该指示的是合并后的第一个括号的四个变量的系数。

④ 、 k表示的是第j个指数,所以k每次增i(因为第i个表达式的增量是i)。

⑤  、把c2的值赋给c1,而把c2初始化为0,因为c2每次是从一个表达式中开始的。



综上,母函数即生成函数,解决某种计数模型,每一个序列都可以对应为母函数,构造母函数的目的一般是为了解决某个特定的问题,对于每个序列对应成母函数那么这个母函数展开后的每一项的系数、指数就是有了实际意义。所以我们再将序列转化为母函数后,问题就转化为求母函数中的每一项前面的系数,或系数an的通项。用代码去实现母函数的计算过程很简单,它是模拟我们人工计算多项式乘积的过程,比如有多项式H1*H2*H3......我们先计算H1和H2的乘积,得到结果H',再用H'和H3相乘......依次类推下去,直到得到最终的结果。


补充:

指数型母函数:指数型母函数 简介

指数型母函数求解时,因为标志函数x^m/m!,这里的m!也是要参与运算的,但是注意在最终结果中我们所要的系数是(x^m/m!)的系数,体现在代码上就是在运算过程中我们可以直接求x^m的系数,最终输出时把结果除m!就行。

题目:HDU 1521 排列组合

#include<bits/stdc++.h>
using namespace std;
#define M 15

int n,m,num[M];
double a[M],c[M],tempc[M];

void init()
{
    int i;
    a[0]=a[1]=1;
    for(i=2;i<=10;i++)
        a[i]=a[i-1]*i;
}

int main()
{
    init();
    int i,j,k;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(i=1;i<=n;i++)
            scanf("%d",&num[i]);
        for(i=0;i<=10;i++)
            c[i]=tempc[i]=0.0;
        for(i=0;i<=num[1];i++)
        {
            c[i]=1.0/a[i];
        }

        for(i=2;i<=n;i++)
        {
            for(j=0;j<=m;j++)
            {
                for(k=0;k<=num[i]&&j+k<=m;k++)
                    tempc[j+k]+=c[j]/a[k];  //1.0/a[k]是k指向的多项式的系数,c[j]/a[k]是x^j*x^k后新形成的x^(k+j)的系数
            }
            for(j=0;j<=m;j++)
            {
                c[j]=tempc[j];
                tempc[j]=0;
            }
        }
        printf("%.01f\n",c[m]*a[m]);     //c[m]是x^m的系数,要想将其等价转化为分母等于a[m],那么分子就是c[m]*a[m]
    }
    return 0;
}



参考:

《信息学奥赛之数学一本通》

母函数与排列组合

母函数 入门 + 模板

Tanky Woo

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
母函数和矩母函数是两个重要的数学工具,它们有着不同的定义和应用。 1. 母函数 母函数是一种数列表示方法,它是一个形式幂级数。设 $(a_n)$ 是一个数列,其普通生成函数为: $$F(x)=\sum_{n=0}^{\infty}a_nx^n$$ 这个形式幂级数可以看作是数列 $(a_n)$ 的一种“代表”,它包含了该数列的所有信息。通过母函数,我们可以方便地进行数列的运算、计算和推导,例如求解递推关系式、计算组合数等问题。 2. 矩母函数母函数是一种随机变量的生成函数,它与随机变量的各阶矩有关。设 $X$ 是一个随机变量,其矩母函数定义为: $$M_X(t)=\mathrm{E}(e^{tX})=\int_{-\infty}^{\infty}e^{tx}f_X(x)dx$$ 其中,$\mathrm{E}$ 表示期望,$f_X(x)$ 是 $X$ 的概率密度函数。矩母函数的第 $n$ 阶矩可以通过对 $M_X(t)$ 进行 $n$ 阶导数得到: $$\mathrm{E}(X^n)=M_X^{(n)}(0)$$ 矩母函数在概率论和统计学中有着广泛的应用,例如求解分布的矩、计算随机变量的卷积等问题。 3. 区别 母函数和矩母函数都是形式幂级数的一种表示方法,但它们的定义和应用有很大的不同。母函数通常用于表示数列,它可以用于求解递推关系式、计算组合数等问题;而矩母函数则用于表示随机变量的矩,它可以用于求解分布的矩、计算随机变量的卷积等问题。 此外,母函数和矩母函数的形式也有所不同。母函数是普通的无穷级数,而矩母函数是积分形式的无穷级数。因此,在使用上两者也有一些差异。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值