BM \textit{BM} BM 算法
一、概述
全称是 Berlekamp-Massey \text{Berlekamp-Massey} Berlekamp-Massey 算法,用于求出一个序列 ⟨ a 0 , a 1 , a 2 , … , a n ⟩ \langle a_0,a_1,a_2,\dots,a_n\rangle ⟨a0,a1,a2,…,an⟩ 的最短 线性递推式。
线性递归式:一个有限非空数列 ⟨ r 0 , r 1 , … , r m ⟩ \langle r_0,r_1,\dots,r_{m}\rangle ⟨r0,r1,…,rm⟩ 使得任意 p ⩾ m p\geqslant m p⩾m 有 ∑ i = 0 m a p − i r i = 0 \sum_{i=0}^{m}a_{p-i}r_i=0 ∑i=0map−iri=0,则 ⟨ r ⟩ \langle r\rangle ⟨r⟩ 就是 ⟨ a ⟩ \langle a\rangle ⟨a⟩ 的线性递归式。从生成函数的角度来看,令 ⟨ a ⟩ \langle a\rangle ⟨a⟩ 对应 f ( x ) f(x) f(x),而 ⟨ r ⟩ \langle r\rangle ⟨r⟩ 对应 A ( x ) A(x) A(x),则存在次数不超过 ( m − 1 ) (m{\rm-}1) (m−1) 的多项式 R ( x ) R(x) R(x) 使得 A ( x ) f ( x ) = R ( x ) A(x)f(x)=R(x) A(x)f(x)=R(x) 。
线性递推式:满足 r 0 = 1 r_0=1 r0=1 的线性递归式。或者说 [ x 0 ] A ( x ) = 1 [x^0]A(x)=1 [x0]A(x)=1 。
然并无甚区别。
记号约定:记 ∣ f ( x ) ∣ |f(x)| ∣f(x)∣ 为形式幂级数 f ( x ) f(x) f(x) 的次数,即最大的 k k k 使得 [ x k ] f ( x ) ≠ 0 [x^k]f(x)\ne 0 [xk]f(x)=0 。
二、过程
看上去可怕,过程好像也并不复杂。假设我们求出了前
n
n
n 项的线性递推式
A
n
(
x
)
f
n
(
x
)
≡
R
n
(
x
)
(
m
o
d
x
n
)
A_n(x)f_n(x)\equiv R_n(x)\pmod{x^{n}}
An(x)fn(x)≡Rn(x)(modxn)
现在试试能否据此找到前
(
n
+
1
)
(n{+}1)
(n+1) 项的线性递推式。如果
A
n
(
x
)
f
n
(
x
)
≡
R
n
(
x
)
(
m
o
d
x
n
+
1
)
A_n(x)f_n(x)\equiv R_n(x)\pmod{x^{n+1}}
An(x)fn(x)≡Rn(x)(modxn+1),那么
A
n
(
x
)
A_n(x)
An(x) 就是。所以我们只讨论
A
n
(
x
)
f
n
(
x
)
≡
R
n
(
x
)
+
d
x
n
(
m
o
d
x
n
+
1
)
(1)
A_n(x)f_n(x)\equiv R_n(x)+dx^{n}\pmod{x^{n+1}} \tag{1}
An(x)fn(x)≡Rn(x)+dxn(modxn+1)(1)
假如上次在
p
(
p
<
n
)
p\;(p<n)
p(p<n) 处,也有一次 “失配” 的情况,即
A
p
(
x
)
f
(
x
)
≡
R
p
(
x
)
+
c
x
p
(
m
o
d
x
p
+
1
)
(2)
A_p(x)f(x)\equiv R_p(x)+cx^p\pmod{x^{p+1}} \tag{2}
Ap(x)f(x)≡Rp(x)+cxp(modxp+1)(2)
并且 满足
∣
A
p
+
1
(
x
)
∣
>
∣
A
p
(
x
)
∣
|A_{p+1}(x)|>|A_{p}(x)|
∣Ap+1(x)∣>∣Ap(x)∣ 。那么
(
2
)
(2)
(2) 式两边同时乘
x
n
−
p
d
c
−
1
x^{n-p}dc^{-1}
xn−pdc−1 得
x
n
−
p
d
c
−
1
A
p
(
x
)
f
(
x
)
≡
x
n
−
p
d
c
−
1
R
p
(
x
)
+
d
x
n
(
m
o
d
x
n
+
1
)
x^{n-p}dc^{-1}A_p(x)f(x)\equiv x^{n-p}dc^{-1}R_p(x)+dx^n\pmod{x^{n+1}}
xn−pdc−1Ap(x)f(x)≡xn−pdc−1Rp(x)+dxn(modxn+1)
然后与
(
1
)
(1)
(1) 式相减得
[
A
n
(
x
)
−
x
n
−
p
d
c
−
1
A
p
(
x
)
]
f
(
x
)
≡
R
n
(
x
)
−
x
n
−
p
d
c
−
1
R
p
(
x
)
(
m
o
d
x
n
+
1
)
\left[A_n(x)-x^{n-p}dc^{-1}A_p(x)\right]f(x)\equiv R_n(x)-x^{n-p}dc^{-1}R_p(x)\pmod{x^{n+1}}
[An(x)−xn−pdc−1Ap(x)]f(x)≡Rn(x)−xn−pdc−1Rp(x)(modxn+1)
所以我们找到了新的解是
{
A
n
+
1
(
x
)
=
A
n
(
x
)
−
d
c
x
n
−
p
A
p
(
x
)
R
n
+
1
(
x
)
=
R
n
(
x
)
−
d
c
x
n
−
p
R
p
(
x
)
\begin{cases} A_{n+1}(x)=A_n(x)-{d\over c}x^{n-p}A_p(x)\\ R_{n+1}(x)=R_n(x)-{d\over c}x^{n-p}R_p(x) \end{cases}
{An+1(x)=An(x)−cdxn−pAp(x)Rn+1(x)=Rn(x)−cdxn−pRp(x)
由 ∣ R n ( x ) ∣ < ∣ A n ( x ) ∣ ∧ ∣ R p ( x ) ∣ < ∣ A p ( x ) ∣ |R_n(x)|<|A_n(x)|\land |R_p(x)|<|A_p(x)| ∣Rn(x)∣<∣An(x)∣∧∣Rp(x)∣<∣Ap(x)∣ 可知 ∣ R n + 1 ( x ) ∣ < ∣ A n + 1 ( x ) ∣ |R_{n+1}(x)|<|A_{n+1}(x)| ∣Rn+1(x)∣<∣An+1(x)∣,而且 [ x 0 ] A n + 1 ( x ) = 1 [x^0]A_{n+1}(x)=1 [x0]An+1(x)=1,符合定义。时间复杂度 O ( n 2 ) \mathcal O(n^2) O(n2),其中 n n n 是最短线性递推式长度;我们后面也会说到, n n n 跟序列长度是同级的。
三、最短性
可行的线性递推式一抓一大把;唯独 BM \textit{BM} BM 抓住了最短的那个!下文记 A n ( x ) A_n(x) An(x) 为前 n n n 项的最短线性递推式。
首先 ∣ A n ( x ) ∣ ⩾ ∣ A n − 1 ( x ) ∣ |A_n(x)|\geqslant|A_{n-1}(x)| ∣An(x)∣⩾∣An−1(x)∣ 是显然的。再结合下面的引理,就可以说明最短性了。
Lemma:若 ∣ A n ( x ) ∣ > ∣ A n − 1 ( x ) ∣ |A_n(x)|>|A_{n-1}(x)| ∣An(x)∣>∣An−1(x)∣,则最短线性递推式长度 ∣ A n ( x ) ∣ ⩾ n − ∣ A n − 1 ( x ) ∣ |A_n(x)|\geqslant n-|A_{n-1}(x)| ∣An(x)∣⩾n−∣An−1(x)∣ 。
插嘴:这个 L e m m a \rm Lemma Lemma 使用 “线性递推式” 的定义其实很好证明;我已写了生成函数的证明,不愿换掉罢了。
证明:反证法。若
∣
A
n
(
x
)
∣
⩽
n
−
1
−
∣
A
n
−
1
(
x
)
∣
|A_n(x)|\leqslant n-1-|A_{n-1}(x)|
∣An(x)∣⩽n−1−∣An−1(x)∣,由
[
x
0
]
A
n
(
x
)
=
1
[x^0]A_n(x)=1
[x0]An(x)=1 可知
[
1
−
A
n
(
x
)
]
[1-A_n(x)]
[1−An(x)] 是
x
x
x 的倍数,同乘于
A
n
−
1
(
x
)
A_{n-1}(x)
An−1(x) 的定义式方程
A
n
−
1
(
x
)
f
(
x
)
≡
R
n
−
1
(
x
)
(
m
o
d
x
n
)
A_{n-1}(x)f(x)\equiv R_{n-1}(x)\pmod{x^n}
An−1(x)f(x)≡Rn−1(x)(modxn)
就可以让模数乘
x
x
x 了。得
[
1
−
A
n
(
x
)
]
A
n
−
1
(
x
)
f
(
x
)
≡
[
1
−
A
n
(
x
)
]
R
n
−
1
(
x
)
(
m
o
d
x
n
)
[1-A_n(x)]A_{n-1}(x)f(x)\equiv [1-A_n(x)]R_{n-1}(x)\pmod{x^n}
[1−An(x)]An−1(x)f(x)≡[1−An(x)]Rn−1(x)(modxn)
对等式左侧进行处理,把
f
(
x
)
f(x)
f(x) 乘进括号内,用
A
n
(
x
)
f
(
x
)
≡
R
n
(
x
)
A_n(x)f(x)\equiv R_n(x)
An(x)f(x)≡Rn(x) 作代换得
[
f
(
x
)
−
R
n
(
x
)
]
A
n
−
1
(
x
)
≡
R
n
−
1
(
x
)
−
A
n
(
x
)
R
n
−
1
(
x
)
[f(x)-R_n(x)]A_{n-1}(x)\equiv R_{n-1}(x)-A_n(x)R_{n-1}(x)
[f(x)−Rn(x)]An−1(x)≡Rn−1(x)−An(x)Rn−1(x)
再把括号拆开,然后移项。
A
n
−
1
(
x
)
f
(
x
)
≡
R
n
−
1
(
x
)
+
A
n
−
1
(
x
)
R
n
(
x
)
−
A
n
(
x
)
R
n
−
1
(
x
)
(
m
o
d
x
n
)
(3)
A_{n-1}(x)f(x)\equiv R_{n-1}(x)+A_{n-1}(x)R_n(x)-A_n(x)R_{n-1}(x)\pmod{x^n}\tag{3}
An−1(x)f(x)≡Rn−1(x)+An−1(x)Rn(x)−An(x)Rn−1(x)(modxn)(3)
我们敏锐地注意到后两项是类似叉积的形式,联想到克莱默法则。或许它是某个方程组的解?正是如此。
{
A
n
−
1
(
x
)
f
(
x
)
≡
R
n
−
1
(
x
)
A
n
(
x
)
f
(
x
)
≡
R
n
(
x
)
(
m
o
d
x
n
−
1
)
⟹
A
n
−
1
(
x
)
R
n
(
x
)
−
A
n
(
x
)
R
n
−
1
(
x
)
≡
0
(
m
o
d
x
n
−
1
)
\begin{cases} A_{n-1}(x)f(x)\equiv R_{n-1}(x)\\ A_n(x)f(x)\equiv R_n(x) \end{cases} \pmod{x^{n-1}}\\ \implies A_{n-1}(x)R_n(x)-A_n(x)R_{n-1}(x)\equiv 0\pmod{x^{n-1}}
{An−1(x)f(x)≡Rn−1(x)An(x)f(x)≡Rn(x)(modxn−1)⟹An−1(x)Rn(x)−An(x)Rn−1(x)≡0(modxn−1)
联系我们的前提条件
∣
A
n
−
1
(
x
)
R
n
(
x
)
−
A
n
(
x
)
R
n
−
1
(
x
)
∣
<
∣
A
n
(
x
)
∣
+
∣
A
n
−
1
(
x
)
∣
⩽
n
−
1
|A_{n-1}(x)R_n(x)-A_n(x)R_{n-1}(x)|<|A_n(x)|+|A_{n-1}(x)|\leqslant n-1
∣An−1(x)Rn(x)−An(x)Rn−1(x)∣<∣An(x)∣+∣An−1(x)∣⩽n−1
可知此处取模毫无意义,二者就是相等。代入 ( 3 ) (3) (3) 式即知 A n − 1 ( x ) , R n − 1 ( x ) A_{n-1}(x),R_{n-1}(x) An−1(x),Rn−1(x) 是取模 x n x^{n} xn 意义下的合法解,与前提条件 ∣ A n ( x ) ∣ > ∣ A n − 1 ( x ) ∣ |A_n(x)|>|A_{n-1}(x)| ∣An(x)∣>∣An−1(x)∣ 矛盾。证毕。 ■ \blacksquare ■
证明 L e m m a \rm Lemma Lemma 之后,我们就有 ∣ A n + 1 ( x ) ∣ ⩾ max { ∣ A n ( x ) ∣ , n + 1 − ∣ A n ( x ) ∣ } |A_{n+1}(x)|\geqslant\max\big\{|A_n(x)|,\;n{\rm+}1{\rm-}|A_{n}(x)|\big\} ∣An+1(x)∣⩾max{∣An(x)∣,n+1−∣An(x)∣} 。用归纳法证明这个下界恰好被取到,则是比较简单的事情了:
“失配” 时,递推式告诉我们 ∣ A n + 1 ( x ) ∣ = max { ∣ A n ( x ) ∣ , n − p + ∣ A p ( x ) ∣ } |A_{n+1}(x)|=\max\big\lbrace|A_n(x)|,\;n{\rm-}p{\rm+}|A_p(x)|\big\rbrace ∣An+1(x)∣=max{∣An(x)∣,n−p+∣Ap(x)∣} 。只需看后者。由于 p p p 是上一次变长的位置,即 i ∈ ( p , n ] i\in(p,n] i∈(p,n] 的 ∣ A i ( x ) ∣ |A_i(x)| ∣Ai(x)∣ 都相同,再由归纳假设,得 n − p + ∣ A p ( x ) ∣ = n − p + p + 1 − ∣ A p + 1 ( x ) ∣ = n + 1 − ∣ A n ( x ) ∣ n-p+|A_p(x)|=n-p+p+1-|A_{p+1}(x)|=n+1-|A_{n}(x)| n−p+∣Ap(x)∣=n−p+p+1−∣Ap+1(x)∣=n+1−∣An(x)∣,证毕。
四、无穷项序列
无穷项序列也可能存在线性递推式。若我们已知其线性递推式满足 ∣ A ( x ) ∣ ⩽ m |A(x)|\leqslant m ∣A(x)∣⩽m,那么只需要求出前 2 m 2m 2m 项的线性递推式。
证明蕴含在 L e m m a \rm Lemma Lemma 中:若以后还想更改线性递推式,就至少是 n + 1 − ∣ A ( x ) ∣ ⩾ 2 m + 1 − m = m + 1 n+1-|A(x)|\geqslant 2m+1-m=m+1 n+1−∣A(x)∣⩾2m+1−m=m+1 长度的线性递推式了,不满足我们的已知信息。
注意:此方法不能逆用;仅在已知 m m m 的范围时可行。
五、初值
BM \textit{BM} BM 算法的赋初值,真的不是那么平凡的。比如 A 1 ( x ) A_1(x) A1(x) 是什么?或者从 A 0 ( x ) A_0(x) A0(x) 开始么?
值得注意的是,
∣
A
n
(
x
)
∣
|A_n(x)|
∣An(x)∣ 不一定小于
n
n
n 。虽然这对于线性递推式的成立是毫无帮助的,但是它可以让
∣
R
n
(
x
)
∣
<
∣
A
n
(
x
)
∣
|R_n(x)|<|A_n(x)|
∣Rn(x)∣<∣An(x)∣ 恒成立。说白了还是妥协之计。
那么,我们其实可以从 A 0 ( x ) A_0(x) A0(x) 开始赋值。就设定为 { A 0 ( x ) = 1 R 0 ( x ) = 0 \begin{cases}A_0(x)=1\\ R_0(x)=0\end{cases} {A0(x)=1R0(x)=0 就好。其实 ∣ R 0 ( x ) ∣ = − ∞ |R_0(x)|=-\infty ∣R0(x)∣=−∞,这是为了使得 ∣ f ( x ) g ( x ) ∣ = ∣ f ( x ) ∣ + ∣ g ( x ) ∣ |f(x)g(x)|=|f(x)|+|g(x)| ∣f(x)g(x)∣=∣f(x)∣+∣g(x)∣ 恒成立;所以此时 ∣ A 0 ( x ) ∣ > ∣ R 0 ( x ) ∣ |A_0(x)|>|R_0(x)| ∣A0(x)∣>∣R0(x)∣ 成立,当然也满足 [ x 0 ] A 0 ( x ) = 1 [x^0]A_0(x)=1 [x0]A0(x)=1 的定义。
这样赋值以后,可能会让
A
n
(
x
)
A_n(x)
An(x) 及以前都保留该设定:在
a
0
=
a
1
=
⋯
=
a
n
−
1
=
0
a_0=a_1=\cdots=a_{n-1}=0
a0=a1=⋯=an−1=0 的时候就会发生。然后如果
A
n
+
1
(
x
)
A_{n+1}(x)
An+1(x) 失配了呢?即
A
n
(
x
)
f
(
x
)
≡
R
n
(
x
)
+
a
n
x
n
(
m
o
d
x
n
+
1
)
A_n(x)f(x)\equiv R_n(x)+a_nx^n\pmod{x^{n+1}}
An(x)f(x)≡Rn(x)+anxn(modxn+1),那么根据我们的调整方式,我们会得到
{
A
n
+
1
(
x
)
=
A
n
(
x
)
−
x
n
−
p
a
n
c
A
p
(
x
)
R
n
+
1
(
x
)
=
R
n
(
x
)
−
x
n
−
p
a
n
c
R
p
(
x
)
\begin{cases} A_{n+1}(x)=A_n(x)-x^{n-p}{a_n\over c}A_p(x)\\ R_{n+1}(x)=R_n(x)-x^{n-p}{a_n\over c}R_p(x) \end{cases}
{An+1(x)=An(x)−xn−pcanAp(x)Rn+1(x)=Rn(x)−xn−pcanRp(x)
而事实上唯一可行的解是
{
A
n
+
1
(
x
)
=
1
+
λ
x
n
+
1
(
λ
≠
0
)
R
n
+
1
(
x
)
=
a
n
x
n
\begin{cases} A_{n+1}(x)=1+\lambda x^{n+1} & (\lambda\ne 0) \\ R_{n+1}(x)=a_nx^n \end{cases}
{An+1(x)=1+λxn+1Rn+1(x)=anxn(λ=0)
毕竟我们需要满足
∣
A
n
+
1
(
x
)
∣
=
n
+
1
−
∣
A
n
(
x
)
∣
=
n
+
1
|A_{n+1}(x)|=n{+}1{-}|A_{n}(x)|=n+1
∣An+1(x)∣=n+1−∣An(x)∣=n+1 嘛。对比一下系数可知,
{
p
=
0
R
p
(
x
)
=
−
c
(
c
≠
0
)
A
p
(
x
)
=
x
\begin{cases} p=0\\ R_p(x)=-c & (c\ne 0)\\ A_p(x)=x \end{cases}
⎩
⎨
⎧p=0Rp(x)=−cAp(x)=x(c=0)
就是我们需要的初始转移点。其中 c c c 是任取的。由于在代码实现时, A p , R p A_p,R_p Ap,Rp 与 A n , R n A_n,R_n An,Rn 是分别存储的,此处 A 0 , R 0 A_0,R_0 A0,R0 矛盾的定义在代码实现中是可以被接受的。
六、示例代码
int a[MAXN], r[MAXN];
int BM(const int v[],int n){
static int ap[MAXN], rp[MAXN], p, invc;
static int an[MAXN], rn[MAXN]; // new
int lenp = 2, lenn = 1; p = 0;
rp[0] = ap[1] = 1, rp[1] = ap[0] = 0;
a[0] = 1, r[0] = 0; invc = MOD-1;
for(int i=0,d; i!=n; ++i){
for(int j=d=0; j!=lenn; ++j)
d = int((d+llong(a[j])*v[i-j])%MOD);
if(d == 0) continue; // keep it
const llong f = MOD-llong(d)*invc%MOD;
for(int j=i-p; j!=lenp+i-p; ++j){
an[j] = int((a[j]+f*ap[j-i+p])%MOD);
rn[j] = int((r[j]+f*rp[j-i+p])%MOD);
}
if(lenp+i-p > lenn){ // update p
memcpy(ap,a,lenn<<2), memcpy(rp,r,lenn<<2);
memcpy(a+i-p,an,lenp<<2), memcpy(r+i-p,rn,lenp<<2);
swap(lenp,lenn), lenn += i-p, p = i;
invc = int(qkpow(d,MOD-2));
}
else memcpy(a+i-p,an,lenp<<2), memcpy(r+i-p,rn,lenp<<2);
}
return lenn;
}
比 BM \textit{BM} BM 更快
可见于 EntropyIncreaser \textsf{EntropyIncreaser} EntropyIncreaser 的博客。因为我还没搞懂,因此讲不了。
常系数齐次线性递推
零、前言
在学废这个特征多项式的做法之后,我发现了 四、更好的做法
。推荐大家只阅读那个,而不需要在这个渣做法上浪费时间了。
Comment. 该方法在求解远处的连续值时倒是确实无可替代。
一、定义
形如
x
n
=
∑
i
=
1
k
a
i
x
n
−
i
x_{n}=\sum_{i=1}^{k}a_{i}x_{n-i}
xn=i=1∑kaixn−i
也就是上面的所谓线性递推式。
- 常系数:系数 a i a_i ai 不随 n n n 的改变而改变。
- 齐次线性: x q x^q xq 等于前面几项的 x q x^q xq 的线性和。
那么,定长(一定由前 k k k 项确定下一项)的含义在哪里呢 😂
二、特征方程
想必大家都看过数竞的小蓝本。其中有一种求递推通项公式的方法:特征方程。在这里,我也不便引入数竞的那一套;我将不加证明地说,下面的
f
(
x
)
f(x)
f(x) 是特征多项式,
f
(
x
)
=
0
f(x)=0
f(x)=0 就是特征方程。
f
(
x
)
=
x
k
−
∑
i
=
1
k
a
i
x
k
−
i
f(x)=x^k-\sum_{i=1}^{k}a_ix^{k-i}
f(x)=xk−i=1∑kaixk−i
特征方程 f ( x ) = 0 f(x)=0 f(x)=0 的含义就是,以 x x x 为公比的等比数列满足该递推式。
巧妙的是,如果我们用 矩阵 改写这个方程式,得到
f
(
A
)
=
A
k
−
∑
i
=
1
k
a
i
A
k
−
i
=
0
f(A)=A^k-\sum_{i=1}^{k}a_iA^{k-i}=0
f(A)=Ak−i=1∑kaiAk−i=0
这里 A A A 是一个矩阵, 0 0 0 代表全零矩阵。你会发现,这个方程的一个解就是转移矩阵。
简单证明一下。如果您对这一套有深入的了解,晓得 f ( x ) f(x) f(x) 真的是特征多项式,又晓得特征多项式是零化多项式,就不用管下面的证明了。下面的证明比较初等,但是正确。
不妨设状态向量 v i = ( x i , x i − 1 , x i − 2 , … , x i − k + 1 ) v_i=(x_i,x_{i-1},x_{i-2},\dots,x_{i-k+1}) vi=(xi,xi−1,xi−2,…,xi−k+1) 为列向量。转移矩阵的第一行是 a 1 , a 2 , a 3 , … , a k a_1,a_2,a_3,\dots,a_k a1,a2,a3,…,ak,以后的每一行都只有 A i ( i − 1 ) = 1 A_{i(i-1)}=1 Ai(i−1)=1 。
此时考虑矩阵的幂的意义,就是在有向图上走特定的步数,两个点之间有多少条路径。考虑从点 1 1 1 出发,如果你第一步从 1 1 1 走到 r r r,那么第二步只能是 r r r 到 ( r − 1 ) (r{-}1) (r−1),第三步到 ( r − 2 ) (r{-}2) (r−2),直到第 r r r 步回到 1 1 1 。这一路径的方案数是 a r a_r ar,由第一步带来的系数。
那么,我可以大胆地说,这就像 势能。那么枚举第一步(如果起点不是
1
1
1,那么第一步是走回
1
1
1 之后的第一步)获得的势能,一定满足
A
k
=
∑
i
=
1
k
a
i
A
k
−
i
A^k=\sum_{i=1}^{k}a_iA^{k-i}
Ak=i=1∑kaiAk−i
所以 f ( A ) = 0 f(A)=0 f(A)=0 得证。这个证明感觉不太严谨,仔细想想,又挺正确的……
接下来,根据多项式取模,可以得到
g
(
x
)
,
r
(
x
)
g(x),r(x)
g(x),r(x),满足
x
n
−
k
=
g
(
x
)
f
(
x
)
+
r
(
x
)
x^{n-k}=g(x)f(x)+r(x)
xn−k=g(x)f(x)+r(x)
此处可以使用快速幂,就是一个带取模的快速幂,计算
r
(
x
)
≡
x
n
−
k
(
m
o
d
f
(
x
)
)
r(x)\equiv x^{n-k}\pmod{f(x)}
r(x)≡xn−k(modf(x))
复杂度是
O
(
k
log
k
log
n
)
\mathcal O(k\log k\log n)
O(klogklogn) 的,还不算太差。令
x
=
A
x=A
x=A 则
A
n
−
k
=
g
(
A
)
f
(
A
)
+
r
(
A
)
A^{n-k}=g(A)f(A)+r(A)
An−k=g(A)f(A)+r(A)
直接改变了定义域,看上去是胆大妄为,实际上是很自然的。因为关于
A
A
A 矩阵的整式也构成环。而我们已知
f
(
A
)
=
0
f(A)=0
f(A)=0,所以我们得到重要等式
A
n
−
k
=
r
(
A
)
A^{n-k}=r(A)
An−k=r(A)
不妨设
r
(
x
)
r(x)
r(x) 的系数为
{
b
i
}
(
0
⩽
i
<
k
)
\{b_i\}\;(0\leqslant i<k)
{bi}(0⩽i<k),我们想要计算的是
A
n
−
k
v
k
→
A^{n-k}\overrightarrow{{\bf v}_k}
An−kvk 即
r
(
A
)
v
k
→
=
∑
i
=
0
k
−
1
b
i
A
i
v
k
→
r(A)\overrightarrow{{\bf v}_k}=\sum_{i=0}^{k-1}b_iA^i\overrightarrow{{\bf v}_k}
r(A)vk=i=0∑k−1biAivk
注意到
A
i
v
k
→
=
v
k
+
i
→
=
(
x
k
+
i
,
x
k
+
i
−
1
,
x
k
+
i
−
2
,
…
,
x
i
+
1
)
A^i\overrightarrow{{\bf v}_k}=\overrightarrow{{\bf v}_{k+i}}=(x_{k+i},x_{k+i-1},x_{k+i-2},\dots,x_{i+1})
Aivk=vk+i=(xk+i,xk+i−1,xk+i−2,…,xi+1),所以我们实际上要求的就是
r
(
A
)
v
k
→
=
∑
i
=
0
k
−
1
b
i
v
k
+
i
→
r(A)\overrightarrow{{\bf v}_k}=\sum_{i=0}^{k-1}b_i\overrightarrow{{\bf v}_{k+i}}
r(A)vk=i=0∑k−1bivk+i
而且我们只需要向量的首项,所以右侧实际上是 ∑ i = 0 k − 1 b i x k + i \sum_{i=0}^{k-1}b_ix_{k+i} ∑i=0k−1bixk+i 。计算这些项,可以用卷积,所以复杂度 O ( k log k ) \mathcal O(k\log k) O(klogk) 。
总时间复杂度是 O ( k log k log n ) \mathcal O(k\log k\log n) O(klogklogn) 的,吊打 O ( k 3 log n ) \mathcal O(k^3\log n) O(k3logn) 矩阵快速幂优化啊!
三、总结
我回看自己的博客,发现我不知道代码咋打 😢 赶紧总结一下。
对于递推 x n = ∑ i = 1 k a i x n − i x_{n}=\sum_{i=1}^{k}a_ix_{n-i} xn=∑i=1kaixn−i 而言,
- 第一步:令 f ( x ) = x k − ∑ i = 0 k − 1 a k − i x i f(x)=x^k-\sum_{i=0}^{k-1}a_{k-i}x^i f(x)=xk−∑i=0k−1ak−ixi 。
- 第二步:令 g ( x ) = x n − k m o d f ( x ) g(x)=x^{n-k}\bmod f(x) g(x)=xn−kmodf(x) ,求出 g ( x ) = ∑ i = 0 k − 1 b i x i g(x)=\sum_{i=0}^{k-1}b_ix^i g(x)=∑i=0k−1bixi 。
- 第三步: x n = ∑ i = 0 k − 1 b i x k + i x_n=\sum_{i=0}^{k-1}b_ix_{k+i} xn=∑i=0k−1bixk+i 。
四、更好的方法
毒瘤读毒瘤论文:以毒攻毒。
E
I
\sf EI
EI 搞了个新做法,据
Tiw-Air-OAO
\textsf{Tiw-Air-OAO}
Tiw-Air-OAO 称,好写并且常数小(即便无下文中的常数优化),值得信赖!
在
BM
\textit{BM}
BM 算法里讲过,线性递推等价于
A
(
x
)
f
(
x
)
=
R
(
x
)
A(x)f(x)=R(x)
A(x)f(x)=R(x),所以可以解出
f
(
x
)
=
R
(
x
)
A
(
x
)
f(x)={R(x)\over A(x)}
f(x)=A(x)R(x) 。更一般地,我们就是求
[
x
τ
]
A
(
x
)
B
(
x
)
(
[
x
0
]
B
(
x
)
≠
0
)
[x^\tau]{A(x)\over B(x)}\quad\Big([x^0]B(x)\ne 0\Big)
[xτ]B(x)A(x)([x0]B(x)=0)
上下同时乘以
B
(
−
x
)
B(-x)
B(−x),然后按照指数的奇偶性分类。即,存在形式幂级数
F
(
x
)
,
G
(
x
)
,
D
(
x
)
F(x),G(x),D(x)
F(x),G(x),D(x) 使得
{
A
(
x
)
B
(
−
x
)
=
x
F
(
x
2
)
+
G
(
x
2
)
B
(
x
)
B
(
−
x
)
=
D
(
x
2
)
\begin{cases} A(x)B(-x)=xF(x^2)+G(x^2)\\ B(x)B(-x)=D(x^2) \end{cases}
{A(x)B(−x)=xF(x2)+G(x2)B(x)B(−x)=D(x2)
毕竟
B
(
x
)
B
(
−
x
)
B(x)B(-x)
B(x)B(−x) 是偶函数,无奇数次项。于是有
A
(
x
)
B
(
x
)
=
x
⋅
F
(
x
2
)
D
(
x
2
)
+
G
(
x
2
)
D
(
x
2
)
\frac{A(x)}{B(x)}=x\cdot \frac{F(x^2)}{D(x^2)}+\frac{G(x^2)}{D(x^2)}
B(x)A(x)=x⋅D(x2)F(x2)+D(x2)G(x2)
若 2 ∤ n 2\nmid n 2∤n 则递归到求 [ x n − 1 2 ] F ( x ) D ( x ) [x^{n-1\over 2}]{F(x)\over D(x)} [x2n−1]D(x)F(x),否则递归到 [ x n 2 ] G ( x ) D ( x ) [x^{n\over 2}]{G(x)\over D(x)} [x2n]D(x)G(x) 。若 A ( x ) , B ( x ) A(x),B(x) A(x),B(x) 次数不超过 n n n,则 F ( x ) , D ( x ) F(x),D(x) F(x),D(x) 次数也不超过 n n n,复杂度 O ( n log n log τ ) \mathcal O(n\log n\log \tau) O(nlognlogτ) 没问题。
常数优化一
计算 B ( x ) B ( − x ) B(x)B(-x) B(x)B(−x) 时,无需两次正变换,因为 B ( − x ) B(-x) B(−x) 的对应位置的点值 B ( − ω n k ) = B ( ω n k + n 2 ) B(-\omega_n^k)=B(\omega_{n}^{k+{n\over 2}}) B(−ωnk)=B(ωnk+2n),即 B ( x ) B(x) B(x) 的点值的第 ( k + n 2 ) (k{+}\frac{n}{2}) (k+2n) 项。
常数优化二
求出 A ( x ) B ( − x ) A(x)B(-x) A(x)B(−x) 的点值表达式之后,我们只需要提取 F ( x ) F(x) F(x) 或 G ( x ) G(x) G(x) 其中之一。是否存在更快的逆变换方法?
记 δ ( x ) = A ( x ) B ( − x ) \delta(x)=A(x)B(-x) δ(x)=A(x)B(−x),那么只需求出 F ( x 2 ) = δ ( x ) + δ ( − x ) 2 F(x^2)=\frac{\delta(x)+\delta(-x)}{2} F(x2)=2δ(x)+δ(−x) 或 G ( x 2 ) = δ ( x ) − δ ( − x ) 2 G(x^2)=\frac{\delta(x)-\delta(-x)}{2} G(x2)=2δ(x)−δ(−x) 。看来得到 F ( x ) F(x) F(x) 的点值 F ( ω n k ) = δ ( ω 2 n k ) + δ ( ω 2 n k + n ) 2 F(\omega_n^k)=\frac{\delta(\omega_{2n}^{k})+\delta(\omega_{2n}^{k+n})}{2} F(ωnk)=2δ(ω2nk)+δ(ω2nk+n) 毫不费力,因为 δ ( x ) \delta(x) δ(x) 的点值是已知的。对 G ( x 2 ) G(x^2) G(x2) 同理。然后逆变换得到 F ( x ) F(x) F(x) 或 G ( x ) G(x) G(x) 就行了。逆变换的长度减小到了 1 2 1\over 2 21,可喜可贺!
当然,在 D ( x 2 ) = B ( x ) B ( − x ) D(x^2)=B(x)B(-x) D(x2)=B(x)B(−x) 中也可以使用这个技巧。
常数优化三
在 常数优化二
中,我们成功算出了
F
(
x
)
F(x)
F(x) 或
G
(
x
)
G(x)
G(x) 的点值表达式。可否压根儿就不做那逆变换,直接递归?同样地,我们也不做
Q
(
x
)
Q(x)
Q(x) 的逆变换,只保留点值?
仍然是求解 [ x n ] A ( x ) B ( x ) [x^n]{A(x)\over B(x)} [xn]B(x)A(x),但是 A ( x ) , B ( x ) A(x),B(x) A(x),B(x) 被表示为 ω n \omega_n ωn 的 DFT \textit{DFT} DFT 点值结果。为了求得 A ( x ) B ( − x ) A(x)B(-x) A(x)B(−x),需要得到 ω 2 n \omega_{2n} ω2n 的 DFT \textit{DFT} DFT 点值结果。似乎这并不需要从头再算,因为 A ( ω 2 n 2 k ) = A ( ω n k ) A(\omega_{2n}^{2k})=A(\omega_n^k) A(ω2n2k)=A(ωnk) 可直接得到。
想办法在
n
n
n 的长度内解决
ω
2
n
2
k
+
1
\omega_{2n}^{2k+1}
ω2n2k+1 。则必然要递归到
ω
n
k
\omega_n^k
ωnk 的点值。提出一个
ω
2
n
\omega_{2n}
ω2n 就行了!所以我们得到
A
(
ω
2
n
2
k
+
1
)
=
∑
i
(
ω
2
n
i
a
i
)
⋅
ω
n
i
k
A(\omega_{2n}^{2k+1})=\sum_{i}(\omega_{2n}^ia_i)\cdot \omega_{n}^{ik}
A(ω2n2k+1)=i∑(ω2niai)⋅ωnik
其中 a i = [ x i ] A ( x ) a_i=[x^i]A(x) ai=[xi]A(x) 。只需要先做长为 n n n 的 IDFT \textit{IDFT} IDFT,将系数 a i a_i ai 乘上 ω 2 n i \omega_{2n}^i ω2ni,然后再做一次 DFT \textit{DFT} DFT 就结束了。
审视一下目前得到的方法:每次递归只需要做长度为 n n n 的 IDFT \textit{IDFT} IDFT 和 DFT \textit{DFT} DFT 各两次(对于 A ( x ) A(x) A(x) 和 B ( x ) B(x) B(x) 都需要点值表达式的长度翻倍),然后是两次长度为 2 n 2n 2n 的点值乘法和加法,再没别的!多么清爽的做法!
注记
其实多项式取模和多项式求逆(多项式除法)的本质相同,因为 O ( n log n ) \mathcal O(n\log n) O(nlogn) 的多项式取模不就是先求逆么?