巷子口有家“数列小馆”,老板娘阿数有个习惯:每天打烊后,把每道菜的销量写在一张纸条上,按日期从第 0 天开始排成一排。日子久了,纸条越积越长,她想搬家时看着这一抽屉纸条犯了愁:怎么才能把这串数字“打包”成一张卡片带走呢? 她想出一个办法——给每张纸条贴上不同的“标签”:第 0 天的纸条贴 \(x^0\),第 1 天的贴 \(x^1\),第 2 天的贴 \(x^2\)……然后把这些纸条按顺序串成一串,就得到一张“标签账单”:\(a_0 + a_1x + a_2x^2 + a_3x^3 + \cdots\)。这串东西在数学上有个响亮的名字——生成函数。 神奇的是,这张账单不仅能“打包”数列,还能像真正的账本一样做加减乘除:两本账合并是对应项相加,两本账相乘能算出“买一份主菜加一份甜点的套餐”有多少种搭配,甚至能从每天的销量反推出第一百天的销量。计算机科学家还发现,让计算机处理这种“多项式账本”时,用快速傅里叶变换能把原本 \(n^2\) 次的乘法加速到 \(n\log n\) 次。这就是生成函数:一串数字的“压缩包”,也是组合计数的“万能工具箱”。

岗位关联

生成函数是组合数学的“翻译官”。掌握它,你就能把数列、递推、计数问题统一成多项式运算——这是算法设计、概率论和密码学中最常用的数学思维之一。

目录
  1. 可视化演示
  2. 生成函数定义:给数列做“标签账本”
  3. 普通生成函数:最朴素的记账法
  4. 指数生成函数:给排列“脱外套”的账本
  5. 运算性质:账本的合并、平移与微调
  6. 卷积定理:套餐组合的计数魔法
  7. 递推求解:从账本反推经营规律
  8. 分组与分配:把食材装进餐盒
  9. 算法应用:从手工账本到计算机流水线
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

生成函数将数列编码为形式幂级数,是组合计数的强大工具。本文详解普通生成函数与指数生成函数的定义、运算性质与卷积... 切换模式查看不同维度。

本节配有交互动效(进入视口后自动加载,约 3KB)。

普通生成函数:核心原理 动态展示关键流程与数据流动 交互控件调节参数实时渲染 f(x) = transform(x) 核心概念的可视化呈现

生成函数定义:给数列做“标签账本”

阿数贴标签的方法其实很有讲究。每张纸条上的数字就是“数列的一项”,而标签 \(x^n\) 上的指数 \(n\),就是它在队伍里的位置。位置不同,标签就不同——第 3 天的纸条绝不能贴在第 1 天的位置上。

这种“给每一项贴上位置标签”的编码方式,就是生成函数的核心思想。把数列 \((a_0, a_1, a_2, \dots)\) 变成一串多项式:

这里的 \(x\) 是个“占位符”,数学家称这种不要求 \(x\) 取任何具体数值的级数为形式幂级数。它像名片上的印章:只用来区分身份,不用来兑换现金。正因为 \(x\) 不取值,无穷级数也不需要讨论“收敛不收敛”——我们只关心每张纸条上的系数 \(a_n\)。

举个例子:数列 \(1, 2, 4, 8, \dots\)(每天销量翻倍)的生成函数就是 \(1 + 2x + 4x^2 + 8x^3 + \cdots\)。把这个式子写出来,整家店的经营记录就全部装进了一串多项式里。

阿数把一抽屉纸条变成一串项链。这串项链不只是收纳工具,它还保留了每张纸条的位置信息——这正是生成函数后续能做加减乘除的基础。

A(x)=∑n=0∞anxn=a0+a1x+a2x2+a3x3+⋯\displaystyle A(x)=\sum_{n=0}^{\infty} a_n x^n = a_0 + a_1 x + a_2 x^2 + a_3 x^3 + \cdots
生成函数把“一长串数字”压缩成“一个多项式”,系数就是原数列的每一项。它是编码,不是赋值。
  1. 系数 \(a_n\)这是账本上第 \(n\) 张纸条的数字,也就是数列的第 \(n\) 项。它决定了多项式里 \(x^n\) 前面的“重量”。
    an=[xn]A(x)\displaystyle a_n = [x^n] A(x)
  2. \(x^n\)第 \(n\) 个位置的标签。指数 \(n\) 如同一张座位号,决定了这一项排在哪一位。
    xn,n=0,1,2,…\displaystyle x^n,\quad n = 0, 1, 2, \dots
  3. 求和号 \(\sum\)把所有纸条按顺序首尾相接,串成一条完整的“标签项链”。
    ∑n=0∞anxn\displaystyle \sum_{n=0}^{\infty} a_n x^n
  4. 整串 \(A(x)\)整本账本的简写符号。以后阿数只要写 \(A(x)\),就代表那一整抽屉的纸条。

普通生成函数:最朴素的记账法

阿数最常用的记账法就是“原样记录”——第 \(n\) 天的销量直接写在 \(x^n\) 的标签下,不加任何修饰。这种最直接的编码方式,就是普通生成函数(OGF)。

A(x)=∑n=0∞anxn\displaystyle A(x) = \sum_{n=0}^{\infty} a_n x^n

它的威力体现在一个经典式子上:如果数列是 \(1, 1, 1, \dots\)(每天恰好卖 1 份招牌菜),那么它的 OGF 是:

∑n=0∞xn=11−x\displaystyle \sum_{n=0}^{\infty} x^n = \frac{1}{1-x}

左边是无穷项,右边只有短短一行——数学家把这种“把无限长账单折叠成一行”的表达式叫做封闭形式。有了封闭形式,我们就能借用代数工具研究数列。而要从折叠的账本中取出某一天的销量,只需用系数提取符号:

[xn]A(x)=an\displaystyle [x^n] A(x) = a_n

记号 \([x^n]\) 读作“提取 \(x^n\) 的系数”,好比从账本里精准抽出第 \(n\) 张纸条。

1. 左侧 \(\sum x^n\):无穷多个 1 依次排列,代表常数数列 \(1,1,1,\dots\)。

1+x+x2+x3+⋯\displaystyle 1 + x + x^2 + x^3 + \cdots

2. 等号(折叠):把无穷级数“卷”成一个简洁的分式,这是几何级数求和的本质。

∑n=0∞xn=1+x+x2+⋯=11−x\displaystyle \sum_{n=0}^{\infty} x^n = 1 + x + x^2 + \cdots = \frac{1}{1-x}

3. \(\frac{1}{1-x}\):封闭形式。账本从无限长变成一行,搬家时轻松带走。

11−x\displaystyle \frac{1}{1-x}

4. \([x^n]\) 符号:从封闭形式中反向展开,抽出第 \(n\) 项的系数。

[xn]11−x=1\displaystyle [x^n]\frac{1}{1-x} = 1

阿数发现,账本折叠后好拿多了,要用系数时再展开,每张纸条一个不少。这就是普通生成函数:朴素,却极其好用。

指数生成函数:给排列“脱外套”的账本

有些菜要按顺序上——前菜、主菜、甜点,顺序不同就算不同的套餐。阿数发现普通账本会把“顺序”也算进去,可她有时只想统计“有哪些菜”而不管先后。于是她发明了第二种账本:每张纸条上盖一个“除以 \(n!\)”的章。

A~(x)=∑n=0∞anxnn!\displaystyle \widetilde{A}(x) = \sum_{n=0}^{\infty} a_n \frac{x^n}{n!}

这就是指数生成函数(EGF)。其中 \(n!\) 是 \(n\) 个元素的排列数。做一个“由 \(n\) 个不同元素组成的排列”时,内部有 \(n!\) 种顺序,除以 \(n!\) 就把顺序“抹平”了。EGF 最著名的封闭形式是:

∑n=0∞xnn!=ex\displaystyle \sum_{n=0}^{\infty} \frac{x^n}{n!} = e^x

因为 \(e^x\) 的泰勒展开正是这个级数。这解释了“指数生成函数”的名字由来。再看一个巧妙的例子:\(n\) 个元素的全排列数是 \(n!\),它的 EGF 为:

∑n=0∞n!⋅xnn!=∑n=0∞xn=11−x\displaystyle \sum_{n=0}^{\infty} n! \cdot \frac{x^n}{n!} = \sum_{n=0}^{\infty} x^n = \frac{1}{1-x}

排列数 \(n!\) 被分母抵消后,露出了朴素的几何级数——这正是 EGF 的“脱外套”魔法。

1. 分子 \(a_n\):数列本身,比如 \(n\) 个元素的全排列数 \(n!\)。

an=n!\displaystyle a_n = n!

2. 分母 \(n!\):抹平内部顺序的“章”。盖了这个章,排列不再区分先后。

ann!\displaystyle \frac{a_n}{n!}

3. \(e^x\) 级数:全 1 数列的 EGF 恰好是指数函数,这是 EGF 得名的由来。

∑n=0∞xnn!=ex\displaystyle \sum_{n=0}^{\infty} \frac{x^n}{n!} = e^x

4. 抵消现象:当 \(a_n = n!\) 时,分子分母相约,露出普通几何级数。

n!⋅xnn!=xn\displaystyle n! \cdot \frac{x^n}{n!} = x^n

阿数把这本“盖章账本”挂在墙上,处理排列套餐时再也不被顺序干扰。她心里清楚:普通问题用 OGF,排列问题用 EGF。

运算性质:账本的合并、平移与微调

账本不只是存放的,还可以像实体账本一样操作。阿数常常把两本账并排相加,或者把一本账整体往后挪一天,再用放大镜观察每一天的变化率。这些操作在生成函数里都有对应:

A(x)±B(x)=∑n=0∞(an±bn)xn\displaystyle A(x) \pm B(x) = \sum_{n=0}^{\infty} (a_n \pm b_n) x^n

加法就是逐项相加——两本账每天并起来,简单明了。而乘法更有意思,它生成了一个新的数列(卷积),我们下一节专门讲。再看两个“微调”技巧。移位:把整个数列右移 \(m\) 位,相当于在账本前面补 \(m\) 张空白纸条:

xmA(x)=∑n=m∞an−mxn\displaystyle x^m A(x) = \sum_{n=m}^{\infty} a_{n-m} x^n

求导加权:对 \(A(x)\) 求导后乘回 \(x\),相当于把第 \(n\) 天的销量放大 \(n\) 倍:

xA′(x)=∑n=0∞n anxn\displaystyle x A'(x) = \sum_{n=0}^{\infty} n\, a_n x^n

为什么求导会变成“放大 \(n\) 倍”?因为对 \(x^n\) 求导得到 \(n x^{n-1}\),再乘回一个 \(x\),指数恢复成 \(n\),而系数多了一个因子 \(n\)。

1. 加法运算:对应数列逐项相加,是账本的“合并”。

an+bn=[xn](A(x)+B(x))\displaystyle a_n + b_n = [x^n]\big(A(x)+B(x)\big)

2. 乘法运算:对应卷积,新数列的每一项由旧项交叉相乘再求和。

[xn](A(x)B(x))=∑k=0nakbn−k\displaystyle [x^n]\big(A(x)B(x)\big) = \sum_{k=0}^{n} a_k b_{n-k}

3. 移位运算:乘 \(x^m\) 让整个数列右移,前面补零。

xmA(x)=∑n=m∞an−mxn\displaystyle x^m A(x) = \sum_{n=m}^{\infty} a_{n-m} x^n

4. 求导加权:\(x A'(x)\) 把第 \(n\) 项的系数放大 \(n\) 倍。

nan=[xn](xA′(x))\displaystyle n a_n = [x^n]\big(x A'(x)\big)

阿数像变魔术一样让账本平移、放大、合并。这些操作在解题时是她的“十八般兵器”。

卷积定理:套餐组合的计数魔法

这天阿数想让主菜师傅和甜点师傅联手推出“\(n\) 元套餐”:主菜花 \(k\) 元(有 \(a_k\) 种选择),甜点花 \(n-k\) 元(有 \(b_{n-k}\) 种选择),那么花恰好 \(n\) 元的套餐搭配数就是 \(a_k \cdot b_{n-k}\) 种。把所有可能的 \(k\) 累加,就得到卷积:

cn=∑k=0nakbn−k\displaystyle c_n = \sum_{k=0}^{n} a_k b_{n-k}

这正是两个生成函数相乘时,\(x^n\) 项的系数。换句话说:生成函数的乘法自动完成卷积。这是生成函数最神奇的地方——复杂的选择计数,变成了一次多项式乘法。

EGF 的卷积略有不同,会带上二项式系数:

cn=∑k=0n(nk)akbn−k\displaystyle c_n = \sum_{k=0}^{n} \binom{n}{k} a_k b_{n-k}

原因藏在指数运算里:\(\frac{x^k}{k!} \cdot \frac{x^{n-k}}{(n-k)!} = \binom{n}{k} \frac{x^n}{n!}\)。两个 EGF 相乘时,\(x^n/n!\) 的系数自动混入组合数 \(\binom{n}{k}\),这正好对应“先从 \(n\) 个位置里选 \(k\) 个放第一种结构”的计数逻辑。

经典例子:掷两枚骰子,求点数之和的分布。单枚骰子的分布是 \(d = [\frac16, \frac16, \dots, \frac16]\)(6 个面),两枚点数之和的分布就是 \(d * d\) 的卷积。和为 7 的概率是 \(\frac{6}{36} = \frac{1}{6}\)。

1. \(a_k b_{n-k}\):主菜花 \(k\) 元与甜点花 \(n-k\) 元的搭配数,两者相乘。

ak⋅bn−k\displaystyle a_k \cdot b_{n-k}

2. 求和 \(\sum_{k=0}^{n}\):把所有可能的 \(k\) 全部加起来,覆盖所有分钱方案。

∑k=0nakbn−k\displaystyle \sum_{k=0}^{n} a_k b_{n-k}

3. 二项式系数 \(\binom{n}{k}\):EGF 卷积里自带的“选位置”因子,来自两个阶乘分母的合并。

xkk!⋅xn−k(n−k)!=(nk)xnn!\displaystyle \frac{x^k}{k!} \cdot \frac{x^{n-k}}{(n-k)!} = \binom{n}{k}\frac{x^n}{n!}

4. 多项式乘法等价:\(A(x)B(x)\) 展开后,\(x^n\) 的系数就是 \(c_n\)。

[xn](A(x)B(x))=cn\displaystyle [x^n]\big(A(x)B(x)\big) = c_n

主厨和甜点师的合作,就是生成函数乘法背后的秘密。阿数从此再也不用手工枚举套餐,一次乘法全搞定。

递推求解:从账本反推经营规律

阿数发现店里最近的销量有个规律:今天的销量等于昨天加前天的销量(老顾客总带着新顾客来)。她想知道第 100 天能卖多少,总不能一天天推下去。生成函数可以直接“解”出通项公式。这个经典问题就是斐波那契数列:

Fn=Fn−1+Fn−2,F0=0, F1=1\displaystyle F_n = F_{n-1} + F_{n-2}, \quad F_0 = 0,\ F_1 = 1

设 \(F(x) = \sum F_n x^n\) 为斐波那契数列的生成函数。利用移位公式,把递推式翻译成方程:

F(x)=x+xF(x)+x2F(x)\displaystyle F(x) = x + xF(x) + x^2 F(x)

解得封闭形式:

F(x)=x1−x−x2\displaystyle F(x) = \frac{x}{1 - x - x^2}

接下来用部分分式把分母拆成两个一次因子。设 \(\varphi = \frac{1+\sqrt5}{2}\),\(\psi = \frac{1-\sqrt5}{2}\),则:

F(x)=15(11−φx−11−ψx)\displaystyle F(x) = \frac{1}{\sqrt{5}}\left(\frac{1}{1-\varphi x} - \frac{1}{1-\psi x}\right)

两边分别展开成几何级数,提取 \(x^n\) 系数,得到著名的比内公式:

Fn=15(φn−ψn)\displaystyle F_n = \frac{1}{\sqrt{5}}\left(\varphi^n - \psi^n\right)

有了这条通项公式,阿数直接代入 \(n=100\) 就能算出第一百天的销量,不用再一天天递推。

1. 设生成函数:把整条递推数列编码成一个未知函数 \(F(x)\)。

F(x)=∑n=0∞Fnxn\displaystyle F(x)=\sum_{n=0}^{\infty} F_n x^n

2. 代入递推:\(F_{n-1}\) 对应 \(xF(x)\),\(F_{n-2}\) 对应 \(x^2F(x)\),初值 \(F_1=1\) 产生单独的 \(x\)。

F(x)=x+xF(x)+x2F(x)\displaystyle F(x) = x + xF(x) + x^2F(x)

3. 解方程:把含 \(F(x)\) 的项移到一边,提取公因式。

F(x)(1−x−x2)=x\displaystyle F(x)(1-x-x^2) = x

4. 部分分式:把分式拆成两个简单分式之差,每个都是几何级数的封闭形式。

x1−x−x2=15(11−φx−11−ψx)\displaystyle \frac{x}{1-x-x^2} = \frac{1}{\sqrt{5}}\left(\frac{1}{1-\varphi x} - \frac{1}{1-\psi x}\right)

5. 提取通项:两个几何级数同时展开,\(x^n\) 的系数合并成比内公式。

Fn=φn−ψn5\displaystyle F_n = \frac{\varphi^n - \psi^n}{\sqrt{5}}

阿数再也不用一天天数销量了。她把递推问题变成代数方程,一条公式直接算到第一百天。

分组与分配:把食材装进餐盒

阿数要把 \(n\) 个相同的包子打包出售,每份随便装几个,问有多少种打包方式。这就是整数拆分:把 \(n\) 写成若干正整数之和,顺序无关。比如 \(4\) 有 5 种拆分:\(4,\ 3+1,\ 2+2,\ 2+1+1,\ 1+1+1+1\)。

整数拆分的生成函数是一个无穷连乘:

∑n=0∞p(n)xn=∏k=1∞11−xk\displaystyle \sum_{n=0}^{\infty} p(n) x^n = \prod_{k=1}^{\infty} \frac{1}{1-x^k}

理解这个式子的关键在于每个因子。因子 \(\frac{1}{1-x^k} = 1 + x^k + x^{2k} + \cdots\) 表示“选 0 份、1 份、2 份……重量为 \(k\) 的食材包”。把所有 \(k\) 的因子连乘,展开后 \(x^n\) 的系数就是总重量恰好为 \(n\) 的分装方案数 \(p(n)\)。

如果换成不同的物品(比如把 \(n\) 道不同的菜分给若干顾客),问题性质就变了——物品内部有顺序,这时要用 EGF。集合划分问题(把 \(n\) 个不同元素分成若干组)的 EGF 是著名的 Bell 数公式:

B(x)=eex−1\displaystyle B(x) = e^{e^x - 1}

展开后 \(x^n/n!\) 的系数就是第 \(n\) 个 Bell 数,表示把 \(n\) 个不同元素分到任意多非空集合的方案数。

1. 因子 \(\frac{1}{1-x^k}\):一种“单位重量 \(k\)”的食材包,选 \(j\) 个就贡献 \(x^{jk}\)。

11−xk=1+xk+x2k+⋯\displaystyle \frac{1}{1-x^k} = 1 + x^k + x^{2k} + \cdots

2. 连乘 \(\prod\):所有重量单位自由组合,最终覆盖所有拆分方案。

∏k=1∞11−xk\displaystyle \prod_{k=1}^{\infty} \frac{1}{1-x^k}

3. 系数 \(p(n)\):展开后 \(x^n\) 的系数,就是 \(n\) 的拆分方案数。

p(n)=[xn]∏k=1∞11−xk\displaystyle p(n) = [x^n]\prod_{k=1}^{\infty}\frac{1}{1-x^k}

4. EGF 分组:\(e^{e^x-1}\) 展开得到 Bell 数,适合处理“不同元素分到若干组”。

B(x)=eex−1=∑n=0∞Bnxnn!\displaystyle B(x) = e^{e^x-1} = \sum_{n=0}^{\infty} B_n \frac{x^n}{n!}

阿数把“打包”问题变成了“选因子”问题。每种食材包自由组合,展开一算,方案数清清楚楚。

算法应用:从手工账本到计算机流水线

店越开越大,手工翻账本太慢,阿数把账本交给了计算机。生成函数在计算机里的化身是多项式,而多项式乘法就是卷积。朴素的多项式乘法要两两相乘,复杂度是 \(O(n^2)\);但快速傅里叶变换(FFT)可以在 \(O(n\log n)\) 时间内完成卷积,这是信号处理、图像识别和 AI 推理的底层加速器。

另一个重要应用是概率母函数。设随机变量 \(X\) 取值非负整数,它的概率母函数定义为:

G(z)=∑k=0∞P(X=k) zk\displaystyle G(z) = \sum_{k=0}^{\infty} P(X=k)\, z^k

把每个概率值 \(P(X=k)\) 当作系数“挂”在 \(z^k\) 标签上。概率母函数最实用的性质是:对 \(z\) 求导后令 \(z=1\),得到期望:

G′(1)=E[X]\displaystyle G'(1) = \mathbb{E}[X]

同理,二阶导数还能给出方差。这在分析随机算法的平均复杂度时极其有用。此外,生成函数还能解决背包计数问题:把每个物品的重量 \(w\) 编码成因子 \((1 + x^w)\)(选或不选),所有因子连乘后,\(x^n\) 的系数就是凑出总重量 \(n\) 的方案数。

1. \(\sum P(X=k)z^k\):把每个概率值当作系数,随机变量的分布被编码成一个幂级数。

G(z)=∑k=0∞P(X=k) zk\displaystyle G(z)=\sum_{k=0}^{\infty}P(X=k)\,z^k

2. \(G'(1)\):对 \(z\) 求导后令 \(z=1\),正好是期望 \(\mathbb{E}[X]\)。

G′(1)=E[X]\displaystyle G'(1)=\mathbb{E}[X]

3. FFT 加速:快速傅里叶变换把卷积从 \(O(n^2)\) 降到 \(O(n\log n)\)。

FFT: O(n2)→O(nlog⁡n)\displaystyle \text{FFT: } O(n^2) \to O(n\log n)

有了计算机流水线,阿数再大的账本也能瞬间算完。从数学公式到高效代码,生成函数一直是那本万能的“标签账本”。

AI 应用场景

概率母函数与用户行为分布分析

阶段:数据分析与特征工程

解决的问题:推荐系统中“用户每天点击次数 \(X\)”的分布很难直接测量。传统方法用直方图统计,数据稀疏时噪声极大。概率母函数 \(G(z)=\mathbb{E}[z^X]\) 把分布编码成幂级数,求导即可得到期望、方差等矩信息,无需完整直方图就能刻画分布形状。

具体案例:某推荐团队要判断用户点击是否服从泊松分布,以决定负采样策略。他们用概率母函数的一阶导估计期望点击率,用二阶导判断过离散程度,发现数据呈现“长尾”,于是改用负二项分布建模,线上 CTR 提升了 3%。

卷积运算与神经网络加速

阶段:模型推理与算法优化

解决的问题:卷积神经网络里的卷积运算与生成函数的卷积同源。朴素实现是四重循环,速度极慢。利用 FFT 把时域卷积变成频域乘法,是语音识别、图像处理等实时推理系统的底层优化手段。

具体案例:某语音助手的唤醒词检测需要实时处理音频流。工程师把音频帧与滤波器组做 FFT 卷积,将 20ms 窗口的卷积耗时从 8ms 降到 1.2ms,让模型能在低功耗设备上流畅运行。

背包计数与优惠券组合优化

阶段:约束求解与资源分配

解决的问题:电商平台的优惠券凑单方案数量巨大,直接穷举会指数爆炸。生成函数把“选或不选某张券”编码成因子 \((1+x^{w})\),连乘后 \(x^n\) 的系数就是凑出金额 \(n\) 的方案数,把组合爆炸变成多项式运算。

具体案例:某电商要做“满 300 减 50”的凑单推荐,后端用生成函数在毫秒级算出所有凑单方案数,再结合用户偏好排序,高峰期的推荐接口 P99 延迟从 2 秒降到 120 毫秒。

Python 演示

python39 行
import sympy as sp x = sp.symbols('x')  def extract(expr, n_max):    """展开生成函数并提取前 n_max+1 个系数"""    s = sp.series(expr, x, 0, n_max + 1).removeO()    return [s.coeff(x, k) for k in range(n_max + 1)]  def main():    # 1) 斐波那契 生成函数 vs 递推    F = x / (1 - x - x**2)    gf = extract(F, 10)    fib = [0, 1]    for _ in range(9):        fib.append(fib[-1] + fib[-2])    print('生成函数系数:', gf)    print('递推前10项 :', fib[:10])    assert gf == fib[:10], '两种方法结果不一致'     # 2) 整数拆分 p(n) = [x^n] prod 1/(1-x^k)    P = sp.prod(1 / (1 - x**k) for k in range(1, 6))    ps = sp.series(P, x, 0, 11).removeO()    p = [ps.coeff(x, k) for k in range(11)]    print('整数拆分 p(0)~p(10):', p)     # 3) 两枚骰子点数之和的卷积    die = [1 / 6] * 6    prob = [0] * 13    for i in range(6):        for j in range(6):            prob[i + j + 2] += die[i] * die[j]    print('和为7的概率:', prob[7])  if __name__ == '__main__':    main()
目的:用 Python 验证三个核心结论——斐波那契生成函数展开等于递推、整数拆分的连乘生成函数系数、两枚骰子的卷积分布。
  1. F = x / (1 - x - x**2)斐波那契数列的生成函数,用封闭形式代替无穷级数。
  2. gf = extract(F, 10)把生成函数展开成幂级数,逐项取出前 10 个系数。
  3. assert gf == fib[:10]用递推暴力算出的结果做对照,保证生成函数解正确。
  4. P = sp.prod(1 / (1 - x**k) for k in range(1, 6))整数拆分的“连乘账本”,每个因子代表一种单位重量的食材包。
  5. ps = sp.series(P, x, 0, 11).removeO()展开连乘积并丢弃高阶余项,只保留前 11 项。
  6. prob[i + j + 2] += die[i] * die[j]卷积的核心操作:两枚骰子的点数相加,概率累加。

常见误区

误区看到 \(\frac{1}{1-x}\) 就迫不及待代入 \(x=2\),结果算出 \(-1\),大惑不解:“销量怎么可能是负数?”

正解生成函数里的 \(x\) 只是占位符(标签),不是变量。数学上称其为“形式幂级数”,我们只关心各项系数。就像阿数账本上的 \(x^n\) 写着“第 \(n\) 天”,你往标签上写日期以外的数字没有意义。把 \(x=2\) 代入等于把座位号当成了票价,自然得出荒谬结论。

为何常见:中学里学的函数 \(f(x)\) 都要代值计算,惯性思维让初学者把“形式”二字忽略了。

误区遇到“\(n\) 个元素排队有多少种方案”这类排列问题,直接套普通生成函数,结果比正确答案少了 \(n!\) 倍还浑然不觉。

正解有顺序的计数问题用 EGF(除以 \(n!\) 抹平内部顺序),无顺序的组合计数用 OGF。判断标准是“元素之间是否存在顺序区分”。就像阿数记账,统计“点了哪些菜”用普通账本,统计“上菜顺序”必须用盖章账本。

为何常见:两类生成函数的定义长得太像,只差一个分母 \(n!\),初学者容易忽略适用场景。

误区看到 \(\sum_{n=0}^{\infty} a_n x^n\) 就条件反射地问“这个级数收敛吗?”,以为不收敛就不能用。

正解形式幂级数不需要收敛。我们只做系数运算,从不把 \(x\) 当作真实数值代入。收敛性在生成函数的“组合意义”层面无关紧要——它是代数对象,不是分析对象。阿数把纸条串成项链时,从不在乎“项链的重量”是多少。

为何常见:微积分课程把级数与收敛性深度绑定,形成了先入为主的观念。

误区解递推时特征方程有重根(比如 \((1-2x)^2\)),却只写一个 \(\frac{A}{1-2x}\) 项,导致求出的通项少了一个含 \(n\) 的因子。

正解重根必须写多个分式项:\(\frac{A}{1-2x} + \frac{B}{(1-2x)^2}\)。展开后第二项会产生 \((n+1)2^n\) 形式的修正。就像阿数整理账本时发现某个页面重复出现了,就要给每个副本单独留一个格子。

为何常见:中学分式分解只见过单根场景,没有处理重根的系统经验。

误区写 \(c_n = \sum_{k=0}^{n} a_k b_{n-k}\) 时不管数组边界,直接让 \(k\) 走满整个区间,导致下标越界,或者把不存在的项当成 0 计入。

正解实际求和界限应为 \(k\) 从 \(\max(0, n-\text{len}(b)+1)\) 到 \(\min(n, \text{len}(a)-1)\),超出边界的项视为 0。在代码里必须写清楚边界。阿数串纸条时,账本只有那么长,不能把不存在的纸条硬算进去。

为何常见:数学式子的无穷求和与编程的有限数组下标混为一谈,忽略了长度约束。

面试题

Q1. 数列 \(1, 1, 1, \dots\) 的普通生成函数是?

单选题 · easy

  • A.\(\frac{1}{1-x}\)← 正确答案
  • B.\(\frac{1}{1+x}\)
  • C.\(\frac{1}{1-x^2}\)
  • D.\(e^x\)

答案:A。几何级数公式 \(\sum_{n=0}^{\infty} x^n = \frac{1}{1-x}\)。就像每天卖 1 份招牌菜,账本折叠后就是 \(\frac{1}{1-x}\)。B 是交错级数,C 是偶数项级数,D 是指数生成函数的级数。

Q2. 提取系数 \([x^n]\frac{1}{1-2x}\) 的结果是?

单选题 · easy

  • A.\(2^n\)← 正确答案
  • B.\(n \cdot 2^n\)
  • C.\(2^{n+1}\)
  • D.\(\frac{1}{2^n}\)

答案:A。\(\frac{1}{1-2x} = \sum_{n=0}^{\infty} (2x)^n = \sum 2^n x^n\),所以系数是 \(2^n\)。B 是求导加权的结果,C 是等比数列求和误算,D 把底数倒置了。

Q3. 斐波那契数列 \(F_0=0, F_1=1\) 的生成函数是?

单选题 · mid

  • A.\(\frac{1}{1-x-x^2}\)
  • B.\(\frac{x}{1-x-x^2}\)← 正确答案
  • C.\(\frac{1}{1+x-x^2}\)
  • D.\(\frac{x}{1+x+x^2}\)

答案:B。代入递推得 \(F(x) = x + xF(x) + x^2F(x)\),解得 \(\frac{x}{1-x-x^2}\)。分子上的 \(x\) 来自初值 \(F_1=1\)。A 少了初值修正项,C、D 分母符号错误。

Q4. 指数生成函数中分母 \(n!\) 的数学意义是什么?

单选题 · mid

  • A.加速级数收敛
  • B.把排列的内部顺序抹平为组合计数← 正确答案
  • C.让系数数值变小便于计算
  • D.纯粹是历史约定,没有数学意义

答案:B。EGF 除以 \(n!\) 是为了消除有顺序结构的内部排列数。比如全排列数 \(n!\) 的 EGF 展开后得到 \(x^n\),顺序信息被“脱外套”。A 是误导,形式幂级数不关心收敛;C、D 忽略了 \(n!\) 的组合数学本质。

Q5. 两个普通生成函数 \(A(x)B(x)\) 中 \(x^n\) 的系数等于?

单选题 · mid

  • A.\(a_n b_n\)
  • B.\(\sum_{n=0}^{\infty} a_n b_n\)
  • C.\(\sum_{k=0}^{n} a_k b_{n-k}\)← 正确答案
  • D.\(\sum_{k=0}^{n} a_k b_k\)

答案:C。多项式乘法中,\(x^n\) 项来自所有指数和为 \(n\) 的配对,即 \(a_k x^k\) 与 \(b_{n-k} x^{n-k}\) 的乘积求和。这正是卷积。A 是同下标相乘,B 是全局求和,D 是下标相同的卷积变体。

Q6. 关于指数生成函数(EGF)的说法,正确的有?

多选题 · mid

  • A.EGF 乘法对应带二项式系数的卷积← 正确答案
  • B.EGF 适合处理有顺序的排列计数问题← 正确答案
  • C.EGF 乘法与普通生成函数乘法完全一样
  • D.全 1 数列的 EGF 是 \(e^x\)← 正确答案

答案:ABD。因为 \(\frac{x^k}{k!}\cdot\frac{x^{n-k}}{(n-k)!} = \binom{n}{k}\frac{x^n}{n!}\),所以 A 正确。EGF 除以 \(n!\) 抹平顺序,适合排列问题,B 正确。全 1 数列的 EGF 是 \(\sum x^n/n! = e^x\),D 正确。C 错误——两者的卷积规则不同,EGF 多出二项式系数。

Q7. 整数拆分 \(p(n)\) 的生成函数是?

单选题 · hard

  • A.\(\prod_{k=1}^{\infty} \frac{1}{1-x^k}\)← 正确答案
  • B.\(\frac{1}{(1-x)^n}\)
  • C.\(\sum_{k=1}^{\infty} x^{k^2}\)
  • D.\(\prod_{k=1}^{\infty} (1+x^k)\)

答案:A。每个因子 \(\frac{1}{1-x^k}\) 表示“重量为 \(k\) 的食材包可以选 0、1、2…… 份”,连乘后 \(x^n\) 的系数就是拆分方案数。B 是重复组合的生成函数,C 是平方数序列,D 是“每个重量最多选一次”的限制拆分。

Q8. 提取系数 \([x^n]\frac{1}{(1-x)^3}\) 的结果是?

单选题 · hard

  • A.\(n^2\)
  • B.\(\binom{n+2}{2}\)← 正确答案
  • C.\(\binom{n}{3}\)
  • D.\(n(n-1)\)

答案:B。\(\frac{1}{(1-x)^3}\) 是重复组合的生成函数,展开后系数为 \(\binom{n+2}{2} = \frac{(n+1)(n+2)}{2}\)。可以理解为从 3 类物品中可重复地取 \(n\) 个的方案数。A、D 都是多项式但不是组合数形式,C 下标不匹配。

Q9. 关于生成函数在算法中的应用,正确的有?

多选题 · hard

  • A.多项式乘法在系数上等价于卷积运算← 正确答案
  • B.FFT 可以把卷积复杂度从 \(O(n^2)\) 降到 \(O(n\log n)\)← 正确答案
  • C.概率母函数 \(G(z)=\mathbb{E}[z^X]\) 的导数在 \(z=1\) 处给出期望← 正确答案
  • D.背包计数问题只能通过穷举所有组合求解

答案:ABC。多项式乘法的系数恰好是卷积,A 正确。FFT 通过频域相乘加速时域卷积,B 正确。概率母函数是 \(G(z)=\sum P(X=k)z^k\),求导后令 \(z=1\) 得到 \(\mathbb{E}[X]\),C 正确。背包计数可以用生成函数连乘因子快速求解,D 错误。

Q10. 数列 \(a_n = n\)(即 \(0, 1, 2, 3, \dots\))的普通生成函数是?

单选题 · easy

  • A.\(\frac{x}{(1-x)^2}\)← 正确答案
  • B.\(\frac{x}{1-x}\)
  • C.\(\frac{1}{(1-x)^2}\)
  • D.\(e^x\)

答案:A。对 \(\frac{1}{1-x} = \sum x^n\) 两边求导得 \(\sum n x^{n-1} = \frac{1}{(1-x)^2}\),再乘 \(x\) 得到 \(\sum n x^n = \frac{x}{(1-x)^2}\)。这正是“求导加权”运算的实例。B 是常数位移的结果,C 少乘了 \(x\),D 是 EGF 的封闭形式。