BSGS & EXBSGS 算法略解

版权声明:作为一个蒟蒻,转载时请通知我这个蒟蒻 https://blog.csdn.net/zyszlb2003/article/details/89379327

BSGS \operatorname{BSGS} 要解决的问题:
给定整数 a , b , p a,b,p ,其中 a , p a,p 互质,求一个非负整数 x x ,使得
a x b ( m o d   p ) a^x \equiv b(mod~p)
首先我们先可以了解一下,欧拉定理
a φ ( n ) b ( m o d   n ) a^{\varphi(n)}\equiv b(mod~n)
x = i m + w x=i*m+w ,其中 m =   p , 0 w m 1 m=\left \lceil\ \sqrt{p}\right\rceil,0\leq w\leq m-1 ,为什么要向上取整呢,因为我们要保证遍历到 p p ,才能保证 φ ( p ) \varphi(p) 被遍历到,保证覆盖到最小正整数解。
则原问题转化为:
a i m + w b ( m o d   n ) a^{i*m+w}\equiv b(mod~n)
即:
a i m a w b ( m o d   n ) a^{i*m}*a^w\equiv b(mod~n)
使用时空扭曲术! A = a i m , B = n , x = a w , k = b . A=a^{i*m},B=n,x=a^w,k=b. 使用大复原术! 得到:
A x + B y = k A*x+B*y=k
这不是同余方程吗! 通过 exgcd \operatorname{exgcd} ,求出 x x ,即 a w a^w .
最小整数解即为第一个符合条件的 i m + w i*m+w .
关于怎么实现快速查找 w w ,我们需要打一个hash表,用map也行,查表即可

struct hash
{
    struct node
    {
        ll t,next,p;
    }a[maxn<<2];
    int v[maxn<<2],num,len;
    void ins(int p,ll t)
    {
        ll k=t&maxn;
        if(v[k]!=num)
        {
            v[k]=num;
            a[k].next=-1;a[k].t=t;a[k].p=p;
        }
        else
        {
            for(;;k=a[k].next)
            {
                if(t==a[k].t)return ;
                if(a[k].next==-1)break;
            }
            a[k].next=++len;a[len].t=t;a[len].p=p;a[len].next=-1;
        }
    }
    ll check(ll t)
    {
        ll k=t&maxn;
        if(v[k]!=num)return -1;
        for(;k!=-1;k=a[k].next)if(a[k].t==t)return a[k].p;
        return -1;
    }
}h;

这是怎么一回事呢?就是说,我预处理将 a 1 , a 2 , . . . . . . , a m 1 a_1,a_2,......,a_{m-1} 加密到 h a s h hash 表中,由于 0 w &lt; m 0\le w&lt;m ,就可以快速查询出 w w 的值了。
那么
我们代码的流程就是
1.将 a 1 , a 2 , . . . . . . , a m a_1,a_2,......,a_m 预处理进hash表
2.后枚举 i m , 0 i m i*m,0\leq i \leq m ,判断是否可行。
代码

ll bsgs(ll A,ll B,ll p)
{
    //ll t=1%p;for(ll i=0;i<=100;i++){if(t==B)return i;t=t*A%p;}
    ll D=1%p;ll t;
    ll m=(int)sqrt(p)+1;
    t=1%p;h.len=maxn;
    for(int i=0;i<m;i++)
        {h.ins(i,t);t=t*A%p;}
    //h.ins(m,t);
	ll am=t;
    for(int i=0;i<=m;i++)
    {
        ll a=D,b=p,k=B,x,y;
        ll d=exgcd(a,b,x,y);
        x=((x*(k/d))%(b/d)+(b/d))%(b/d);
        ll w=h.check(x);
        if(w!=-1)return i*m+w;
        D=D*am%p;
    }
    return -1;
}

e x B S G S exBSGS 算法

本文含两种做法。
a , p a,p 不互质,怎么办呢?
a x b ( m o d   p ) a^x\equiv b(mod~p)
那我们就想办法让它互质呗。
d 1 = g c d ( a , p ) d_1=gcd(a,p) ,则
a x + p y = b a^x+py=b
a x 1 a d 1 + p d 1 y = b d 1 a^{x-1}*\frac{a}{d_1}+\frac{p}{d_1}*y=\frac{b}{d_1}
a x 1 a d 1 b d 1 ( m o d   p d 1 ) a^{x-1}*\frac{a}{d_1}\equiv \frac{b}{d_1}(mod ~\frac{p}{d_1})
D 1 = a d 1 , b 1 = b d 1 , p 1 = p d 1 D_1=\frac{a}{d_1},b_1=\frac{b}{d_1},p_1=\frac{p}{d_1}
若依旧不互质,则设 d 2 = g c d ( a , p 1 ) d_2=gcd(a,p_1) ,则
a x 2 D 1 a d 2 b 1 d 2 ( m o d   p 1 d 2 ) a^{x-2}*D_1*\frac{a}{d_2}\equiv\frac{b_1}{d_2}(mod~\frac{p_1}{d_2})
. . . . . . . . . . . . . . . . . .................
经过多次运算,得到最后一条互质的柿子

a x n D n b n ( m o d   p n ) a^{x-n}*D_n\equiv b_n(mod~p_n)

其中 D n = i = 1 n a d i , b n = b i = 1 n d i , p n = p i = 1 n d i D_n=\prod_{i=1}^n\frac{a}{d_i},b_n=\frac{b}{\prod _{i=1}^{n}d_i},p_n=\frac{p}{\prod _{i=1}^{n}d_i}

做法一:
BSGS \operatorname{BSGS} 可知,设 x n = i m + w x-n=i*m+w ,其中 m =   p n , 0 w m 1 m=\left \lceil\ \sqrt{p_n}\right\rceil,0\leq w\leq m-1
则原问题转化为:
a i m + w D n b n ( m o d   p n ) a^{i*m+w}*D_n\equiv b_n(mod~p_n)
即:
a i m a w D n b n ( m o d   p n ) a^{i*m}*a^w*D_n\equiv b_n(mod~p_n)
使用时空扭曲术! A = a i m D n , B = p n , x = a w , k = b n . A=a^{i*m}*D_n,B=p_n,x=a^w,k=b_n. 使用大复原术! 得到:
A x + B y = k A*x+B*y=k
最后答案就为 i m + w + n i*m+w+n .
回到 BSGS \operatorname{BSGS} 即可

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#define ll long long
using namespace std;
const ll maxn=(1<<16)-1;
struct hash
{
    struct node
    {
        ll t,next,p;
    }a[maxn<<2];
    int v[maxn<<2],num,len;
    void ins(int p,ll t)
    {
        ll k=t&maxn;
        if(v[k]!=num)
        {
            v[k]=num;
            a[k].next=-1;a[k].t=t;a[k].p=p;
        }
        else
        {
            for(;;k=a[k].next)
            {
                if(t==a[k].t)return ;
                if(a[k].next==-1)break;
            }
            a[k].next=++len;a[len].t=t;a[len].p=p;a[len].next=-1;
        }
    }
    ll check(ll t)
    {
        ll k=t&maxn;
        if(v[k]!=num)return -1;
        for(;k!=-1;k=a[k].next)if(a[k].t==t)return a[k].p;
        return -1;
    }
}h;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(!b){x=1;y=0;return a;}
    else
    {
        ll tx,ty;ll d=exgcd(b,a%b,tx,ty);
        x=ty;y=tx-(a/b)*ty;
        return d;
    }
}
ll exbsgs(ll A,ll B,ll p)
{
    //ll t=1%p;for(ll i=0;i<=100;i++){if(t==B)return i;t=t*A%p;}
    ll D=1%p;ll g=0,d;ll t;
    B%=p;if(B==1||C==1)return 0;
    while((d=gcd(A,p))!=1)
    {
        if(B%d)return -1;
        B/=d;p/=d;D=D*(A/d)%p;g++;
        if(B==D)return g;
    }
    ll m=(int)sqrt(p)+1;
    t=1%p;h.len=maxn;
    for(int i=0;i<m;i++)
        {h.ins(i,t);t=t*A%p;}
    //h.ins(m,t);
	ll am=t;
    for(int i=0;i<=m;i++)
    {
        ll a=D,b=p,k=B,x,y;
        ll d=exgcd(a,b,x,y);
        x=((x*(k/d))%(b/d)+(b/d))%(b/d);
        ll w=h.check(x);
        if(w!=-1)return i*m+g+w;
        D=D*am%p;
    }
    return -1;
}
int main()
{
    ll A,p,B;h.num=0;
    while(scanf("%lld%lld%lld",&A,&p,&B)!=EOF)
    {
    	if(!A&&!p&&!B)break;
        h.num++;
        ll x=exbsgs(A,B,p);
        if(x==-1)puts("No Solution");
        else printf("%lld\n",x);
    }
    return 0;
}

做法二:
经过我们机房日日夜夜地辛苦奋战,成功将不用 e x g c d exgcd 的方法出炉啦!
x n = i m w x-n=i*m-w ,其中 m =   p n , 0 w m 1 m=\left \lceil\ \sqrt{p_n}\right\rceil,0\leq w\leq m-1
则原问题转化为:
a i m w D n b n ( m o d   p n ) a^{i*m-w}*D_n\equiv b_n(mod~p_n)
即:
a i m D n b n a w ( m o d   p n ) a^{i*m}*D_n\equiv b_n*a^w(mod~p_n)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#define ll long long
using namespace std;
const int maxn=(1<<16)-1;
struct hash
{
    int v[maxn<<2];int num;
    struct node
    {
        ll t;int p,next;
    }a[maxn<<2];int len;
    hash(){num=0;}
    inline void ins(int p,ll t)
    {
        int k=t&maxn;
        if(v[k]!=num)
        {
            v[k]=num;a[k].next=-1;a[k].p=p;a[k].t=t;
        }
        else 
        {
        	for(;;k=a[k].next)
        	{
        		if(t==a[k].t){a[k].p=p;return ;}
        		if(a[k].next==-1)break;
        	}
            a[k].next=++len;a[len].p=p;a[len].t=t;a[len].next=-1;
        }
    }
    inline int find(ll t)
    {
        int k=t&maxn,p=-1;
        if(v[k]!=num)return -1;
        for(;k!=-1;k=a[k].next)
            if(t==a[k].t)p=a[k].p;
        return p;
    }
}h;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll exbsgs(ll A,ll B,ll p)
{
    ll d,D=1%p;ll g=0;B=B%p;
    if(B==1||p==1)return 0;
    while((d=gcd(A,p))!=1)
    {
        if(B%d)return -1;
        B/=d;p/=d;D=(D*A/d)%p;
        g++;if(B==D)return g;
    }
    ll t=1%p;ll m=(int)sqrt(p)+1;
    h.len=maxn;
    for(int i=0;i<m;i++)
        {h.ins(i,(t*B)%p);t=t*A%p;}
    ll am=t;
    for(int i=0;i<=m;i++)
    {
        ll w=h.find(D);
        if(w!=-1&&i*m-w>=0)return i*m-w+g;
        D=D*am%p;
    }
    return -1;
}
int main()
{
    ll a,b,p;
    while(scanf("%lld%lld%lld",&a,&p,&b)!=EOF)
    {
        if(!a&&!b&&!p)break;h.num++;
        ll ans=exbsgs(a,b,p);
        if(ans!=-1)printf("%lld\n",ans);
        else puts("No Solution");
    }
    return 0;
}

其实每次加密时,我们只要插入 B A j B*A^j ,查表即可

猜你喜欢

转载自blog.csdn.net/zyszlb2003/article/details/89379327