在做【GXOI/GZOI2019 逼死强迫症】的时候,在题解区看到一种并没有解待定系数方程,而利用生成函数和特征方程直接算出通项公式的做法,去UOJ群里面问了下群友,一位dalao在私信里面给了我一份《信号与系统——奥本海姆(第二版)》的pdf,在附录里面我找到了一种比较通用的方法,感觉还不错,在这里记录一下。
主要是有的方程列待定系数方程太TM麻烦了还难算,这里给出一种方法从一些角度优化人脑计算部分的计算量。
如果您是dalao/您觉得这玩意太简单了/您觉得博主讲的太垃圾了/您觉得这玩意早就是您玩剩下的了,也请您指出博主这篇讲解的不足之处,评论区留言即可,十分感谢!无意义评论将会被删除。
这篇博客主要讲解内容和算法没有太大关系,只是为了优化人脑的计算量。
按照规矩,先列一些前置知识,熟悉的可以直接跳过,不多BB,开始主题。
前置知识:特征方程。
定义: k k k阶常系数齐次线性递归关系
设 k k k为正整数,在初始条件 a 0 , a 1 , ⋯ a k − 1 a_0,a_1,\cdots a_{k-1} a0,a1,⋯ak−1下,递归关系
a n = c 1 a n − 1 + c 2 a n − 2 + ⋯ + c k a n − k ( n ≥ k ) \begin{aligned}a_n=c_1a_{n-1}+c_2a_{n-2}+\cdots +c_ka_{n-k}&&&&(n\geq k)\end{aligned} an=c1an−1+c2an−2+⋯+ckan−k(n≥k)
称为 k k k阶常系数齐次线性递归关系,其中 c 1 , c 2 , ⋯ c k c_1,c_2,\cdots c_k c1,c2,⋯ck为常数且 c k ≠ 0 c_k\neq 0 ck=0
如果一个数列存在 k k k阶常系数齐次线性递归关系,定义其特征方程为:
λ k − c 1 λ k − 1 − c 2 λ k − 2 − ⋯ − c k = 0 \lambda^k-c_1\lambda ^{k-1}-c_2\lambda^{k-2}-\cdots-c_k=0 λk−c1λk−1−c2λk−2−⋯−ck=0
定理1:若递归关系
a
n
=
c
1
a
n
−
1
+
c
2
a
n
−
2
+
⋯
+
c
k
a
n
−
k
(
n
≥
k
)
a_n=c_1a_{n-1}+c_2a_{n-2}+\cdots +c_ka_{n-k}(n\geq k)
an=c1an−1+c2an−2+⋯+ckan−k(n≥k)的特征方程
λ
k
−
c
1
λ
k
−
1
−
c
2
λ
k
−
2
−
⋯
−
c
k
=
0
\lambda^k-c_1\lambda ^{k-1}-c_2\lambda^{k-2}-\cdots-c_k=0
λk−c1λk−1−c2λk−2−⋯−ck=0有
k
k
k个不同的根
λ
1
,
λ
2
,
⋯
λ
k
\lambda_1,\lambda_2,\cdots \lambda_k
λ1,λ2,⋯λk。
则其通解为
a
n
=
C
1
λ
1
n
+
C
2
λ
2
n
+
⋯
+
C
k
λ
k
n
a_n=C_1\lambda_1^n+C_2\lambda_2^n+\cdots +C_k\lambda_k^n
an=C1λ1n+C2λ2n+⋯+Ckλkn,给定一组初始条件
a
0
,
a
1
⋯
a
k
−
1
a_0,a_1\cdots a_{k-1}
a0,a1⋯ak−1可以唯一确定一组特定常数
C
1
,
C
2
,
⋯
,
C
k
C_1,C_2,\cdots ,C_k
C1,C2,⋯,Ck
例子太多了,可以参考斐波那契数列。
不证,一般OI里面用到的就是这个东西,推广一下得到下一个引理:
定理2:若递归关系
a
n
=
c
1
a
n
−
1
+
c
2
a
n
−
2
+
⋯
+
c
k
a
n
−
k
(
n
≥
k
)
a_n=c_1a_{n-1}+c_2a_{n-2}+\cdots +c_{k}a_{n-k}(n\geq k)
an=c1an−1+c2an−2+⋯+ckan−k(n≥k)的特征方程
λ
k
−
c
1
λ
k
−
1
−
c
2
λ
k
−
2
−
⋯
−
c
k
=
0
\lambda^k-c_1\lambda ^{k-1}-c_2\lambda^{k-2}-\cdots-c_k=0
λk−c1λk−1−c2λk−2−⋯−ck=0有
t
t
t个不同的根
λ
1
,
λ
2
⋯
λ
t
\lambda_1,\lambda_2\cdots \lambda_t
λ1,λ2⋯λt,其重数分别为
r
1
,
r
2
⋯
r
t
(
r
1
+
r
2
+
⋯
+
r
t
=
k
)
r_1,r_2\cdots r_t(r_1+r_2+\cdots +r_t=k)
r1,r2⋯rt(r1+r2+⋯+rt=k)。
则其通解为
a
n
=
a
1
(
n
)
+
a
2
(
n
)
+
⋯
+
a
t
(
n
)
a_n=a_1(n)+a_2(n)+\cdots +a_t(n)
an=a1(n)+a2(n)+⋯+at(n),其中
a
i
(
n
)
=
(
A
i
1
+
A
i
2
n
+
⋯
+
A
i
r
i
n
r
i
−
1
)
λ
i
n
a_i(n)=(A_{i1}+A_{i2}n+\cdots +A_{ir_i}n^{r_i-1})\lambda_i^n
ai(n)=(Ai1+Ai2n+⋯+Airinri−1)λin,给定一组初始条件
a
0
,
a
1
⋯
a
k
−
1
a_0,a_1\cdots a_{k-1}
a0,a1⋯ak−1可以唯一确定其中待定系数。
不证,这个定理就是上面那个的推广版本。
多的东西不讲了,这篇博客的重点是求通项公式。
所以对于一般的线性递推关系,我们只需要把待定系数设出来,然后把初始条件代入,暴力高斯消元求解即可。
但是一旦特征根带根号这个方法就显得蛋疼起来,所以我们希望能够有一些比较优秀的方法能够快速计算出一些系数从而减少解方程组的计算量。。。
前置知识:生成函数
其实能来看这篇博客的应该生成函数那些基本运用都是比较熟练的了吧。。。
这篇文章主要讲的是求通项,那么这里就来讲一下求通项的事情。
以下所说的求通项,全部是指对一般生成函数求通项,也就是多项式的系数的通项。
其实初学者可能对下面整懵,请时刻记住,生成函数一般是一个无限多项式,而我们能够解方程的一般都是有限多项式,所以下面基本上就是在想办法利用有限多项式的各种性质来搞。
定理:一个具有线性递推关系的数列,其生成函数可以表示为 F ( x ) = P ( x ) Q ( x ) F(x)=\frac{P(x)}{Q(x)} F(x)=Q(x)P(x),其中 P ( x ) , Q ( x ) P(x),Q(x) P(x),Q(x)都是有限多项式,且 Q ( x ) Q(x) Q(x)常数项为 1 1 1, P ( x ) , Q ( x ) P(x),Q(x) P(x),Q(x)没有公因式,即它们没有相等的根,这样的 P ( x ) , Q ( x ) P(x),Q(x) P(x),Q(x)唯一存在。
性质: Q ( x ) Q(x) Q(x)的若干个根就是该线性递推数列的特征根的倒数。
因为 Q ( x ) Q(x) Q(x)实际上就是特征方程的reverse。
煮个栗子:拿斐波那契数列来说,其递推关系表示为
F
i
b
n
=
F
i
b
n
−
1
+
F
i
b
n
−
2
,
n
≥
2
,
F
i
b
1
=
F
i
b
0
=
1
Fib_n=Fib_{n-1}+Fib_{n-2},n\geq 2,Fib_1=Fib_0=1
Fibn=Fibn−1+Fibn−2,n≥2,Fib1=Fib0=1,特征方程为
x
2
−
x
−
1
=
0
x^2-x-1=0
x2−x−1=0,而其生成函数设为
F
(
x
)
F(x)
F(x),显然有
F
(
x
)
=
x
F
(
x
)
+
x
2
F
(
x
)
+
1
=
1
1
−
x
−
x
2
F(x)=xF(x)+x^2F(x)+1=\frac{1}{1-x-x^2}
F(x)=xF(x)+x2F(x)+1=1−x−x21。
而这本质是因为在特征方程中
λ
\lambda
λ的次数是用来比较高项和低项的,而
Q
(
x
)
Q(x)
Q(x)里面的系数本质是用来在生成函数中把次数对齐,用来把低次项拉到高次项的,所以刚好反起来。
生成函数这样的多项式我们称为形式幂级数,简单说来这里 x x x就是提供一个占位符的作用,一般不考虑形式幂级数的收敛条件而在合适的场合下默认它收敛。
煮个栗子:对于形式幂级数 1 + x + x 2 + ⋯ + x ∞ 1+x+x^2+\cdots +x^\infty 1+x+x2+⋯+x∞,一般直接认为它等于 1 1 − x \frac{1}{1-x} 1−x1,直接套用等比数列求和公式,然后默认 x ∞ x^\infty x∞收敛于 0 0 0即可。
对于形式幂级数,以下等式成立:
1 1 − a x = 1 + a x + ( a x ) 2 + ⋯ \dfrac{1}{1-ax}=1+ax+(ax)^2+\cdots 1−ax1=1+ax+(ax)2+⋯
1 ( 1 − a x ) 2 = 1 + 2 a x + 3 ( a x ) 2 + ⋯ ( n + 1 ) ( a x ) n + ⋯ \dfrac{1}{(1-ax)^2}=1+2ax+3(ax)^2+\cdots (n+1)(ax)^n+\cdots (1−ax)21=1+2ax+3(ax)2+⋯(n+1)(ax)n+⋯
继续推广可以得到:
1 ( 1 − a x ) t = ∑ n = 0 ∞ ( n + t − 1 n ) ( a x ) n \dfrac{1}{(1-ax)^t}=\sum\limits_{n=0}^\infty{n+t-1\choose n}(ax)^n (1−ax)t1=n=0∑∞(nn+t−1)(ax)n
这种形式可以直接得到第 n n n项系数,非常方便。
这些等式就是我们接下来加速人脑计算的基础。
进入主题:
现在我们希望快速用人脑算出一个递推关系的通解。
当然,前面已经说了,解待定系数方程可能会很窒息。
所以我们希望能够减少方程的数量。
定义:如果一个函数 G ( x ) G(x) G(x)能够写作 G ( x ) = P ( x ) Q ( x ) G(x)=\frac{P(x)}{Q(x)} G(x)=Q(x)P(x),其中 P ( x ) P(x) P(x)次数为 m m m,且 Q ( x ) Q(x) Q(x)次数为 n n n,如果 m < n m<n m<n,我们称 G ( x ) G(x) G(x)为一个真有理函数。
我们这里考虑的所有生成函数都是真有理函数,不考虑 e x p exp exp之类的东西。
定理:对于一个真有理函数 G ( x ) = P ( x ) Q ( x ) G(x)=\frac{P(x)}{Q(x)} G(x)=Q(x)P(x),设 Q ( x ) Q(x) Q(x)有 r r r个不同的根 ρ 1 , ρ 2 ⋯ , ρ r \rho_1,\rho_2\cdots,\rho_r ρ1,ρ2⋯,ρr,且重数分别为 σ 1 , . σ 2 ⋯ σ r \sigma_1,.\sigma_2\cdots \sigma_r σ1,.σ2⋯σr,则 G ( x ) G(x) G(x)存在一个分式拆分如下:
G ( x ) = ∑ i = 1 r ∑ k = 1 σ i A i k ( x − ρ i ) k G(x)=\sum_{i=1}^r\sum_{k=1}^{\sigma_i}\frac{A_{ik}}{(x-\rho_i)^k} G(x)=i=1∑rk=1∑σi(x−ρi)kAik
其中所有 A i k A_{ik} Aik均为常数。如果把生成函数表示成这种形式,能够得到 A i k A_{ik} Aik具体的值,我们就能直接得到拆分中每一项的 n n n次项系数从而得到该数列的通项公式。
下面直接给出求
A
i
k
A_{ik}
Aik的方法,证明留作练习
A i k = 1 ( σ i − k ) ! [ d σ i − k d x σ i − k ( x − ρ i ) σ i G ( x ) ] ∣ x = ρ i A_{ik}=\frac{1}{(\sigma_i-k)!}\left[\frac{\mathrm{d}^{\sigma_i-k}}{\mathrm{d}x^{\sigma_i-k}}(x-\rho_i)^{\sigma_i}G(x)\middle]\right|_{x=\rho_i} Aik=(σi−k)!1[dxσi−kdσi−k(x−ρi)σiG(x)]∣∣∣∣x=ρi
就是这么暴力,证明可以考虑其实就是反复求导使得 A i k A_{ik} Aik项不含 ( x − ρ i ) (x-\rho_i) (x−ρi)。此时含 ( x − ρ i ) (x-\rho_i) (x−ρi)次数较小的已经求导成0了,次数大的含有 ( x − ρ i ) (x-\rho_i) (x−ρi)项我们直接令 x = ρ i x=\rho_i x=ρi就行了
例题的话就开头提的那道题吧。