Berlekamp-Massey 算法(求数列的最短递推式)

本文介绍了一种通过增量法和失配调整求解数列最短递推式的技巧,涉及历史递推式、差值计算和递推式优化,适用于解决数列规律问题。关键步骤包括计算真实值与递推式预测值的差,根据失配调整新的递推公式,寻找最短的调整方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

用于求数列的最短递推式。

本文参考自 https://www.cnblogs.com/jz-597/p/14983564.html

增量法,设 R i R_i Ri 表示第 i i i 个历史递推式,当前为 R c n t R_{cnt} Rcnt

Δ \Delta Δ 表示真实的 a i a_i ai 与用 R c n t R_{cnt} Rcnt 求出的 a i a_i ai 的差值,即 Δ = a i − ∑ j = 1 ∣ R c n t ∣ a i − j R c n t , j \Delta=a_i-\sum_{j=1}^{|R_{cnt}|}a_{i-j}R_{cnt,j} Δ=aij=1RcntaijRcnt,j

  • Δ = 0 \Delta=0 Δ=0,则无事发生,跳过。
  • Δ ≠ 0 \Delta\neq 0 Δ=0,那么我们需要调整这个递推式使得它对 a i a_i ai 也成立。

接下来看第二种情况怎么处理。

有一个想法是找到另一个递推式,使得这个递推式在算 ⋯   , a i − 1 \cdots,a_{i-1} ,ai1 时取 0 0 0,在算 a i a_i ai 时取 Δ \Delta Δ

我们可以找到一个历史递推式 R l s t R_{lst} Rlst,设其失配位置为 f a i l l s t fail_{lst} faillst,失配时的差值为 Δ l s t \Delta_{lst} Δlst(显然 Δ l s t ≠ 0 \Delta_{lst}\neq 0 Δlst=0)。

d = Δ Δ l s t d=\dfrac{\Delta}{\Delta_{lst}} d=ΔlstΔ,那么我们可以找到这么一个递推式 R ′ = { 0 , 0 , ⋯   , 0 , d , − d ⋅ R l s t , 1 , − d ⋅ R l s t , 2 , ⋯   } R'=\{0,0,\cdots,0,d,-d\cdot R_{lst,1},-d\cdot R_{lst,2},\cdots\} R={0,0,,0,d,dRlst,1,dRlst,2,},其中前面是 i − f a i l l s t − 1 i-fail_{lst}-1 ifaillst1 0 0 0

可以发现,使用这个递推式得到的 a i ′ = ∑ j = 1 ∣ R ′ ∣ R j ′ a i − j = d ( a f a i l l s t − ∑ j = 1 ∣ R l s t ∣ R l s t , j a f a i l l s t − j ) = d ⋅ Δ l s t = Δ a_i'=\sum_{j=1}^{|R'|}R'_{j}a_{i-j}=d(a_{fail_{lst}}-\sum_{j=1}^{|R_{lst}|}R_{lst,j}a_{fail_{lst}-j})=d\cdot \Delta_{lst}=\Delta ai=j=1RRjaij=d(afaillstj=1RlstRlst,jafaillstj)=dΔlst=Δ

而对于任意 ( i − k ) ∈ [ ∣ R ′ ∣ + 1 , i − 1 ] (i-k)\in [|R'|+1,i-1] (ik)[R+1,i1] a i − k ′ = ∑ j = 1 ∣ R ′ ∣ R j ′ a ( i − k ) − j = d ( a f a i l l s t − k − ∑ j = 1 ∣ R l s t ∣ R l s t , j a ( f a i l l s t − k ) − j ) = 0 a_{i-k}'=\sum_{j=1}^{|R'|}R'_j{a_{(i-k)-j}}=d(a_{fail_{lst}-k}-\sum_{j=1}^{|R_{lst}|}R_{lst,j}a_{(fail_{lst}-k)-j})=0 aik=j=1RRja(ik)j=d(afaillstkj=1RlstRlst,ja(faillstk)j)=0

那么我们令 R c n t + 1 ← R c n t + R ′ R_{cnt+1}\gets R_{cnt}+R' Rcnt+1Rcnt+R 即可。

实质上是利用了以前的信息 R l s t R_{lst} Rlst:注意到失配前的差值都是 0 0 0,所以我们用差值来定义递推式。

那么我们怎么找到最短的 R ′ R' R 呢?由于 ∣ R ′ ∣ = i + ( ∣ R l s t ∣ − f a i l l s t ) |R'|=i+\big(|R_{lst}|-fail_{lst}\big) R=i+(Rlstfaillst),所以我们直接找 ∣ R l s t ∣ − f a i l l s t |R_{lst}|-fail_{lst} Rlstfaillst 最小的就好了。

至于为什么求出来的递推式一定是最短的不会证。

【XSY3403】求数列的最短递推式 代码:

#include<bits/stdc++.h>

#define N 10010

using namespace std;

namespace modular
{
    const int mod=1000000007;
    inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
    inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
    inline int mul(int x,int y){return 1ll*x*y%mod;}
    inline void Add(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
}using namespace modular;

inline int poww(int a,int b)
{
    int ans=1;
    while(b)
    {
        if(b&1) ans=mul(ans,a);
        a=mul(a,a);
        b>>=1;
    }
    return ans;
}

inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^'0');
        ch=getchar();
    }
    return x*f;
}

int n,a[N],delta[N],fail[N];

vector<int>r[N];

int main()
{
    n=read();
    for(int i=1;i<=n;i++) a[i]=read();
    int cnt=0,lst=cnt;
    for(int i=1;i<=n;i++)
    {
        delta[cnt]=0;
        for(int j=0;j<r[cnt].size();j++)
            Add(delta[cnt],mul(r[cnt][j],a[i-j-1]));
        delta[cnt]=dec(a[i],delta[cnt]);
        if(!delta[cnt]) continue;
        fail[cnt]=i;
        if(!cnt)
        {
            r[++cnt].resize(i);
            continue;
        }
        r[cnt+1]=r[cnt];
        r[cnt+1].resize(max((int)r[cnt+1].size(),i+(int)r[lst].size()-fail[lst]));
        int d=mul(delta[cnt],poww(delta[lst],mod-2));
        Add(r[cnt+1][i-fail[lst]-1],d);
        for(int j=0;j<r[lst].size();j++)
            Add(r[cnt+1][i-fail[lst]+j],mul(dec(0,d),r[lst][j]));
        if((int)r[cnt].size()-fail[cnt]<(int)r[lst].size()-fail[lst]) lst=cnt;
        cnt++;
    }
    printf("%d\n1000000006 ",(int)r[cnt].size());
    for(int i=0;i<r[cnt].size();i++)
        cout<<r[cnt][i]<<" \0"[i==r[cnt].size()-1];
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值