跳转至

常系数齐次线性递推

简介

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

对于数列 (aj)j0 和其递推式

an=j=1dcjanj,(nd)

其中 cj 不全为零,我们的目标是在给出初值 a0,,ad1 和递推式中的 c1,,cd 后求出 ak。如果 kd,我们想要更快速的算法。

这里 (aj)j0 被称为 d 阶的常系数齐次线性递推数列。

Fiduccia 算法

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

算法:构造多项式 Γ(x):=xdj=0d1cdjxjA(x):=j=0d1ajxj,那么

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

其中定义 (j=0n1fjxj),(j=0n1gjxj):=j=0n1fjgj 为内积。

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

CΓ:=[cd1cd11c1]

我们定义多项式 b(x):=j=0d1bjxj

Bb:=[b0b1bd1]

观察到

[cd1cd11c1]CΓ[b0b1bd1]Bb=[cdbd1b0+cd1bd1bd2+c1bd1]BxbmodΓ

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

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

[akak+1ak+d1]=[11cdcd1c1]k((CΓ))k=((CΓ)k)[a0a1ad1]

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

表示为有理函数

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

P(x)Q(x)=j0ajxj

Q(x)=xdΓ(x1)degP<d。我们称其为「有理函数」是因为 P(x),Q(x) 是「多项式」。

证明:对于 P(x)=j=0d1pjxjQ(x):=j=0dqjxj 考虑 P(x)Q(x)=j0q~jxj 的系数定义,这几乎就是形式幂级数「除法」的定义,

q~N={p0q01, if N=0,(pNj=1Nqjq~Nj)q01, else if N<d,q01j=1dqjq~Nj, otherwise.

我们只需要令

P(x)=((j0ajxj)xdΓ(x1))modxd

那么根据 q~N 的定义,必然有 P(x)Q(x)=j0ajxj

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)=[xk/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 算法的关键。

我们不妨假设 degP<degQ,否则我们也可以通过一次带余除法使问题回到这种情况。

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

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

我们需要求出 [x[LdegQ,R)]1Q(x)Q(x) 然后作一次乘法并取出 xL,,xR1 的系数。令 V(x2)=Q(x)Q(x) 那么我们只需求出

[x[LdegQ2,R2)]1V(x)

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

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

[x[L,L+degQ+1)]1Q(x)=[x[L,L+degQ+1)]1Q(x)Q(x)Q(x)

我们需要求出

[x[LdegQ,L+degQ+1)]1Q(x)Q(x)

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

[x[(LdegQ)/2,(L+degQ+1)/2)]1V(x)

这是因为

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

我们知道 L+degQLdegQ 的奇偶性是一样的,所以

L+degQ+12LdegQ2={degQ+1,if L+degQ0(mod2),degQ,otherwise.

这样我们可以写出伪代码

Algorithm Slice-Coefficients(Q,L):InputQ(x)C[x],LZ.Output[x[L,L+degQ+1)]Q(x)1.1if L1 then return [x[L,L+degQ+1)]Q(x)1Use other algorithm to compute Q(x)12V(x2)Q(x)Q(x)3kLdegQ24(tk,,tk+degQ)Slice-Coefficients(V,k)5T(x)x(LdegQ)mod2j=0degQtj+kx2j6return [x[degQ,2degQ+1)]T(x)Q(x)

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

找到新的有理函数表示

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

具体的,考虑

P(x)Q(x)=j0ajxj

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

jnajxjn=P(x)Q(x)xnQ(x)j=0n1ajxjQ(x)xn

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

P~(x)Q(x)=j0([xL+j]P(x)Q(x))xj

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

参考文献

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