BSGS大步小步算法

BSGS大步小步算法

用于解决离散对数问题:

已知 \(a^x≡b (mod\quad p)\),求 x 的最小非负整数解。其中 \(gcd(a,p)=1\)

Small Step:对于 \(i ϵ [0,S)\) ,求出 \(a^i% p=A[i]\)

Big Step:对于 \(j ϵ [0,p/S]\) ,求出 \(a^{jS}% p=B[j]\)

这样如果 \(A[i]×B[j] ≡ b\) ( mod p) ,那么 \(x=jS+i\) 是合法解。

也即若对于 \(j ϵ [0,p/S]\) ,若存在 \(i ϵ [0,S)\) ,使得\(A[i]≡b×B[j]^{-1}\) (mod p) ,那么 \(x=jS+i\) 是合法解。

\(S≈\sqrt{p}\),则时间复杂度为\(O(\sqrt{p} log⁡p)\)

例1:New Product 板子,可爱的质数/【模板】BSGS

\[设u=\sqrt(p), A^x≡B(mod\quad p)可转化为 A^{iu-j}≡B(mod\quad p),iϵ[1,u],jϵ[0,u)\\ A^{iu}≡A^{j}B(mod\quad p)我们就可以枚举j,存到map中,再枚举i判重就行了\\ 不过当存在不同的j使A^j≡b(mod\quad p)相同时,我们记录较大的(因为j越大答案越小嘛)\\ 费马小定理:若gcd(x,p)=1,则有x^{p−1}≡1(mod\quad p),得到x^p≡x(mod\quad p)\\ 所以当指数不小于p时,mod\quad p的值会形成循环\\ 注意sqrt(p)必须上取整ceil()函数,否则sqrt(p)*sqrt(p)<p-1 漏了p-1这种情况 \]

#include <cstdio>
#include <iostream>
#include <cmath>
#include <map>
using namespace std;
#define int long long
inline int read(){
    int x=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x;
}
map<int,int>mp;
int t,a,b,p;
int qpow(int a,int b,int p){
    int ans=1;
    while(b){
        if(b&1) ans=(ans*a)%p;
        a=(a*a)%p;
        b>>=1;
    }
    return ans;
}
int BSGS(int a,int b,int p){
    if(!a)return b?-1:1;
    if(b==1)return 0;
    if(a%p==0) return -1;
    map<int,int>mp;
    int u=ceil(sqrt(p)),ax=b;
    for(int i=0;i<u;i++){
        mp[ax]=i;
        ax=ax*a%p;
    }
    int w=qpow(a,u,p),aj=1;
    for(int i=1;i<=u;i++){
		aj=aj*w%p;
        if(mp.count(aj))return u*i-mp[aj];
    }
    return -1;
}
signed main(){
    t=read();
    while(t--){
        mp.clear();//清空好习惯
        p=read();a=read();b=read();
        int ans=BSGS(a,b,p);
        if(~ans) printf("%lld\n",ans);
        else puts("Couldn't Produce!");
    }
    return 0;
}

例2:计算器

#include <cstdio>
#include <iostream>
#include <cmath>
#include <map>
using namespace std;
#define ll long long
inline int read(){
    int x=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x;
}
map<int,int>mp;
int t,a,b,p,k;
ll qpow(ll a,ll b,int p){
    ll ans=1;
    while(b){
        if(b&1) ans=(ans*a)%p;
        a=(a*a)%p;
        b>>=1;
    }
    return ans;
}
ll BSGS(ll a,ll b,ll p){
    if(!a)return b?-1:1;
    if(b==1)return 0;
    if(a%p==0) return -1;
    map<ll,ll>mp;
    ll u=ceil(sqrt(p)),ax=b;
    for(int i=0;i<u;i++){
        mp[ax]=i;
        ax=ax*a%p;
    }
    ll w=qpow(a,u,p),aj=1;
    for(int i=1;i<=u;i++){
		aj=aj*w%p;
        if(mp.count(aj))return u*i-mp[aj];
    }
    return -1;
}
int main(){
    t=read();k=read();
    if(k==1){
        for(int i=1;i<=t;i++){
            a=read();b=read();p=read();
            printf("%lld\n",qpow(a,b,p));
        }
    }else if(k==2){
        for(int i=1;i<=t;i++){
            a=read();b=read();p=read();
            if(a%p==0) printf("Orz, I cannot find x!\n");
            else printf("%lld\n",qpow(a,p-2,p)*b%p);
        }
    }else{
        for(int i=1;i<=t;i++){
            a=read();b=read();p=read();
            ll ans=BSGS(a%p,b%p,p);
            if(~ans) printf("%lld\n",ans);
            else printf("Orz, I cannot find x!\n");
        } 
    }
    return 0;
}

拓展BSGS

\[若gcd(a,p)≠1,令d=gcd(a,p),将方程改写成等式形式,a^x+kp=b;\\ 此时 必须满足b|d,同时除以d 得到\frac{a}{d}*a^{x-1}+\frac{k}{d}*p=\frac{b}{d}\\ 这样前面的\frac{a}{d}就是一个系数了,不断检查gcd(\frac{b}{d},a),一直除到互质为止 此时的形式就变成了(\frac{a}{d})^k*a^{x-k}≡\frac{b}{d}(mod\quad \frac{p}{d})\\ 这样子bsgs求解之后在还原回去就行了。 \]

好吧下面是正解,其实就多了这一步

while(d!=1){
    if(b%d)return -1;
    p/=d;b/=d;++k;
    c=1ll*c*(a/d)%p;
    if(b==c) return k;
    d=gcd(a,p);
}

注意aj = c;

还要特判 if(b==c) 的情况

#include <cstdio>
#include <iostream>
#include <cmath>
#include <map>
using namespace std;
#define int long long
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
map<int,int>mp;
int t,a,b,p;
int gcd(int a,int b){
    return b?gcd(b,a%b):a;
}
int qpow(int a,int b,int p){
    int ans=1;
    while(b){
        if(b&1) ans=(ans*a)%p;
        a=(a*a)%p;
        b>>=1;
    }
    return ans;
}
int ex_BSGS(int a,int b,int p){
    if(!a) return b?-1:1;
    if(b==1||p==1)return 0;
    if(a%p==0) return -1;
    int d=gcd(a,p),k=0,c=1;
    while(d!=1){
        if(b%d)return -1;
        p/=d;b/=d;++k;
        c=1ll*c*(a/d)%p;
        if(b==c) return k;
        d=gcd(a,p);
    }
    mp.clear();
    int u=ceil(sqrt(p)),ax=b;
    for(int i=0;i<u;i++){
        mp[ax]=i;
        ax=1ll*ax*a%p;
    }
    int w=qpow(a,u,p),aj=c;
    for(int i=1;i<=u;i++){
		aj=1ll*aj*w%p;
        if(mp.count(aj)) return k+u*i-mp[aj];
    }
    return -1;
}
signed main(){
    while(1){
        a=read();p=read();b=read();
        if(!p && !a && !b) return 0;;
        int ans=ex_BSGS(a%p,b%p,p);
        if(~ans) printf("%lld\n",ans);
        else puts("No Solution");        
    }
}

例3:多少个1?

Desciption

给定整数 K和质数 m,求最小的正整数 N,使得 11111⋯1(N个 1)≡K(mod m)

Solution

\[N个1 可以转成等比数列求和 1,10,100....即 \frac{10^n-1}{9}≡K(mod\quad m)\\ 移项 10^n≡9*K+1(mod\quad m) ,令a=10,b=9*K+1,p=m,套exBSGS板子可过~~不去~~\\ 额乘法爆long long,题解的巧妙方法,快速乘 \]

#include <iostream>
#include <cstdio>
#include <map>
#include <cmath>
using namespace std;
#define int long long 
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int mul(int a, int b, int P){//快速乘
    int L = a * (b >> 25ll) % P * (1ll << 25) % P;
    int R = a * (b & ((1ll << 25) - 1)) % P;
    return (L + R) % P;
}
int gcd(int a,int b){
    if(a<b) swap(a,b);
    return b?gcd(b,a%b):a;
}
int qpow(int a,int b,int p){
    int ans=1;
    while(b){if(b&1)ans=mul(ans,a,p);a=mul(a,a,p);b>>=1;}
    return ans;
}
map<int,int>mp;
int ex_BSGS(int a,int b,int p){
    if(!a) return b?-1:1;
    if(b==1|p==1) return 0;
    if(a%p==0) return -1;
    int d=gcd(a,p),c=1,k=0;
    while(d!=1){
        if(b%d) return -1;
        b/=d;p/=d;++k;
        c=mul(c,a/d,p);
        if(b==c) return k;
        d=gcd(a,p);
    }
    mp.clear();
    int u=ceil(sqrt(p)),ax=b;
    for(int i=0;i<u;i++)
        mp[ax]=i,ax=mul(ax,a,p);   
    int aj=c,w=qpow(a,u,p);
    for(int i=1;i<=u;i++){
        aj=mul(aj,w,p);
        if(mp.count(aj)) return i*u-mp[aj]+k;
    }
    return -1;
}
signed main(){
    int k=read(),p=read();
    printf("%lld\n",ex_BSGS(10,(9*k+1)%p,p));
}

posted @ 2020-08-19 21:12  ke_xin  阅读(62)  评论(0编辑  收藏  举报