在看普通母函数之前,先了解一下什么是母函数,为此我们先看一个多项式:
(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