BSGS及其扩展

定义:

Baby-Step-Giant-Step算法,简称BSGS算法,又称大步小步算法,用于求方程a≡b(mod c)的最小非负整数解,此过程又称离散对数。

普通BSGS算法只能解决c为质数的情况,对其进行扩展之后则没有这个限制。

原理:

当c为质数时,若a%c==0且b!=0,则显然无解。若a%c!=0,有ac-1 ≡ a≡1(mod c),即c-1是一个循环节。因此如果方程有解,必定在0~c-2中。

因此得到一个朴素的算法,枚举0~c-2中每一个数,快速幂判断,如果相等就输出答案,时间复杂度O(c)。考虑对其进行优化,这时我们引入分块的思想。

设x=i*m+j(0≤j<m),原方程写成ai*m *aj ≡b(mod c)。令ai*m =D(i),原方程变为D(i)*aj ≡b(mod c)。

暴力枚举每一个可能的i,用扩展欧几里得可以求出a。可是我们依然无法求得j。此时注意到j的所有可能取值只有m个,因此我们预先将所有的a存入一个哈希表,当我们确定a时,直接查表可以O(1)得到是否有对应的j,以及对应的j是多少。暴力枚举复杂度O(i),预处理复杂度O(m),总复杂度O(i+m)。注意到x≈i*m,因此当m取c½ 时,总复杂度最小,为O(c½)。

至此,我们采用以空间换时间,最后通过分块的思想将该算法优化至根号级别。

代码:

#define LL long long

void bsgs(LL a,LL b,LL c)
{
   LL i,j,m=ceil(sqrt(c)),tmp,base,x;
   if(a==0&&b!=0){cout<<"Orz, I cannot find x!\n";return;}//特判(求逆元的方法不同,0没有逆元)
   for(base=1,i=0;i<m;i++)//预处理:将所有的a存入哈希表
   {
      insert(base,i);
      base=base*a%c;
   }
   for(tmp=1,i=0;i<m;i++)
   {
      x=pow_mod(tmp,c-2,c)*b%c;//类似扩展欧几里得
      j=find(x);if(j!=-1){printf("%lld\n",i*m+j);return;}//若不存在j返回-1
      tmp=tmp*base%c;//此时base的值就是a
   }
   cout<<"Orz, I cannot find x!\n";//输出无解
}

例题:

SDOI2011 计算器 题目链接

题目要求:你被要求设计一个计算器完成以下三项任务:

1、给定y、z、p,计算y^z mod p 的值;

2、给定y、z、p,计算满足xy ≡z(mod p)的最小非负整数x;

3、给定y、z、p,计算满足y^x ≡z(mod p)的最小非负整数x。

数据范围:多组数据,1≤y,z,p≤1e9,p为质数,数据组数T≤10。

题解:

操作1:快速幂;操作2:扩展欧几里得;操作3:普通BSGS。然而我没看见p为质数拿扩展BSGS做了好久......

代码略

扩展:

讲道理现在还不是很理解扩展之后的一系列操作......大概目前除了背之外并没有太好的办法,先把代码放上吧。

代码:

void exgcd(LL a,LL b,LL &x,LL &y)
{
   LL p,q,u,v;
   if(!b){x=1;y=0;return;}
   p=a/b;q=a%b;
   exgcd(b,q,u,v);
   x=v;y=u-p*v;
}
void exbsgs(LL a,LL b,LL c)
{
   LL i,j,cnt=0,m=ceil(sqrt(c)),tmp,d=1,x,y,base;
   for(tmp=1,i=0;i<32;i++){if(tmp==b){printf("%lld\n",i);return;}tmp=tmp*a%c;}
   for(;(tmp=gcd(a,c))!=1;)
   {
      cnt++;
      if(b%tmp){cout<<"Orz, I cannot find x!\n";return;}
      b/=tmp;c/=tmp;d=d*a/tmp%c;
   }
   //for(tmp=1,i=0;i<cnt;i++){if(tmp==b){printf("%lld\n",i);return;}tmp=tmp*a%c;}
   for(base=1,i=0;i<m;i++)
   {
      insert(base,i);
      base=base*a%c;
   }
   //j=-1;
   for(i=0;i<m;i++)
   {
      exgcd(d,c,x,y);tmp=gcd(d,c);
      x=(x*b/tmp%c+c)%c;
      j=find(x);
      if(j!=-1){printf("%lld\n",i*m+j+cnt);return;}
      d=d*base%c;
   }
   cout<<"Orz, I cannot find x!\n";
}

转载于:https://www.cnblogs.com/XSC637/p/7426753.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值