Berlekamp-Massey算法学习笔记

用途

\(O(n^2)\)求解一个长度为\(n\)的数列的最短线性递推式。

一般可以用于猜结论/骗分。

思想

从左到右依次扫过去,每当出现一个元素不符合原来的递推式时就修正它,得到新的递推式。

记当前已经有的递推式共有\(cnt​\)个,第\(i​\)个递推式出错的位置是\(fail_i​\),出错时原数列与算出的结果的差是\(delta_i​\),第\(i​\)个递推式记作\(R_i​\),把\(R​\)代入\(i​\)位置算出的结果为\(calc(i,R)​\),于是有\(delta_i=a_{fail_i}-calc(fail_i,R_i)​\)

一开始的递推式是\(R_0=\{\}\),也就是空数列,\(cnt\)也为0。

\(R_{cnt}\)在第\(i\)个位置出错了,那么可以得到\(delta_{cnt}\),记\(fail_{cnt}=i\)

首先特判\(cnt=0\)。若\(cnt=0\),也就是之前一直是全\(0\)数列,那么直接设\(R_1=\{0,0,\cdots,0\}\),也就是用\(i\)\(0\)填充,然后接着往后扫。

否则,把之前的某个\(R\)搬过来作为基准(记为\(R_p\))设\(mul=\frac{delta_{cnt}}{delta_p}\)

请注意:\(p\)不能简单地取\(cnt-1\),否则不能保证递推式最短。

(hack方式:n=10,a={1,2,3,4,5,1,2,3,4,5},错误代码的递推式将会非常难看,而正确答案是0,0,0,0,1

现在希望得到一个\(R'​\),使得\(R'​\)\(j<fail_{cnt}​\)时有\(calc(j,R')=0​\),且刚好有\(calc(fail_{cnt},R')=delta_i​\),那么就可以得到新数列\(R_{cnt+1}=R_{cnt}+R'​\)了。

\(R'​\)怎么求?考虑把\(R_p​\)搬过来,并在左边加上一个-1,也就是\(\{-1,R_p\}​\)。容易发现,它在\(j< fail_p​\)时有\(calc(j,R')=0​\),并且\(calc(fail_{p},R')=-delta_{p}​\)。那么把\(R'​\)再乘一个\(-mul​\)就可以使得\(calc(fail_{p},R')=delta_{cnt}​\)

但是它的位置好像有一些不对?我们想要的是\(calc(fail_{cnt},R')=delta_{cnt}\)啊。

这个简单。只需要再在数列左边添上\(fail_{cnt}-fail_p-1​\)个0,相当于是平移了一下。

于是最后得到\(R'=\{0,0,\cdots,0,mul,-mul\times R_{cnt-1}\}\),令\(R_{cnt+1}=R_{cnt}+R'\),就完成了更新。

那么\(p\)究竟应该如何取呢?需要选取一个加完0之后长度最短的递推式,具体见代码。

至于为什么这样一定是最短的递推式,我也不知道qwq

复杂度:最坏情况下要更新\(n\)次,所以复杂度\(O(n^2)\)

代码

#include<bits/stdc++.h>
clock_t t=clock();
namespace my_std{
    using namespace std;
    #define pii pair<int,int>
    #define fir first
    #define sec second
    #define MP make_pair
    #define rep(i,x,y) for (int i=(x);i<=(y);i++)
    #define drep(i,x,y) for (int i=(x);i>=(y);i--)
    #define go(x) for (int i=head[x];i;i=edge[i].nxt)
    #define templ template<typename T>
    #define sz 2020
    typedef long long ll;
    typedef double db;
    mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
    templ inline T rnd(T l,T r) {return uniform_int_distribution<T>(l,r)(rng);}
    templ inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
    templ inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
    templ inline void read(T& t)
    {
        t=0;char f=0,ch=getchar();double d=0.1;
        while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
        while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
        if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
        t=(f?-t:t);
    }
    template<typename T,typename... Args>inline void read(T& t,Args&... args){read(t); read(args...);}
    char __sr[1<<21],__z[20];int __C=-1,__zz=0;
    inline void Ot(){fwrite(__sr,1,__C+1,stdout),__C=-1;}
    inline void print(register int x)
    {
        if(__C>1<<20)Ot();if(x<0)__sr[++__C]='-',x=-x;
        while(__z[++__zz]=x%10+48,x/=10);
        while(__sr[++__C]=__z[__zz],--__zz);__sr[++__C]='\n';
    }
    void file()
    {
        #ifdef NTFOrz
        freopen("a.in","r",stdin);
        #endif
    }
    inline void chktime()
    {
        #ifndef ONLINE_JUDGE
        cout<<(clock()-t)/1000.0<<'\n';
        #endif
    }
    #ifdef mod
    ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod;return ret;}
    ll inv(ll x){return ksm(x,mod-2);}
    #else
    ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x) if (y&1) ret=ret*x;return ret;}
    #endif
//  inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std;

int n;
db a[sz];
int fail[sz],cnt;
vector<db>R[sz];
db delta[sz];

int main()
{
    file();
    read(n);
    rep(i,1,n) read(a[i]);
    int bst=0;
    rep(i,1,n)
    {
        db cur=a[i];
        rep(j,0,(int)R[cnt].size()-1) cur-=R[cnt][j]*a[i-j-1];
        if (fabs(cur)<1e-7) continue;
        delta[cnt]=cur;fail[cnt]=i;++cnt;
        if (cnt==1){R[cnt].resize(i);continue;}
        db mul=delta[cnt-1]/delta[bst];
        vector<db>tmp; 
        tmp.resize(i-fail[bst]-1);tmp.push_back(mul);
        rep(j,0,(int)R[bst].size()-1) tmp.push_back(-R[bst][j]*mul);
        R[cnt]=tmp;if (R[cnt-1].size()>tmp.size()) R[cnt].resize(R[cnt-1].size());
        rep(j,0,(int)R[cnt-1].size()-1) R[cnt][j]+=R[cnt-1][j];
        if (i-fail[bst]+R[bst].size()>R[cnt-1].size()) bst=cnt-1;
    }
    rep(i,0,(int)R[cnt].size()-1) printf("%.5lf ",R[cnt][i]);
    return 0;
}

转载于:https://www.cnblogs.com/p-b-p-b/p/10844127.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值