• BSGS大步小步算法


    BSGS大步小步算法

    用于解决离散对数问题:

    已知 (a^x≡b (modquad 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(modquad p)可转化为 A^{iu-j}≡B(modquad p),iϵ[1,u],jϵ[0,u)\ A^{iu}≡A^{j}B(modquad p)我们就可以枚举j,存到map中,再枚举i判重就行了\ 不过当存在不同的j使A^j≡b(modquad p)相同时,我们记录较大的(因为j越大答案越小嘛)\ 费马小定理:若gcd(x,p)=1,则有x^{p−1}≡1(modquad p),得到x^p≡x(modquad p)\ 所以当指数不小于p时,modquad 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
    ",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
    ",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!
    ");
                else printf("%lld
    ",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
    ",ans);
                else printf("Orz, I cannot find x!
    ");
            } 
        }
        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}(modquad 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
    ",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(modquad m)\ 移项 10^n≡9*K+1(modquad 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
    ",ex_BSGS(10,(9*k+1)%p,p));
    }
    
    
  • 相关阅读:
    Delphi 学习笔记
    Extjs 4
    面向对象(OOP)
    Java基础
    Ubantu(乌班图)
    CentOS 6.3操作常识
    英语音标单元音篇
    英语音标双元音篇
    英语音标辅音篇
    Oracle补习班第一天
  • 原文地址:https://www.cnblogs.com/ke-xin/p/13531944.html
Copyright © 2020-2023  润新知