算法:母函数在算法中的运用

来源:互联网 发布:新浪个性域名修改 编辑:程序博客网 时间:2024/05/21 09:33

最近在刷杭电OJ的时候,接触到了母函数理论,发现该理论应用比较多,而且对于新手比较难以理解,故决定写一篇通俗易懂的文章,本文章是在理解别人文章的基础上,再加上自己的理解,所以难免有很多地方重复,在结尾我会给出引用文章。

在数学中,某个序列的母函数(Generating function,又称生成函数)是一种形式幂级数,其每一项的系数可以提供关于这个序列的信息。使用母函数解决问题的方法称为母函数方法

母函数可分为很多种,包括普通母函数指数母函数L级数贝尔级数狄利克雷级数。对每个序列都可以写出以上每个类型的一个母函数。构造母函数的目的一般是为了解决某个特定的问题,因此选用何种母函数视乎序列本身的特性和问题的类型。


这里先给出两句话,不懂的可以等看完这篇文章再回过头来看:

1.把组合问题的加法法则和幂级数的乘幂对应起来。

2.母函数的思想很简单 — 就是把离散数列和幂级数一 一对应起来,把离散数列间的相互结合关系对应成为幂级数间的运算关系,最后由幂级数形式来确定离散数列的构造。

母函数的定义:

对于序列a0,a1,a2,…构造一函数:

称函数G(x)是序列a0,a1,a2,…的母函数。

接下来给出三个例子,看完你就明白母函数理论是怎样将组合问题的加法法则和幂级数的乘幂对应起来:

问题一:有1克、2克、3克、4克的砝码各一枚,能称出哪几种重量?每种重量各有几种可能方案?

分析:对于每种砝码,我们有两种选择,取或者不取,一共有15种情况(四个砝码都不取的情况除外);

接下来,我们将其转化为数学多项式的表示形式:

1个1克的砝码可以用函数1+1*x^1表示,

1个2克的砝码可以用函数1+1*x^2表示,

1个3克的砝码可以用函数1+1*x^3表示,

1个4克的砝码可以用函数1+1*x^4表示,

说明:在转换中,用x代表一个抽象砝码对象,其指数表示该砝码的重量,例如x^2表示一个质量为2的砝码;前面的1又代表什么呢?1=1*x^0,一个质量为0砝码,即不取砝码,也就是说1+1*x^2中的两项分别代表质量为2的砝码的两种状态:取和不取;

通过上述转换,我们可以构造一个多项式:

G(x)=(1+x)(1+x^2)(1+x^3)(1+x^4)

=1 + x + x^2 + 2*x^3 + 2*x^4 + 2*x^5 + 2*x^6 + 2*x^7 + x^8 + x^9 + x^10

由多项式乘积的性质可知:最终得出多项式的每一项的系数就是重量为其指数的方案的种树,,例如:6=1+2+3=2+4,此处不是很好理解,需要理解透彻。

问题二:求用1分、2分、3分的邮票贴出不同数值的方案数。

分析:此处没有规定邮票的张数,所以有无穷多种方案;

我们构造母函数如下:

G(x)=(1+x+x^2+x^3+...)(1+x^2+x^4+x^6+...)(1+x^3+x^6+...)

说明:该多项式中每个括号中内容对应一种邮票,而括号中每一项对应该邮票的一种选取状态,比如说1+x+x^2+x^3+...中的x^2表示选取1分邮票2张,x^2的指数2即为这两张邮票的面值之和。其实在一种选择方案中,我们可以分为三部分,即1分邮票选取多少张,2分邮票选取多少张,3分邮票选取多少张,分别与G(x)的三部分对应。同理,G(x)的展开式中每一项的系数都为方案数;

前两个问题相对来说易于理解,接下来引出第三个问题。

问题三:给定一个正整数,求出其整数拆分的拆分数。

这里再引出两个概念"整数拆分"和"拆分数":

所谓整数拆分即把整数分解成若干整数的和(相当于把n个无区别的球放到n个无标志的盒子,盒子允许空,也允许放多于一个球)。

整数拆分成若干整数的和,办法不一,不同拆分法的总数叫做拆分数

例如:4的整数拆分的拆分数为5,如下所示:

4

1+3

2+2

1+1+2

1+1+1+1

分析:在每一种整数拆分方案中,我们可以这样选取:取多少个1,取多少个2,取多少个3,取多少个4,后面我们可以看到,其每一部分都对应母函数中的一个乘积因子。

故构造母函数如下:

G(x)=(1+x+x^2+x^3+x^4)(1+x^2+x^4)(1+x^3)(1+x^4)

<p>最终展开式中x^4项的系数即为4的整数拆分的拆分数。</p><p>下面是一段模板代码,供参考:</p>
#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;   }  

--------------------------------------------------------------------------------------------------------------------------------------------------------------------

引用文章:http://blog.csdn.net/qq_33199236/article/details/51253524


0 0