zoukankan      html  css  js  c++  java
  • 生成函数/母函数入门学习

     开始学生成函数了,开篇博客记录一下学习过程,现在还只是学了一些很浅显的内容,还会不断更新。

    首先推荐两篇写得很好的博客:https://blog.csdn.net/consciousman/article/details/77935700

    https://blog.csdn.net/wu_tongtong/article/details/78854572#comments

     

    生成函数或者叫母函数,是解决组合数学尤其是计数问题的一大利器。生成函数有许多种,最为常见也是应用最广泛的两种是普通母函数和指数型母函数。形式上说,普通型生成函数用于解决多重集的组合问题,而指数型母函数用于解决多重集的排列问题。母函数还可以解决递归数列的通项问题(例如使用母函数解决斐波那契数列的通项公式)。(来自百度百科)

    普通母函数是用来解决多重集的组合问题的。它的原理是:“母函数的思想很简单—就是把离散数列幂级数一一对应起来,把离散数列间的相互结合关系对应成为幂级数间的运算关系,最后由幂级数形式来确定离散数列的构造. “(来自百度百科)。这句话初听起来很蒙,但是一旦理解了母函数,就能明白这句话就是母函数的本质。

    这里给出比较通俗的解释(也许就有失本意了):假如有n个集合,这n个集合元素个数分别是k1,k2,k3...kn,一个集合的元素是相同的。那么要从这n个集合中的元素中取m个组成一个组合(不计顺序)。那么我们构造生成函数A1=(x^0+x^1+x^2+...x^k1) , A2=(x^0+x^1+...x^k2)... An=(x^0+x^1+...x^kn)  然后令 B=(A1)*(A2)*...*(An)。得到的B应该是这样子的,B=a0*x^0+a1*x^1+...an*x^n+...∞

    于是B的每一项都是一种组合计算的结果,每一项的幂代表需要组合的元素个数m(即从n个集合中取m个元素),每一项的系数就是选出m个元素的组合方案数。例如第B的第i项为 ai*x^i 代表从n个集合中的i组合数为ai。

    生成函数巧妙地运用了多项式乘法的性质,两项相乘:幂数相加,系数相乘。幂代表着选择个数,系数代表着方案数。于是所以n个多项式相乘后的结果就是最终的组合结果。

    母函数的时间限制主要是多项式乘法,可以暴力n^2一项一项乘,也可以用nlogn的FFT计算。

    暴力算的模板,以HDU1028的整数划分问题为例:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int N=125;
    int n,g[2][N];  //滚动数组保存前i个多项式相乘结果,g[i]代表:指数为i的项的系数为g[i] 
    
    int main()
    {
        while (scanf("%d",&n)==1) {
            for (int i=0;i<=n;i++) g[1][i]=1,g[0][i]=0;  //初始化 
            for (int i=2;i<=n;i++) {  //计算前i个砝码的结果 
                for (int j=0;j<=n;j+=i)  //j控制第i个砝码的多项式:(1+x^i+x^2i+...) 
                    for (int k=0;j+k<=n;k++)  //k控制前面(i-1)项的结果相乘 
                        g[i%2][j+k]+=g[(i%2)^1][k];    
                for (int j=0;j<=n;j++) g[(i%2)^1][j]=0;            
            }
            printf("%d
    ",g[n%2][n]);
        }
        return 0;
    }

    指数型母函数是用来解决多重集的排列问题的。只要理解了普通母函数就很容易理解指数型母函数,指数型母函数只是在初始式子上有些不同。

    我们知道多重集的排列公式为:n! / (n1! * n2! * n3! ... nk!) 。普通母函数已经算出了多重集的组合数,在此基础上乘上多重集的排列公式即可解决多重集的排列问题。

    那么指数型生成函数为:A1=(x^0/0!+x^1/1!+x^2/2!+...x^k1/k1!) A2=A1=(x^0/0!+x^1/1!+x^2/2!+...x^k2/k2!) .....可以发现指数型母函数只是在每一项除多了个 个数的阶乘。这样计算出来后把结果再乘上其总排列总个数的阶乘,这样便得到了上诉的多重集排列公式。

    由于存在出除法,暴力求指数型母函数需要用double类型来存储。

    暴力n^2计算指数型母函数,以HDU-1521为例:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=12;
    int n,m,a[N];
    double f[N],g[2][N];
    
    int main()
    {
        while (cin>>n>>m) {
            memset(g,0,sizeof(g));
            for (int i=1;i<=n;i++) scanf("%d",&a[i]);
            f[0]=1; for (int i=1;i<=m;i++) f[i]=f[i-1]*i;
            
            for (int j=0;j<=a[1];j++) g[1][j]=1.0/f[j];
            for (int i=2;i<=n;i++) {  //前i个集合 
                for (int j=0;j<=a[i];j++)  //j为第i个集合的幂 
                    for (int k=0;j+k<=m;k++)  //k为前i-1个集合的幂 
                        g[i%2][j+k]+=g[(i%2)^1][k]/f[j];
                for (int j=0;j<=m;j++) g[(i%2)^1][j]=0;        
            }
            printf("%.0lf
    ",f[m]*g[n%2][m]);
        }
        return 0;
    }

    母函数的化简:以上其实只是最最最基础的内容。学会了以上内容其实也不能做很多的题,因为常常会有的题目的N十分巨大,即使你看出了朴素的母函数,但是也不能在规定时间内求出来(即使用FFT),或者这个母函数根本就没办法算,这时候就需要化简母函数使他变得更简单。

    化简母函数需要积累大量的常见化简公式,这一块还是看上面两个大佬的博客吧,大佬写得很详细。

    题目练习

  • 相关阅读:
    PHP fread() 函数
    PHP fputs() 函数
    PHP fputcsv() 函数
    PHP fpassthru() 函数
    分区表基本类型
    C# const 和 readonly 有什么区别
    win2d 画出好看的图形
    win2d 画出好看的图形
    win2d 渐变颜色
    win2d 渐变颜色
  • 原文地址:https://www.cnblogs.com/clno1/p/10813884.html
Copyright © 2011-2022 走看看