【学习笔记】BM算法和线性递推

280 篇文章 1 订阅

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 pm ∑ i = 0 m a p − i r i = 0 \sum_{i=0}^{m}a_{p-i}r_i=0 i=0mapiri=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) (m1) 的多项式 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} xnpdc1
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}} xnpdc1Ap(x)f(x)xnpdc1Rp(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)xnpdc1Ap(x)]f(x)Rn(x)xnpdc1Rp(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)cdxnpAp(x)Rn+1(x)=Rn(x)cdxnpRp(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)An1(x) 是显然的。再结合下面的引理,就可以说明最短性了。

Lemma:若 ∣ A n ( x ) ∣ > ∣ A n − 1 ( x ) ∣ |A_n(x)|>|A_{n-1}(x)| An(x)>An1(x),则最短线性递推式长度 ∣ A n ( x ) ∣ ⩾ n − ∣ A n − 1 ( x ) ∣ |A_n(x)|\geqslant n-|A_{n-1}(x)| An(x)nAn1(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)n1An1(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)] [1An(x)] x x x 的倍数,同乘于 A n − 1 ( x ) A_{n-1}(x) An1(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} An1(x)f(x)Rn1(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} [1An(x)]An1(x)f(x)[1An(x)]Rn1(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)]An1(x)Rn1(x)An(x)Rn1(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} An1(x)f(x)Rn1(x)+An1(x)Rn(x)An(x)Rn1(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}} {An1(x)f(x)Rn1(x)An(x)f(x)Rn(x)(modxn1)An1(x)Rn(x)An(x)Rn1(x)0(modxn1)

联系我们的前提条件
∣ 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 An1(x)Rn(x)An(x)Rn1(x)<An(x)+An1(x)n1

可知此处取模毫无意义,二者就是相等。代入 ( 3 ) (3) (3) 式即知 A n − 1 ( x ) , R n − 1 ( x ) A_{n-1}(x),R_{n-1}(x) An1(x),Rn1(x) 是取模 x n x^{n} xn 意义下的合法解,与前提条件 ∣ A n ( x ) ∣ > ∣ A n − 1 ( x ) ∣ |A_n(x)|>|A_{n-1}(x)| An(x)>An1(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+1An(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),np+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)| np+Ap(x)=np+p+1Ap+1(x)=n+1An(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+1A(x)2m+1m=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==an1=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)xnpcanAp(x)Rn+1(x)=Rn(x)xnpcanRp(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+1An(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=1kaixni

也就是上面的所谓线性递推式。

  • 常系数:系数 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)=xki=1kaixki

特征方程 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)=Aki=1kaiAki=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,xi1,xi2,,xik+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(i1)=1

此时考虑矩阵的幂的意义,就是在有向图上走特定的步数,两个点之间有多少条路径。考虑从点 1 1 1 出发,如果你第一步从 1 1 1 走到 r r r,那么第二步只能是 r r r ( r − 1 ) (r{-}1) (r1),第三步到 ( r − 2 ) (r{-}2) (r2),直到第 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=1kaiAki

所以 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) xnk=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)xnk(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) Ank=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) Ank=r(A)

不妨设 r ( x ) r(x) r(x) 的系数为 { b i }    ( 0 ⩽ i < k ) \{b_i\}\;(0\leqslant i<k) {bi}(0i<k),我们想要计算的是 A n − k v k → A^{n-k}\overrightarrow{{\bf v}_k} Ankvk
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=0k1biAivk

注意到 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+i1,xk+i2,,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=0k1bivk+i

而且我们只需要向量的首项,所以右侧实际上是 ∑ i = 0 k − 1 b i x k + i \sum_{i=0}^{k-1}b_ix_{k+i} i=0k1bixk+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=1kaixni 而言,

  • 第一步:令 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)=xki=0k1akixi
  • 第二步:令 g ( x ) = x n − k   m o d   f ( x ) g(x)=x^{n-k}\bmod f(x) g(x)=xnkmodf(x) ,求出 g ( x ) = ∑ i = 0 k − 1 b i x i g(x)=\sum_{i=0}^{k-1}b_ix^i g(x)=i=0k1bixi
  • 第三步: x n = ∑ i = 0 k − 1 b i x k + i x_n=\sum_{i=0}^{k-1}b_ix_{k+i} xn=i=0k1bixk+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)=xD(x2)F(x2)+D(x2)G(x2)

2 ∤ n 2\nmid n 2n 则递归到求 [ x n − 1 2 ] F ( x ) D ( x ) [x^{n-1\over 2}]{F(x)\over D(x)} [x2n1]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) 的多项式取模不就是先求逆么?

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值