$\text{Berlekamp–Massey}$ 算法
该算法可以在 \(O(n^2)\) 的复杂度内求解线性递推数列的递推式。其中 \(n\) 为递推式阶数。此法常用于 打表找规律 。
接下来具体讲解一下算法流程。
考虑一个很 \(\text{naive}\) 的想法:假设我们已经求解出了一个递推式 \(g\) ,使得对于 \(\forall i<n\) 都满足 \(f_{j}=\sum_\limits{j=1}^ka_jf_{n-j}\) 。现在我们来让其能够对 \(n\) 也可以满足。分成两种情况:一种是按照这个递推式就是正确的,那就不用管;如果按照当前的递推式和第 \(n\) 项实际值不一样,那就考虑加上一些修正,使得其将 \(n\) 代入时结果与实际一致。
假设现在计算值和实际值的差值为 \(s\) :
\[\sum_{j=1}^ka_jf_{n-j}-f_n=s
\]
之前上一个失配的位置 \(m\) ,满足:
\[\sum_{j=1}^{k'}a^{'}_{j}f_{m-j}-f_m=t
\]
其中 \(a'\) 为当时失配需要修正的递推式。这个式子有一个非常优美的性质:当时这个递推式符合第 \(m\) 项之前的每一项,也就是对于 \(\forall i<m\) 上面式子的取值都为 \(0\) ,恰好只在取值为 \(m\) 时有值。
考虑将其应用到第 \(n\) 项的修正中,让原先在 \(m\) 处的取值贡献到 \(n\) 上,\(m-1\) 处的取值贡献到 \(n-1\) 上,以此类推。由于只在 \(m\) 处取值非零,因此只会影响到当前需要修正的这一项,不会改变之前项的正确性。
具体修正:
\[f_n=\sum_{j=1}^ka_jf_{n-j}+s
\]
\[f_n=\sum_{j=1}^ka_jf_{n-j}+\frac{s}{t}\cdot t
\]
\[f_n=\sum_{j=1}^ka_jf_{n-j}+\frac{s}{t}\left(\sum_{j=1}^{k'}a_j^{'}f_{m-j}-f_m\right)
\]
将递推式对应为相加减即可。
点击查看代码
inline vc<int> BM(int n,int *f) {
vc<int> a[n+2],val; val.resize(n+2); int lst=0,s,t,fr;
for(int i=1;i<=n;i++) {
s=P-f[i];
for(int j=0;j<a[i].size();j++) inc(s,1ll*a[i][j]*f[i-j-1]%P);
val[i]=s; t=val[lst];
if(!s) { a[i+1]=a[i]; continue; }
if(!lst) { lst=i; a[i].pb(0); a[i+1]=a[i]; continue; }
fr=1ll*s*inv(t)%P;
while(a[i].size()<i-lst+a[lst-1].size()) a[i].pb(0);
inc(a[i][i-lst-1],fr);
for(int j=0;j<a[lst-1].size();j++) dec(a[i][i-lst+j],1ll*fr*a[lst-1][j]%P);
lst=i; a[i+1]=a[i];
}
return a[n];
}

浙公网安备 33010602011771号