Berlekamp-Massey算法学习笔记

Berlekamp-Massey算法学习笔记

声明:博主已退役,这是以前的总结,如有错误望指正,如有问题不妨看看别人的博客

问题描述

给一个序列({a_0,a_1,...,a_n})

要求最短的序列({f_0,f_1,...,f_m})

使得对于所有(i>m)

[a_i=sum_{k=0}^{m}f_k*a_{i-1-k} ]

算法流程

主要思想是依次考虑每个(a_i)

不断修改({f})

使得其在保证正确的同时尽量短

一开始({f})为空

对每个(a_i)判断当前递推式是否满足条件

如果满足就直接判断下一个

否则需要修改

如果当前({f})为空

说明(a_i)之前数全是(0)

(a_i ot=0)

所以是不可能用之前的数推出(a_i)

这种情况下直接把(f_0,f_1,...f_i)记为(0)

这样(ile m)就不用推了

否则说明之前已经有一个错误的({f})

为了方便记为({F})

我们希望能通过({F})(i)位推出一个不为(0)的值

然后把这次的错误抵消掉

如果({F})出错的位置是(p)且多了(Delta)

这个时候(a_p)一定不等于(sum_{k=0}^{m}F_k*a_{i-k})

就可以对({F})稍作修改

(a_i)的位置上递推出一个(-Delta)

具体来说

({F})全部变为相反数再在最前面补(1)

得到的新的({F})可以满足在(p+1)位置推出一个(-Delta)

再在({F})最前面补(i-p-1)(0)

(-Delta)就跑到(i)位置来了

把现在得到的({F})除以(Delta)再乘上这次的差

加上({f})就可以把这次的差抵消掉

因为要求递推式最短

我们希望每次能得到最优的({F})

考虑每次修改时({f})的长度变化

变成了(max(len(f),len(F)+i-p))

所以只要记录(len(F)-p)最短的({F})即可

还有Berlekamp-Massey返回的递推式的长度最好要在输入的一半以内

不然还是再多打点表吧

为什么?

感谢LHJ神仙指教

这样多出来的一半就可以列出至少(len(F))个方程组

确定了递推式的唯一性

代码

#include<bits/stdc++.h>

using namespace std;

#define ll long long

const int p=1e9+7;

inline int add(int a,int b){
    return (a+=b)>=p?a-p:a;
}

inline int sub(int a,int b){
    return (a-=b)<0?a+p:a;
}

inline ll qpow(ll a,ll b){
	ll ans=1;
	for(;b;b>>=1,(a*=a)%=p)if(b&1)(ans*=a)%=p;
	return ans;
}

namespace BerlekampMassey{

	typedef vector<int> poly;

	#define len(A) A.size()

	inline poly BM(const poly& A){
		poly F,F0;
		int d0,p0;
		for(int i=0;i<len(A);++i){
			int d=0;
			for(int j=0;j<len(F);++j){
				d=add(d,(ll)F[j]*A[i-j-1]%p);
			}
			d=sub(d,A[i]);
			if(!d)continue;
			if(!len(F)){
				F.resize(i+1);
				d0=d;
				p0=i;
				continue;
			}
			ll t=qpow(d0,p-2)*d%p;
			poly G(i-p0-1);
			G.push_back(t);
			t=sub(0,t);
			for(int j=0;j<len(F0);++j){
				G.push_back(F0[j]*t%p);
			}
			if(len(G)<len(F))G.resize(len(F));
			for(int j=0;j<len(F);++j){
				G[j]=add(G[j],F[j]);
			}
			if(i-p0+len(F0)>=len(F)){
                //注意这里不要把i移项,vector的size是unsigned类型,减成负的就凉了
				F0=F;
				d0=d;
				p0=i;
			}
			F=G;
		}
		return F;
	}
}
using namespace BerlekampMassey;

int F[]={1,2,4,9,20,40,90};

int main(){
	poly G(F,F+((sizeof F)>>2));
	G=BM(G);
	printf("%d
",G.size());
	for(int i=0;i<G.size();++i)printf("%d ",G[i]);
}

然后遇到一个题就可以Berlekamp-Massey+矩阵快速幂水过

当然多项式取模优化线性递推更好

毕竟Berlekamp-Massey的复杂度是(O(k^2))

矩阵快速幂的复杂度是(O(k^3log n))

综合代码效率和实现难度

(O(k^2log n))多项式取模搭配最佳

虽然我觉得不太可能

但也不排除(k)比较大

必须本地跑出递推式然后上(O(k log k log n))多项式取模的情况

#include<bits/stdc++.h>

using namespace std;

#define gc c=getchar()
#define r(x) read(x)
#define ll long long

template<typename T>
inline void read(T&x){
    x=0;T k=1;char gc;
    while(!isdigit(c)){if(c=='-')k=-1;gc;}
    while(isdigit(c)){x=x*10+c-'0';gc;}x*=k;
}

const int p=1e9+7;

inline int add(int a,int b){
	a+=b;
	if(a>=p)a-=p;
	return a;
}

inline int sub(int a,int b){
	a-=b;
	if(a<0)a+=p;
	return a;
}

inline ll qpow(ll a,ll b){
	ll ans=1;
	for(;b;b>>=1,(a*=a)%=p)if(b&1)(ans*=a)%=p;
	return ans;
}

namespace BerlekampMassey{

	typedef vector<int> poly;
	
	#define len(A) A.size()
	
	inline poly mul(const poly &A,const poly&B,const poly&F){
		poly ret(len(F)*2-1);
		for(int i=0;i<len(F);++i){
			for(int j=0;j<len(F);++j){
			    ret[i+j]=add(ret[i+j],(ll)A[i]*B[j]%p);
			}
		}
		for(int i=len(F)*2-2;i>=len(F);--i){
			for(int j=0;j<len(F);++j){
			    ret[i-j-1]=add(ret[i-j-1],(ll)ret[i]*F[j]%p);
			}
		}
		ret.resize(len(F));
		return ret;
	}
	
	inline int solve(const poly &A,const poly &F,ll n){
		if(n<len(A))return A[n];
		poly base(len(F)),ans(len(F));
		base[1]=ans[0]=1;
		for(;n;n>>=1){
			if(n&1)ans=mul(ans,base,F);
			base=mul(base,base,F);
		}
		int ret=0;
		for(int i=0;i<len(F);++i)ret=add(ret,(ll)A[i]*ans[i]%p);
		return ret;
	}
	
	inline poly BM(const poly& A){
		poly F,F0;
		int d0,p0;
		for(int i=0;i<len(A);++i){
			int d=0;
			for(int j=0;j<len(F);++j){
				d=add(d,(ll)F[j]*A[i-j-1]%p);
			}
			d=sub(d,A[i]);
			if(!d)continue;
			if(!len(F)){
				F.resize(i+1);
				d0=d;
				p0=i;
				continue;
			}
			ll t=qpow(d0,p-2)*d%p;
			poly G(i-p0-1);
			G.push_back(t);
			t=sub(0,t);
			for(int j=0;j<len(F0);++j){
				G.push_back(F0[j]*t%p);
			}
			if(len(G)<len(F))G.resize(len(F));
			for(int j=0;j<len(F);++j){
				G[j]=add(G[j],F[j]);
			}
			if(i-p0+len(F0)>=len(F)){
				F0=F;
				d0=d;
				p0=i;
			}
			F=G;
		}
		return F;
	}
	
	inline int main(const poly &A,ll n){
		return solve(A,BM(A),n);
	}
}

int F[]={0,1,1,2,3,5,8,13};

int main(){
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	ll n;r(n);
	printf("%d
",BerlekampMassey::main(vector<int>(F,F+(sizeof(F))/4),n));
}

原文地址:https://www.cnblogs.com/yicongli/p/11143017.html