普通母函数

母函数(生成函数,Generating Function)

在看普通母函数之前,先了解一下什么是母函数,为此我们先看一个多项式:

(1+a1*x)(1+a2*x)…(1+an*x) = 1 + (a1 + a2 + …+an)x + (a1*a2 + a1*a3 + … + an-1*an)x^2 + …+ a1*a2*…*an*x^n

我们可以看出:

(1) x项的系数是从n个元素(a1,a2,…,an)中取一个元素组合的全体,有C(n,1)个。

(2)x^2项的系数是从n个元素(a1,a2,…,an)中取两个元素组合的全体,有C(n,2)个。

(n)x^n项的系数是n个元素(a1,a2,…,an)中取n个元素组合的全体,有C(n,n)个。

那么,如果将a1,a2,…,an均设为1,可以得到:(1+x)^n = 1 + C(n,1)*x + C(n,2)*x^2 + …+C(n,n)*x^n。

同时给出母函数的定义:对于序列a0,a1,a2,…,an构造一函数G(x) = a0 + a1*x + a2*x^2 + … + an*x^n,那么我们称函数G(x)是序列a0,a1,a2,…,an的母函数。

普通母函数

在这里,给出一个普通母函数的模型,并对此进行详解:

现有属性为1,2,3的物品各1个,可以组成多少种不同属性?每种组成属性的组成方案有几种?

假设用x表示物品,x的指数表示属性,可以得到:

1个属性为1的物品可以用表达式 1 + x 表示,1表示可以不用属性为1的物品,x表示可以用属性为1的物品。

1个属性为2的物品可以用表达式 1 + x^2表示,1表示可以不用属性为2的物品,x^2表示可以用属性为2的物品。

1个属性为3的物品可以用表达式 1 + x^3表示,1表示可以不用属性为3的物品,x^3表示可以用属性为3的物品。

于是我们可以构造母函数如下:

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

至此,属于该模型的母函数已经构造出来,其实际意义是x的指数表示组成属性的值,系数为某种属性的组成方案有几种。例如上式中的2*x^3其意义就是组成属性为3的方案有2种。

那么,如果说不同属性值的物品都有2个,我们便可以得到下述母函数:

G(x) = ( 1 + x + x^2 )(1 + x^2 + x^4 )(1 + x^3 + x^6 )。为了在下面能够对母函数有一个更好的叙述,现将每一个幂指数化为Howey所说的标准形式即 (x^i)^j。可化为如下形式:

G(x) = [ (x^1)^0 + (x^1)^1 + (x^1)^2  ]*[ (x^2)^0 + (x^2)^1 + (x^2)^2 ]*[ (x^3)^0 + (x^3)^1 + (x^3)^2 ],在此,我们将每一个中括号所包含的内容称为一个表达式,在每一个表达式中将 i 替换掉的值代表是第几个表达式(很凑巧的是与给出的模型当中所要代表的属性值一直,其实实质上也表达了采用了第几个属性值),将 j 替换掉的值代表使用某种属性值的数量。例如:

(x^2)^0其中2就是标准形式中的i,0就是标准形式中的j,其具体意义是可以使用0个第二个属性值(在该模型中属性值也为2)的物品,现在应该明白母函数中“1”的来历了吧。

(x^3)^2其中3就是标准形式中的i, 2就是标准形式中的j,其具体意义是可以使用2个属性值为2的物品。

如果真正理解上述内容,请用笔写出不同属性值物品有n个时候的标准形式(这对真正理解下面所要介绍的代码十分关键,同时也能帮助大家对代码的改写做到举一反三有所帮助!!!)。

在这里给出不同属性值物品有n个的代码:

 1 int x[MAX],c[MAX];
 2 //数组x的值代表组成某种属性的数目
 3 //c用来得到x的中间变量
 4 
 5 int GeneratingFunction(int n,int m,int value)
 6 {
 7     //n代表有多少种不同的属性
 8     //m代表每种属性的个数
 9     //value代表组成属性值为value
10 
11     memset(x,0,sizeof(x));
12     memset(c,0,sizeof(c));
13 
14     for(int j = 0 ; j <= m; j++)
15     {   //j代表表达式中的第j项
16         x[j] = 1;
17     }
18 
19     for(int i = 2 ; i <= n; i++)
20     {
21         //i代表第i个表达式
22         //(将要与第i个表达式之前的
23         //所有表达式累乘后的结果表
24         //达式相乘)
25         for(int j = 0 ; j <= value ; j++)
26         {
27             //j代表第i个表达式之前累乘
28             //得到的表达式中组合属性值
29             //为j的项
30             for(int k = 0; k <= m ; k++)
31             {   //k代表当前表达式中第k项
32                 if( (k * i + j) > value)
33                     break;
34                 c[k * i + j] += x[j];
35             }
36         }
37 
38         for(int t = 0; t <= value ; t++)
39         {
40             x[t] = c[t];
41             c[t] = 0;
42         }
43     }
44 
45     return x[value];
46 }

最后在这总结一下上述代码,其核心在于三层循环,第一层循环代表当前所要乘的表达式,第二层循环代表之前所有表达式乘积中各组成属性的值,第三层循环代表当前所要成的表达式的第k项。如果对该过程还不是很明白的话,不放将不同属性值各n个的这种情况写成标准形式,然后展开后看看(其实质就是模拟对母函数这个多项式的展开,只不过在此值限制在了组成属性值(即x的指数)到value而已)。

 

参考资料:

1.组合数学及应用(周治国 编)

2.TankyWoo的个人博客 http://www.wutianqi.com/?p=596

posted @ 2013-09-05 18:49  Howey  阅读(478)  评论(0)    收藏  举报