BSGS_及其扩展算法

(Baby Step Giant Step)算法

BSGS存在的目的是求A^x\equiv \ B \ (mod\ C)中x的解,

BSGS算法要求A与C互质,且C为质数

  1. x=i*m-j,其中m=ceil(\sqrt{C}),这样原式就变成了A^{i*m-j}\equiv B (mod\ C),移项化简得到A^{i*m}\equiv B*A^j(mod\ C)
  2. 先循环j=1\rightarrow \ m(共有m项),将(A^j*B \ \%C,j)存入Hash表中,这个就是Baby Steps
  3. 再循环i*m=m\rightarrow \ (C-1)  枚举左边多的部分A^{i*m}(mod \ C),从hash表里面寻找是否有相同的,这就是(这就是Giant Step)
  4. 找到相同的就是一组答案,x=i*m-j

例题  poj2417  裸的BSGS算法

This is the code

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include <iomanip>
#include<list>
#include<queue>
#include<sstream>
#include<stack>
#include<string>
#include<set>
#include<vector>
using namespace std;
#define PI acos(-1.0)
#define EPS 1e-8
//#define MOD 1e9+7
#define LL long long
#define ULL unsigned long long     //1844674407370955161
#define INT_INF 0x7f7f7f7f      //2139062143
#define LL_INF 0x7f7f7f7f7f7f7f7f //9187201950435737471
const int dr[]={0, 0, -1, 1, -1, -1, 1, 1};
const int dc[]={-1, 1, 0, 0, -1, 1, -1, 1};
// ios.sync_with_stdio(false);
// 那么cin, 就不能跟C的scanf,sscanf,getchar,fgets之类的一起使用了。
#define mod 75643
struct Hash
{
    int ha;
    int id;
    int next;
}a[mod];
int head[mod];
int top;
void Insert(int x,int y)
{
    int k=x%mod;
    a[top].ha=x;
    a[top].id=y;
    a[top].next=head[k];
    head[k]=top++;
}
int Find(int x)
{
    int k=x%mod;
    for(int i=head[k];i!=-1;i=a[i].next)
        if(a[i].ha==x)
            return a[i].id;
    return -1;//表示没有找到
}
int BSGS(int a,int b,int c)
{
    if(b==1)
        return 0;
    memset(head,-1,sizeof(head));
    top=1;//一定要等于1
    int m=sqrt(c);
    LL p=1,x=1;
    for(int i=0;i<m;++i)
    {
        Insert(p*b%c,i);
        p=p*a%c;
    }
    for(LL i=m; ; i+=m)//相当于i=1,i++,i*m
    {
        x=x*p%c;//此时的p已经等于a^m
        int j=Find(x);
        if(j!=-1)
            return i-j;//这里的i已经等于i*m了
        if(i>c)
            break;
    }
    return -1;
}
int main()
{
    int a,b,c;
    while(scanf("%d%d%d",&c,&a,&b)!=EOF)
    {
        int ans=BSGS(a,b,c);
        if(ans==-1)
            printf("no solution\n");
        else
            printf("%d\n",ans);
    }
    return 0;
}

 扩展BSGS算法

扩展的BSGS不需要要求C为素数,大致的做法与原始的BSGS一致,只是有一条性质,

d=GCD(A,C),A=a*d,B=b*d, C=c*d;

则有a*d\equiv b*d(mod \ c*d)

等价于a\equiv b(mod \ c),之后再用BSGS算法计算就ok了

实际上问题变成了D*A^{c-cnt}\equiv B(mod \ C)

注意:这种条件下,会导致(0-cnt)之间的解计算不出来,所以开始前要做一次0--log_2^C之间的枚举(实际操作中验证(0-32)就行),直接验证A^i \%C=B是否成立

例题:poj3243 This is the link  裸的扩展BSGS算法

//BSGS的另一种计算方法

  1. x=i*m+j,其中m=ceil(\sqrt{C}),这样原式就变成了A^{i*m+j}\equiv B (mod\ C),移项化简得到A^{i*m}\equiv B*A^j(mod\ C)
  2. 先循环j=0\rightarrow \ m(共有m项),将(A^j \ \%C,j)存入Hash表中,这个就是Baby Steps
  3. 再循环i*m=0\rightarrow \ (C-1)  枚举左边多的部分A^{-i*m}(mod \ C),从hash表里面寻找是否有相同的,这就是(这就是Giant Step)
  4. 找到相同的就是一组答案,x=i*m+j
  5. A^{-i*m}(mod \ C)含有对除法的求模,需要用扩展欧几里得算法计算逆元得到结果A^{-i*m}(mod \ C)=\frac{1}{A^{i*m}}(mod\ C),所以求其关于C的逆元

//这里使用了手写哈希表

This is the code

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<iomanip>
#include<list>
#include<map>
#include<queue>
#include<sstream>
#include<stack>
#include<string>
#include<set>
#include<vector>
using namespace std;
#define PI acos(-1.0)
#define EPS 1e-8
//#define MOD 1e9+7
#define LL long long
#define ULL unsigned long long     //1844674407370955161
#define INT_INF 0x7f7f7f7f      //2139062143
#define LL_INF 0x7f7f7f7f7f7f7f7f //9187201950435737471
const int dr[]={0, 0, -1, 1, -1, -1, 1, 1};
const int dc[]={-1, 1, 0, 0, -1, 1, -1, 1};
// ios.sync_with_stdio(false);
// 那么cin, 就不能跟C的scanf,sscanf,getchar,fgets之类的一起使用了。
#define mod 65535
struct Hash//手写哈希表
{
    struct node
    {
        LL first;
        LL second;
        LL next;
    }edge[250005];
    LL tot;
    LL head[250005];
    void clear()
    {
        memset(head,-1,sizeof(head));
        tot=0;
    }
    void insert(LL x,LL j)
    {
        if(find(x)!=-1)
            return ;
        LL k=x%mod;
        edge[tot].first=x;
        edge[tot].second=j;
        edge[tot].next=head[k];
        head[k]=tot++;
    }
    LL find(LL x)
    {
        int k=x%mod;
        for(LL i=head[k];i!=-1;i=edge[i].next)
            if(edge[i].first==x)
                return edge[i].second;
        return -1;
    }

}hash;
LL GCD(LL a,LL b)
{
    return b? GCD(b,a%b):a;
}
void ext_gcd(LL a,LL b,LL &x,LL &y)//扩展欧几里得求逆元
{
    if(!b)
    {
        x=1;
        y=0;
        return ;
    }
    ext_gcd(b,a%b,y,x);
    y=y-a/b*x;
}
LL quick_pow(LL a,LL n,LL p)//快速幂求模
{
    LL ret=1;
    a%=p;
    while(n)
    {
        if(n&1)
            ret=a*ret%p;
        a=a*a%p;
        n>>=1;
    }
    return ret;
}
int ext_BSGS(int a,int b,int c)
{
    hash.clear();
    LL temp=1;
    LL cnt=0;
    for(int i=0;i<=32;++i)
    {
        if(temp==b)
            return i;
        temp=a*temp%c;
    }
    LL d=1;
    while((temp=GCD(a,c))!=1)
    {
        if(b%temp)
            return -1;
        b/=temp;
        c/=temp;
        d=(d*a/temp)%c;
        cnt++;
    }
    if(b==1)
        return 0;
    LL m=ceil(sqrt((double)c));
    temp=1;
    for(int i=0;i<=m;++i)
    {
        hash.insert(temp,i);
        temp=a*temp%c;
    }
    LL x=1,y=1;
    LL a_m=quick_pow(a,m,c);
    for(LL i=0; ; i+=m)//相当于i=1,i++,i*m
    {
        ext_gcd(d,c,x,y);//这里的d相当于a^m,累加计算在下面
        LL temp=((b*x)%c+c)%c;//最小正整数
        LL j=hash.find(temp);
        if(j!=-1)
            return i+j+cnt;//+cnt;//这里的i已经等于i*m了
        if(i>c)
            break;
        d=d*a_m%c;//这里枚举
    }
    return -1;
}
int main()
{
    int a,b,c;
    while(scanf("%d%d%d",&a,&c,&b)!=EOF)
    {
        if(a+b+c==0)
            break;
        int ans=ext_BSGS(a,b,c);
        if(ans==-1)
            printf("No Solution\n");
        else
            printf("%d\n",ans);
    }
    return 0;
}

 

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值