BSGS,扩展BSGS(求离散对数 b^x%p=n)

BSGS

大步小步法( B a b y − S t e p − G i a n t − S t e p Baby-Step-Giant-Step BabyStepGiantStep,简称 B S G S BSGS BSGS),可以在 O ( l o g P ) O(logP) O(logP)的时间内求出 b a s e x ≡ n ( m o d    P ) base^x\equiv n(mod\;P) basexn(modP)的解 x x x,也就是在模 P P P意义下,以 b a s e base base为底, n n n的离散对数。

其中要求 ( b a s e , P ) = 1 (base,P)=1 (base,P)=1


过程

其实就是一个暴力而已,假定已知 m m m,将 x x x分解成 b m − a bm-a bma的形式,有: b a s e b m ≡ n ∗ b a s e a ( m o d    P ) base^{bm}\equiv n*base^{a}(mod\;P) basebmnbasea(modP)
我们枚举 a ∈ [ 0 , m − 1 ] a\in[0,m-1] a[0,m1],将 n ∗ b a s e a % P n*base^{a}\%P nbasea%P打标记。然后再枚举 b ∈ [ 1 , P / m ] b\in[1,P/m] b[1,P/m],判断 b a s e b m % P base^{bm}\%P basebm%P是否被打标记,如果是,则说明找到了一对 &lt; a , b &gt; &lt;a,b&gt; <a,b>,带入得 b m − a bm-a bma就是答案。

显然,当 m m m P \sqrt P P a , b a,b a,b的枚举次数期望最少。此时有 b ∈ [ 1 , m + 1 ] b\in[1,m+1] b[1,m+1]


判断标记最好使用哈希链表,不然时间复杂度堪忧。

例题

original link - http://poj.org/problem?id=2417

/*
 *  Author : Jk_Chen
 *    Date : 2019-08-20-19.42.31
 */
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<math.h>
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(int)(a);i>=(int)(b);i--)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pill pair<int, int>
#define fi first
#define se second
const LL mod=1e9+7;
const int maxn=1e5+9;
LL rd(){ LL ans=0; char last=' ',ch=getchar();
    while(!(ch>='0' && ch<='9'))last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans; return ans;
}
/*_________________________________________________________head*/

LL Pow(LL a,LL b,LL mod){
    LL res=1;
    while(b>0){
        if(b&1)res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}

namespace Hash{
	const int Mod=102023;
	int head[Mod],nex[Mod],now;
	pill val[Mod];
	void init(){
	    now=0;
	    mmm(head,0);
	}
	void insert(int x,int y){
	    int key=x%Mod;
	    nex[++now]=head[key];head[key]=now;
	    val[now].fi=x,val[now].se=y;
	}
	int find(int x){
	    int key=x%Mod;
	    for(int i=head[key];i;i=nex[i]){
	        if(val[i].fi==x)return val[i].se;
	    }
	    return -1;
	}
}

void BSGS(int base,int n,int p){ // Pow(base, b*m) = n * Pow(base, a)
    if(n==1){
        printf("0\n");return;
    }
    Hash::init();
    // x = a + bm
    int m=sqrt(p);
    int tmp=n;
    rep(a,0,m-1){
        Hash::insert(tmp,a);
        tmp=1ll*tmp*base%p;
    }

    tmp=1;
    LL mul=Pow(base,m,p);
    rep(b,1,m+1){
        tmp=1ll*tmp*mul%p;
        int a=Hash::find(tmp);
        if(~a){
            printf("%d\n",b*m-a);
            return;
        }
    }
    printf("no solution\n");
}

int main(){
    int base,n,p;
    while(cin>>p>>base>>n){
        BSGS(base,n,p);
    }
    return 0;
}


同余定理

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


Extended BSGS

B S G S BSGS BSGS仅适用于 ( b a s e , P ) = 1 (base,P)=1 (base,P)=1的情况,对于非互质情况,需要用扩展算法进行转化。

我们提取 d = ( b a s e , P ) d=(base,P) d=(base,P),原式 b a s e x ≡ n ( m o d &ThickSpace; P ) → base^x\equiv n(mod\;P)\to basexn(modP) b a s e x − 1 ∗ b a s e d ≡ n d ( m o d &ThickSpace; P d ) base^{x-1}*\dfrac{base}{d}\equiv \dfrac{n}{d}(mod\;\dfrac{P}{d}) basex1dbasedn(moddP)
这里的 ( n , d ) = ̸ d (n,d)=\not d (n,d)≠d时,根据同余定理可判断方程无解。

继续上述过程直至 ( b a s e , P ) = 1 (base,P)=1 (base,P)=1,有: b a s e x − k ∗ b a s e d 1 . . d k ≡ n d 1 . . d k ( m o d &ThickSpace; P d 1 . . d k ) base^{x-k}*\dfrac{base}{d_1..d_k}\equiv \dfrac{n}{d_1..d_k}(mod\;\dfrac{P}{d_1..d_k}) basexkd1..dkbased1..dkn(modd1..dkP)
因为模数底数互质,所以可以直接对此式进行 B S G S BSGS BSGS b a s e b m − a ∗ b a s e d 1 . . d k ≡ n d 1 . . d k ( m o d &ThickSpace; P d 1 . . d k ) base^{bm-a}*\dfrac{base}{d_1..d_k}\equiv \dfrac{n}{d_1..d_k}(mod\;\dfrac{P}{d_1..d_k}) basebmad1..dkbased1..dkn(modd1..dkP) b a s e b m ∗ b a s e d 1 . . d k ≡ n d 1 . . d k ∗ b a s e a ( m o d &ThickSpace; P d 1 . . d k ) base^{bm}*\dfrac{base}{d_1..d_k}\equiv \dfrac{n}{d_1..d_k}*base^{a}(mod\;\dfrac{P}{d_1..d_k}) basebmd1..dkbased1..dknbasea(modd1..dkP)

最后的答案为 b m − a + k bm-a+k bma+k


例题

original link - http://poj.org/problem?id=3243

/*
 *  Author : Jk_Chen
 *    Date : 2019-08-21-09.12.01
 */
#include<iostream>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<stdio.h>
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(int)(a);i>=(int)(b);i--)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pill pair<int, int>
#define fi first
#define se second
const LL mod=1e9+7;
const int maxn=1e5+9;
LL rd(){ LL ans=0; char last=' ',ch=getchar();
    while(!(ch>='0' && ch<='9'))last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans; return ans;
}
/*_________________________________________________________head*/

LL Pow(LL a,LL b,LL mod){
    LL res=1;
    while(b>0){
        if(b&1)res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}

namespace Hash{
	const int Mod=102023;
	int head[Mod],nex[Mod],now;
	pill val[Mod];
	void init(){
	    now=0;
	    mmm(head,0);
	}
	void insert(int x,int y){
	    int key=x%Mod;
	    nex[++now]=head[key];head[key]=now;
	    val[now].fi=x,val[now].se=y;
	}
	int find(int x){
	    int key=x%Mod;
	    for(int i=head[key];i;i=nex[i]){
	        if(val[i].fi==x)return val[i].se;
	    }
	    return -1;
	}
}

int BSGS(int base,int n,int p){ // Pow(base, b*m) * bd = n * Pow(base, a)
    if(n==1){
        return 0;
    }
    int d=__gcd(base,p);
    int bd=1,k=0;
    while(d>1){
        if(n%d){
            return -1;
        }
        n/=d;
        p/=d;
        k++;
        bd=1ll*bd*(base/d)%p;
        if(bd==n){
            return k;
        }
        d=__gcd(base,p);
    }

    Hash::init();
    int m=sqrt(p);
    int tmp=n;
    rep(a,0,m-1){
        Hash::insert(tmp,a);
        tmp=1ll*tmp*base%p;
    }

    tmp=bd;
    LL mul=Pow(base,m,p);
    rep(b,1,m+1){
        tmp=1ll*tmp*mul%p;
        int a=Hash::find(tmp);
        if(~a){
            return b*m+k-a;
        }
    }
    return -1;
}

int main(){
    int base,n,p;
    while(cin>>base>>p>>n,base|p|n){
        int res=BSGS(base,n,p);
        if(~res)cout<<res<<endl;
        else puts("No Solution");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值