关于生成函数

生成函数简介

一般生成函数

对于一个数列 \((f_i)_{i\ge 0}\)

其中 \(f_i\) 表示规模为 \(i\) 的特定组合对象的个数

定义 \((f_i)_i\) 的一般型生成函数为

\[F(x) = \sum_{i=0}^{\infty} f_i x^i. \]

我们把所有满足 \(F(x)\) 的要求的组合对象简记为 \(f\)-对象,共有 \(f_i\) 个规模为 \(i\)\(f\)-对象。

定义生成函数的加法运算:

\[F(x)+G(x) = \sum_{i\ge 0} (f_i+g_i) x^i \]

定义生成函数的乘法运算:

\[F(x)\cdot G(x) = \sum_{i\ge 0}\left( \sum_{j=0}^i f_j g_{i-j} \right) x^i. \]

生成序列:

推广一下,我们考虑 \(F(x)^k\),表示我们关心的组合对象是由 \(k\)\(f\)-对象构成的一个序列

新序列的规模是每个元素的规模之和。

在此情形下 \(F(x)^k\) 生成函数为:

\[\sum_{k=0}^{\infty} F(x)^k= \frac{1}{1-F(x)}. \]

可以看出,\(\frac{1}{1-F(x)}\) 就是 “由 0 个或多个 \(f\)-对象构成的有序序列” 的生成函数。

需要注意的是,这里我们假设 \(f_0 =0\),也即没有规模为 \(0\)\(f\)-对象(否则,生成的序列里可以有任意多个规模为 0 的 \(f\)-对象,上式不良定义)。

生成集合:

在有的场合里我们也关心 “由若干个 \(f\)-对象构成的多重集” 的生成函数,这里一个集合的规模定义为集合中元素规模之和。

考虑对于每个 \(f\)-对象,枚举它在最后的多重集里出现了多少次,有生成函数:

\[1 + x^i + x^{2i} + \dots = \frac{1}{1-x^i}. \]

因为总共有 \(f_i\) 个不同的规模为 \(i\)\(f\)-对象,要对它们依次做决策,所对应的生成函数即为

\[\left( \frac{1}{1-x^i} \right)^{f_i}. \]

最后,枚举 \(i\) 并求乘积,我们所求的生成函数即为

\[G(x) = \prod_{i=1}^{\infty} \left( \frac{1}{1-x^i} \right)^{f_i}. \]

我们使用 ln-then-exp 技巧做一些处理,得到一个更清晰的式子:

\[\begin{aligned} G(x) &=\exp\left(-\sum_{i=1}^{\infty} f_i \ln (1-x^i) \right) \\ &=\exp \left(\sum_{i=1}^{\infty}f_i \cdot \sum_{j=1}^{\infty} \frac{x^{ij}}{j}\right) \\ &= \exp \left( \sum_{j=1}^{\infty}\frac{1}{j} \sum_{i=1}^{\infty} f_i \cdot x^{ji}\right) \\ &= \exp\left( \sum_{j=1}^{\infty} \frac{F(x^j)}{j}\right). \end{aligned} \]

相比之前错误想法得到的结果 \(\exp(F(x))\),这里的正确结果添加了形如 \(F(x^j)/j\) 的一系列修正项。

注. 另有一个基于 Polya 原理的推导方法,可以得出一样的结论。

注. 在网络上有些资料中把 \(F\to \exp\left( \sum_{j=1}^{\infty} F(x^j)/j\right)\) 这一操作称为 Euler 变换,我尚未找到此命名的原始出处。

例. 计算有 \(N\) 个节点的无标号有根树个数,这里 \(N\le 2\times 10^5\)

解法. 设所求数列的生成函数为 \(F(x)\)。考虑生成一个有根树的过程:

  • 首先固定一个根节点。
  • 根节点连出若干个子树,子树之间不区分顺序(相当于多重集)。

由此容易看出,\(F\) 满足递归式

\[F(x) = x\cdot \exp\left( \sum_{j=1}^{\infty} \frac{F(x^j)}{j} \right). \]

可以使用分治FFT(在网络上有人称为“半在线卷积”)解决上述问题。计算量为 \(O(N\log^2 N)\)

也可以参考这个题目.

指数生成函数

指数生成函数常用于处理统计 \(n\) 个不同标号的元素可以产生的某种结构的数量的计数问题

例如 \(n\) 个点的带标号无向连通图个数、\(n\) 个元素构成的某种排列个数等

定义 \((f_i)_i\) 的指数型生成函数为:

\[F(x) = \sum_{i=0}^{\infty} f_i \frac{x^i}{i!}. \]

我们把所有满足 \(F(x)\) 的要求的结构记为 \(f\)-结构,在 \(i\) 个不同的元素之间,可以建立 \(f_i\) 种不同的 \(f\)-结构。

定义其加法运算:

\[F(x)+G(x) = \sum_{i\ge 0} (f_i+g_i) \frac{x^i}{i!} \]

定义其乘法运算:

\[F(x)\cdot G(x) = \sum_{i\ge 0}\left( \sum_{j=0}^i \binom{n}{j} f_j g_{i-j} \right) \frac{x^i}{i!}.\]

这两种运算同样有着非常直观的组合解释:

\(F+G\) 表示我们可以在 \(i\) 个元素之间建立 \(f\)-结构或者 \(g\)-结构的总方案数

\(F\cdot G\) 则是计算了把 \(n\) 个元素划分成两组,然后在两组中分别建立一个 \(f\)-结构和一个 \(g\)-结构的方案数

生成序列:

推广一下,我们考虑 \(F(x)^k\),它表示把 \(n\) 个元素划分成带标号(有序)的 \(k\) 组,并且在每组中建立一个 \(f\)-结构的方案数。

\(k\) 求和就得到 \(\frac{1}{1-F(x)}\),表示把 \(n\) 个元素划分成有序的若干组,并在每一组之中建立一个 \(f\)-结构。

生成集合:

有时我们只关心把元素划分成 \(k\) 组,但是这 \(k\) 组并不需要区分顺序。此时我们可以考虑 \(\frac{F(x)}{k!}\),它表示把 \(n\) 个元素划分成 \(k\) 组,并且在每组里建立一个 \(f\)-结构的方案数。

\(f_0 = 0\) (也即不存在建立在空集上的结构)的时候,\(\frac{F(x)}{k!}\) 的计数一定是不重不漏的(这是因为 \(n\) 个元素互相可以区分,无论它们被怎样划分成 \(k\) 组,同一个划分方案总是会在 \(F(x)^k\) 中计入 \(k!\) 遍)。我们对 \(k=0,1,\dots,\infty\) 求和,就得到

\[G(x) = \sum_{k=0}^{\infty} \frac{F(x)^k}{k!} = \exp(F(x)). \]

此时,\(G(x)\) 表示有 \(n\) 个不同的元素,把它们划分成若干组,并且在每组之中建立一个 \(f\)-结构的方案数。

例. 令 \(H(x)=\sum_n h_n \cdot x^n/n!\) 表示 \(n\) 个点的带标号无向图的指数生成函数,熟知 \(h_n = 2^{\binom{n}{2}}\)。令 \(F(x)=\sum_{n} f_n\cdot x^n/n!\) 表示 \(n\) 个点的带标号连通无向图的指数生成函数。因为每个无向图都可以唯一地划分为若干个连通块,我们得到 \(H(x) = \exp(F(x))\),也就是 \(F(x) = \ln H(x)\)。通过多项式求 ln,可以在 \(O(n\log n)\) 时间内计算出 \(F(x)\) 的前 \(n\) 项系数。

生成函数和递推

处理形如 \(P(x) / Q(x)\)\(\deg P < \deg Q\) 的形式幂级数的一种常用方法是递推。

我们令 \(A(x) = \frac{P(x)}{Q(x)}\),那么就有:

\[A(x)Q(x) = P(x) \]

\(Q(x) = 1 + \sum_{i=1}^{\deg Q}\limits q_i x^i\)\(P(x)= \sum_{i=0}^{\deg P}\limits p_i x^i\),考虑第 \(n\) 项公式的系数,当 \(n > \deg P\) 时候就有

\[a_n = - \sum_{i=1}^{\deg Q}q_i\cdot a_{n-i}. \]

注意到这是一个线性递推的形式。当 \(n \le \deg P\) 时候,有:

\[a_n = p_n - \sum_{i=1}^{n} q_i \cdot a_{n-i}. \]

不难发现通过选择合适的 \((p_i)_{0\le i\le \deg P}\),我们可以取到任意的初值 \((a_i:0< i < \deg Q)\).

\(A(x)Q(x) = P(x)\) 只能实现线性递推。如果我们有一个微分方程 \(A(x)Q_0(x)+ A'(x)Q_1(x) +\dots + A^{(t)}(x)Q_t(x) = P(x)\),那么我们就能构造一个包含指标 \(n\) 的递推(这种递推被称为整式递推)。卡特兰数的 \(O(1)\) 时间递推公式就是这个技巧的一个经典应用。

常见的几个生成函数形式

有必要熟练掌握以下形式幂级数的闭形式。

  1. \(\sum_{i=0}^{\infty}\limits x^i = \frac{1}{1-x}\).
  2. \(\sum_{i=0}^{\infty}\limits \binom{n+k-1}{k-1} x^i = \frac{1}{(1-x)^k}\).
  3. \(\sum_{i=1}^{\infty}\limits \frac{x^i}{i} = -\ln(1-x)\).
  4. \(\sum_{i=1}^{\infty}\limits \frac{x^i}{i!} = \exp(x)\).
  5. \(\sum_{i=0}^{\infty}\limits x^{2i} = \frac{1}{1-x^2}\), \(\sum_{i=0}^{\infty}\limits \frac{x^{2i}}{(2i)!} = \frac{e^x+e^{-x}}{2}\).
  6. (五边形数定理) \(\prod_{n=1}^{\infty}\limits (1-x^n) = \sum_{n=-\infty}^{\infty}\limits (-1)^n x^{\frac{n(3n+1)}{2}}\).

卡特兰数

基于递归式的定义\(h_0 = 1\), \(h_n = \sum_{i=0}^{n-1}\limits h_i h_{n-i-1}, \forall n\ge 1\).

通项公式的定义\(h_n = \binom{2n}{n}-\binom{2n}{n+1} = \frac{1}{n+1}\binom{2n}{n}\).

卡特兰数的生成函数\(zC(z)^2 + 1 = C(z), C(0) = 1\) 的解.

卡特兰数的几种组合意义

  • \(n+2\) 边形的不同的三角剖分方案数:固定一个多边形的一个边,考虑这个边所在的三角形的顶点,把问题划分成两个子问题。
  • \(n\) 个元素按照顺序入栈,可能的出栈顺序数:第一个进栈的元素是第 \(i\) 个出栈,划分成 \(i-1\)\(n-i\) 两个子问题。
  • \(n\)(\(n\)) 的合法括号序列个数:枚举和第一个左括号匹配的右括号的位置,划分成两个子问题。
  • \(n\) 个节点的无标号二叉树形态数:枚举左右子树的大小,划分成两个规模更小的子问题。
  • 构造一个由 +1 和 -1 构成的长度为 \(2n\) 的序列,满足序列和为 0,且任意的前缀和非负。
  • 一个 \(n\times n\) 的网格,从 \((0,0)\) 移动到 \((n,n)\),要求:第一步向右走且不穿越对角线的方案数。

组合推导

考虑第 5 个组合意义. 首先,任意构造一个和为 \(0\)\(\pm 1\) 序列的方案数为 \(\binom{2n}{n}\),但是这样会算进去一些不合法的方案。对于任意一种不合法的方案 \(P = (a_1,\dots, a_{2n})\),考虑第一个 \(i\) 使得 \(\sum_{j=1}^{i}a_j = -1\),把 \(a_1\dots a_i\) 翻转符号,它对应了一个和为 \(2\) 的序列。可以证明“总和为 0 但是不合法的方案” 和 “总和为 2 的方案” 之间存在一个双射,因此我们知道不合法的方案总数为 \(\binom{2n}{n-1}\)。因此所求方案数为 \(\binom{2n}{n} - \binom{2n}{n+1}\)

生成函数

\(C(z) = \sum_{i=0}^{\infty} h_i z^i\) 为卡特兰数的生成函数。那么根据递归式,我们可以写出 \(C(z) = z C(z)^2 + 1\) 以及 \(C(0) = h_0 = 1\)

\(C=zC^2+1\) 我们可以解出 \(C(z) = \frac{1 \pm \sqrt{1-4z}}{2z}\)。由于 \(C(0) = 1\),分子上的符号取负。根据广义二项式定理,我们做以下推导:

\[\begin{aligned} h_n &= -\frac{1}{2}\cdot [z^{n+1}]\sqrt{1-4z} \\ &= -\frac{1}{2} \binom{\frac{1}{2}}{n+1} (-4)^{n+1} \\ &= -\frac{1}{2} \frac{\left(\frac{1}{2}\right)\left(\frac{1}{2}-1\right)\cdot \left(\frac{1}{2}-n\right)}{(n+1)!}\cdot 4^{n+1} \cdot (-1)^{n+1} \\ &= (-1)^n\cdot \frac{1}{2}\cdot 4^{n+1}\frac{(-1)\cdot(-3) \cdot (-5) \dots (1-2n)}{2^{n+1} \cdot (n+1)!} \\ &= \frac{1}{2} \cdot2^{n+1} \frac{(2n-1)\cdot (2n-3)\cdots 3\cdot1}{(n+1)!} \\ &= \frac{(2n)\cdot (2n-2)\cdots 2\cdot 1}{n!}\frac{(2n-1)\cdot (2n-3)\cdots 3\cdot1}{(n+1)!} \\ &= \frac{1}{n+1}\binom{2n}{n}. \end{aligned} \]

其中第 5 行到第 6 行的推导中,我们用到了 \(2^n = \frac{2}{1}\frac{4}{2}\frac{6}{3}\cdots \frac{2n}{n}\).

:有些文献里也会采用双阶乘记号,也即 \((2n)!! = (2n)(2n-2)\cdots 2\) 以及 \((2n-1)!!= (2n-1)(2n-3)\cdots 3\cdot 1\).

也可以用一种称为微分法的方法得到一个卡特兰数的递推公式。我们记 \(1-2zC(z) = \sqrt{P(z)}\),其中 \(P(z)\) 是一个度数为 \(1\) 的多项式。那么对 \(z\) 求导,就得到

\[-2C(z) - 2z C'(z) = \frac{1}{2} \frac{P'(z)}{\sqrt{P(z)}} = \frac{1}{2} \frac{P'(z) \sqrt{P(z)}}{P(z)} = \frac{1}{2P(z)} P'(z)(1-2zC(z)). \]

整理一下,就得到

\[C(z)+zC'(z) - 2zC(z)-4z^2C'(z) = 1. \]

两边作用 \([z^n]\),得出

\[h_n = \frac{4n-2}{1+n}h_{n-1}. \]

不难验证这个递推公式的正确性。

posted @ 2021-12-19 19:03  一粒夸克  阅读(241)  评论(0编辑  收藏  举报