跳转至

常系数齐次线性递推

简介

常系数齐次线性递推数列(又称为 C-finite 或 C-recursive 数列)是常见的一类基础的递推数列.

对于数列 (aj)j≥0 和其递推式

an=∑j=1dcjan−j,(n≥d)

其中 cj 不全为零,我们的目标是在给出初值 a0,…,ad−1 和递推式中的 c1,…,cd 后求出 ak.如果 k≫d,我们想要更快速的算法.

这里 (aj)j≥0 被称为 d 阶的常系数齐次线性递推数列.

Fiduccia 算法

Fiduccia 算法使用多项式取模和快速幂来计算 ak,时间为 O(M(d)log⁡k),其中 O(M(d)) 表示两个次数为 O(d) 的多项式相乘的时间.

算法:构造多项式 Γ(x):=xd−∑j=0d−1cd−jxj 和 A(x):=∑j=0d−1ajxj,那么

ak=⟨xkmodΓ(x),A(x)⟩

其中定义 ⟨(∑j=0n−1fjxj),(∑j=0n−1gjxj)⟩:=∑j=0n−1fjgj 为内积.

证明:我们定义 Γ(x) 的友矩阵为

CΓ:=[cd1cd−1⋱⋮1c1]

我们定义多项式 b(x):=∑j=0d−1bjxj 和

Bb:=[b0b1⋯bd−1]⊺

观察到

[cd1cd−1⋱⋮1c1]⏟CΓ[b0b1⋮bd−1]⏟Bb=[cdbd−1b0+cd−1bd−1⋮bd−2+c1bd−1]⏟BxbmodΓ

且

CΓ=[BxmodΓBx2modΓ⋯BxdmodΓ],(CΓ)2=[Bx2modΓBx3modΓ⋯Bxd+1modΓ],⋯(CΓ)k=[BxkmodΓBxk+1modΓ⋯Bxk+dmodΓ]

我们将这个递推用矩阵表示有

[akak+1⋮ak+d−1]=[1⋱1cdcd−1⋯c1]k⏟((CΓ)⊺)k=((CΓ)k)⊺[a0a1⋮ad−1]

可知 ((CΓ)k)⊺ 的第一行为 BxkmodΓ,根据矩阵乘法的定义得证.

表示为有理函数

对于上述数列 (aj)j≥0 一定存在有理函数

P(x)Q(x)=∑j≥0ajxj

且 Q(x)=xdΓ(x−1),deg⁡P<d.我们称其为「有理函数」是因为 P(x),Q(x) 是「多项式」.

证明:对于 P(x)=∑j=0d−1pjxj 和 Q(x):=∑j=0dqjxj 考虑 P(x)Q(x)=∑j≥0q~jxj 的系数定义,这几乎就是形式幂级数「除法」的定义,

q~N={p0q0−1, if N=0,(pN−∑j=1Nqjq~N−j)⋅q0−1, else if N<d,−q0−1∑j=1dqjq~N−j, otherwise.

我们只需要令

P(x)=((∑j≥0ajxj)⋅xdΓ(x−1))modxd

那么根据 q~N 的定义,必然有 P(x)Q(x)=∑j≥0ajxj.

Bostan–Mori 算法

计算单项

我们的目标仍然是给出上述多项式 P(x),Q(x),求算 [xk]P(x)Q(x).

Bostan–Mori 算法基于 Graeffe 迭代,对于上述多项式 P(x),Q(x) 有

P(x)Q(x)=P(x)Q(−x)Q(x)Q(−x)=U0(x2)+xU1(x2)V(x2)

因为分母 V(x2) 是偶函数,所以子问题只需考虑其中的一侧

[xk]P(x)Q(x)=[x⌊k/2⌋]Ukmod2(x)V(x)

我们付出两次多项式乘法的代价使得问题至少减少为原先的一半,而当 k=0 时显然有 [x0]P(x)Q(x)=P(0)Q(0),时间复杂度同上.

计算连续若干项

目标是给出上述多项式 P(x),Q(x),求算 [x[L,R)]P(x)Q(x).下面的计算中我们只需考虑对答案「有影响」的系数,这是 Bostan–Mori 算法的关键.

我们不妨假设 deg⁡P<deg⁡Q,否则我们也可以通过一次带余除法使问题回到这种情况.

我们先考虑更简单的问题:

[x[L,R)]1Q(x)=[x[L,R)]1Q(x)Q(−x)⋅Q(−x)

我们需要求出 [x[L−deg⁡Q,R)]1Q(x)Q(−x) 然后作一次乘法并取出 xL,…,xR−1 的系数.令 V(x2)=Q(x)Q(−x) 那么我们只需求出

[x[⌈L−deg⁡Q2⌉,⌈R2⌉)]1V(x)

就可以还原出 [x[L−deg⁡Q,R)]1Q(x)Q(−x).进而我们只需求出 [x[L−deg⁡P,R)]1Q(x) 再和 P(x) 作一次乘法即可求出 [x[L,R)]P(x)Q(x).

上面的算法虽然已经可以工作,但是每一次的递归的时间复杂度与 R−L 相关,我们希望能至少在递归求算时摆脱 R−L,更具体的,我们先考虑求算 [x[L,L+deg⁡Q+1)]1Q(x),考虑

[x[L,L+deg⁡Q+1)]1Q(x)=[x[L,L+deg⁡Q+1)]1Q(x)Q(−x)⋅Q(−x)

我们需要求出

[x[L−deg⁡Q,L+deg⁡Q+1)]1Q(x)Q(−x)

那么对于 V(x2)=Q(x)Q(−x) 而言,我们只需求出

[x[⌈(L−deg⁡Q)/2⌉,⌈(L+deg⁡Q+1)/2⌉)]1V(x)

这是因为

[xk]1Q(x)Q(−x)={[xk/2]1V(x),if k≡0(mod2),0,otherwise.

我们知道 L+deg⁡Q 和 L−deg⁡Q 的奇偶性是一样的,所以

⌈L+deg⁡Q+12⌉−⌈L−deg⁡Q2⌉={deg⁡Q+1,if L+deg⁡Q≡0(mod2),deg⁡Q,otherwise.

这样我们可以写出伪代码

Algorithm Slice-Coefficients⁡(Q,L):Input: Q(x)∈C[x],L∈Z.Output: [x[L,L+deg⁡Q+1)]Q(x)−1.1if L≤1 then return [x[L,L+deg⁡Q+1)]Q(x)−1Use other algorithm to compute Q(x)−12V(x2)←Q(x)Q(−x)3k←⌈L−deg⁡Q2⌉4(tk,…,tk+deg⁡Q)←Slice-Coefficients⁡(V,k)5T(x)←x(L−deg⁡Q)mod2∑j=0deg⁡Qtj+kx2j6return [x[deg⁡Q,2deg⁡Q+1)]T(x)Q(−x)

但是只有这个算法还不够,我们需要重新找到一个有理函数并求算更多系数.

找到新的有理函数表示

我们知道 Q(x) 本身和 Q(x)−1 的一部分连续的系数比如 [x[L,L+deg⁡Q)]Q(x)−1 和 L≥0,我们希望求出 [x[L+deg⁡Q,L+2deg⁡Q)]Q(x)−1,这等价于我们要求某个 P(x) 且 deg⁡P<deg⁡Q 使得 P(x)Q(x) 的前 deg⁡Q 项与 [x[L,L+deg⁡Q)]Q(x)−1 相同.简单来说:递推关系(有理函数的分母)是不变的,我们所做的只是更换初值(有理函数的分子).

具体的,考虑

P(x)Q(x)=∑j≥0ajxj

我们现在希望将递推前进 n 项,那么就是

∑j≥najxj−n=P(x)Q(x)xn−Q(x)∑j=0n−1ajxjQ(x)xn

我们先用一次 Slice-Coefficients⁡(Q,L−deg⁡P) 计算出 [x[L−deg⁡P,L−deg⁡P+deg⁡Q+1)]Q(x)−1,然后我们扩展合并出 [x[L−deg⁡P,L+deg⁡Q)]Q(x)−1,再重新计算一个分子使得

P~(x)Q(x)=∑j≥0([xL+j]P(x)Q(x))xj

最后我们使用形式幂级数的除法计算出 [x[0,R−L)]P~(x)Q(x),时间为 O(M(d)log⁡L+M(R−L)).

参考文献

  1. Alin Bostan, Ryuhei Mori.A Simple and Fast Algorithm for Computing the N-th Term of a Linearly Recurrent Sequence.