[学习笔记]BSGS

$\operatorname{BSGS}$ ,也即 $Baby\; step\; Giant\; step$ 大步小步算法,可以在 $\Theta(\sqrt{p})$ 的时间内求解

$$a^x\equiv b\pmod{p}$$

的问题,其中 $a,p$ 互质(也即 $a\perp p$)

那么首先显然地,$a^0\equiv 1\pmod{p}$;

又根据欧拉定理, $a^{φ(p)}\equiv 1\pmod{p}$

不难看出, $0\sim φ(p)$ 是一个循环节

也就是说,最多循环 $φ(p)$ 次就可以找到答案,否则无解

不妨设 $x=i\times m-k$,其中 $k\in [0,m]$

原方程变为 $a^{i\times m-k}\equiv b\pmod{p}$

移项,$a^{i\times m}\equiv a^kb\pmod{p}$

可以先计算 $a^kb\pmod{p}$ 的值,存入哈希表/ $\operatorname{map}$ 中

然后枚举可能的 $i$ 值,计算 $a^{i\times m}$ 的值进行查询

为什么要使用 $i\times m-k$ 而不是 $i\times m+k$ 呢?

事实上,两种方法都可行,但我们选择减法这是因为可以通过移项避免求逆元

也就是说,$\operatorname{BSGS}$ 算法共分为两步:

$\qquad\mathfrak{1:}$ $[0,m]$ 枚举 $k$,将 $a^kb\mod p$ 的值存入哈希表中

$\qquad\mathfrak{2:}$ $[1,\left \lceil \frac{p}{m} \right \rceil]$ 枚举 $i$,将 $a^{i\times m}\mod p$ 在表中进行查询,如果存在,此时的 $i\times m-k$ 即为答案

模板题目:TJOI2007可爱的质数

另外请特别注意,当 $a\mod p==0$ 且 $b\mod p \neq 0$ 时原方程无解(代码里好像没加)

 

#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<map>
#define WR WinterRain
#define int long long
using namespace std;
const int WR=5010,INF=1099588621776;
int BL,ans;
map<int,int>mp;
int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=(s<<1)+(s<<3)+ch-48;
        ch=getchar();
    }
    return s*w;
}
int quick_pow(int a,int b,int mod){
    int base=a,res=1;
    while(b){
        if(b&1) res=res*base%mod;
        base=base*base%mod;
        b>>=1;
    }
    return res;
}
void baby_step(int a,int b,int p){
    int pw=0;
    while(pw<BL){
        if(!mp[b]) mp[b]=pw+1;
        b=b*a%p,pw++;
    }
}
void giant_step(int a,int b,int p){
    int pw=1,base,rec;
    base=rec=quick_pow(a,BL,p);
    while(pw<=BL){
        if(mp[base]) ans=min(ans,BL*pw-mp[base]+1);
        base=base*rec%p,pw++;
    }
}
signed main(){
    int p=read(),a=read(),b=read();
    BL=ceil(sqrt(p));ans=INF;
    // printf("%lld\n",BL);
    baby_step(a,b,p);
    giant_step(a,b,p);
    if(ans==INF) printf("no solution\n");
    else printf("%lld\n",ans);
    return 0;
}
View Code

 

那么为什么不更进一步,考虑一下矩阵的 $\operatorname{BSGS}$ 呢?

显然对于矩阵,想做除法是需要求逆的,并不具备实际的可操作性

但是由于上面我们并没有用到除法(指数层面的减法避免了逆元),所以硬刚即可

 

#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<map>
#define WR WinterRain
#define int long long
using namespace std;
const int INF=1099588621776;
const unsigned int hash1=345431,hash2=135757;
int n,mod,BL;
int ans=INF;
struct Matrix{
    int num[101][101];
    unsigned int val1,val2;
    void clr(){memset(num,0,sizeof(num));val1=val2=0;}
    Matrix operator=(Matrix b){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                num[i][j]=b.num[i][j];
            }
        }
        val1=b.val1,val2,b.val2;
        return *this;
    }
    Matrix operator*(const Matrix &b)const{
        Matrix res;res.clr();
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                for(int k=1;k<=n;k++){
                    res.num[i][j]=(res.num[i][j]+num[i][k]*b.num[k][j]%mod)%mod;
                }
            }
        }
        return res;
    }
    Matrix operator*=(const Matrix &b){*this=*this*b;return *this;}
    void get_hash(){
        val1=0,val2=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                val1=val1*hash1+(unsigned int)num[i][j];
                val2=val2*hash2+(unsigned int)num[i][j];
            }
        }
    }
    void print(){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                printf("%lld ",num[i][j]);
            }
            printf("\n");
        }
        printf("%llu %llu\n",val1,val2);
    }
}a,b;
map<pair<unsigned int,unsigned int>,int>mp;
int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=(s<<1)+(s<<3)+ch-48;
        ch=getchar();
    }
    return s*w;
}
Matrix quick_pow(Matrix a,int b,int mod){
    Matrix base,res;
    base=a;res.clr();
    for(int i=1;i<=n;i++) res.num[i][i]=1;
    while(b){
        if(b&1) res*=base;
        base*=base;
        b>>=1;
    }
    return res;
}
void baby_step(Matrix a,Matrix b,int mod){
    mp.clear();int pw=0;
    while(pw<BL){
        b.get_hash();
        // b.print();
        if(!mp[make_pair(b.val1,b.val2)]) mp[make_pair(b.val1,b.val2)]=pw+1;
        b*=a;pw++;
    }
}
void giant_step(Matrix a,Matrix b,int mod){
    ans=INF;int pw=1;
    Matrix base,rec;
    base=quick_pow(a,BL,mod);rec=base;
    base.get_hash();
    while(pw<=BL){
        base.get_hash();
        // base.print();
        if(mp[make_pair(base.val1,base.val2)]) ans=min(ans,pw*BL-mp[make_pair(base.val1,base.val2)]+1);
        base*=rec;pw++;
    }
}
signed main(){
    n=read(),mod=read();BL=ceil(sqrt(mod));
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            a.num[i][j]=read();
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            b.num[i][j]=read();
        }
    }
    a.get_hash(),b.get_hash();
    baby_step(a,b,mod);
    giant_step(a,b,mod);
    printf("%lld\n",ans);
    return 0;
}
View Code
posted @ 2022-08-14 10:58  冬天丶的雨  阅读(51)  评论(0编辑  收藏  举报
Live2D