Baby Step Giant Step 初步

引入

对于 a , b , m ∈ N , gcd ⁡ ( a , m ) = 1 a,b,m\in\N,\gcd(a,m)=1 a,b,mNgcd(a,m)=1 求解指数同余方程
a x ≡ b ( m o d m ) a^x\equiv b\pmod m axb(modm)
的最小正数解。


BSGS (Baby step Giant step)

我们考虑余数是存在循环节的,也就是说某个数   m o d   m \bmod m modm 只有 [ 0 , m − 1 ] [0,m-1] [0,m1] 个取值,于是我们尝试寻找 a x   m o d   m a^x\bmod m axmodm 的循环节。
欧拉定理可知 a φ ( m ) ≡ 1 ( m o d m ) a^{\varphi(m)}\equiv 1\pmod m aφ(m)1(modm) ,因此有 a x   m o d   m a^x\bmod m axmodm 的取值只有 φ ( m ) \varphi(m) φ(m),一旦 x x x 大于 φ ( m ) \varphi(m) φ(m),那么就会与之前的值重复,答案不是最优。这个想法也可以从欧拉定理的推论 a b   m o d   φ ( m ) ≡ a b ( m o d m ) a^{b\bmod \varphi(m)}\equiv a^b\pmod m abmodφ(m)ab(modm) 得到,万变不离其宗。
于是有暴力思想,枚举 [ 1 , φ ( m ) ] [1,\varphi(m)] [1,φ(m)] 的数作为 x x x 的值,暴力判断是否满足方程。但是当 m ≥ 1 0 8 m\ge 10^8 m108 且为质数时, φ ( m ) = m − 1 ≥ 1 0 8 \varphi(m)=m-1\ge 10^8 φ(m)=m1108 ,该思路任然会超时。

于是我们就引入了“优化暴力”的 B a b y   s t e p   G i a n t   s t e p Baby~step~Giant~step Baby step Giant step 算法,简称 B S G S \mathbf{BSGS} BSGS

考虑 gcd ⁡ ( a , m ) = 1 \gcd(a,m)=1 gcd(a,m)=1,因此我们可以对同余式进行整除运算(裴蜀定理可得吧,这里就不赘述了),即可以将 x x x 表示为 x = i ⋅ t − j x=i\cdot t-j x=itj,我们设 t = ⌈ m ⌉ t=\left\lceil\sqrt m\right\rceil t=m
于是原方程变为
a i ⋅ t − j ≡ b ( m o d m )   a i ⋅ t a j ≡ b ( m o d m )   a i ⋅ t ≡ b ⋅ a j ( m o d m )   ( a t ) i ≡ b ⋅ a j ( m o d m ) a^{i\cdot t-j}\equiv b\pmod m\\ ~\\ \dfrac{a^{i\cdot t}}{a^j}\equiv b\pmod m\\ ~\\ a^{i\cdot t}\equiv b\cdot a^j\pmod m\\ ~\\ (a^t)^i\equiv b\cdot a^j\pmod m aitjb(modm) ajaitb(modm) aitbaj(modm) (at)ibaj(modm)

此时我们将原方程的 x x x 分块来枚举,就可以均摊复杂度。
我们先枚举方程右边的 j j j ,然后将 j j j 取值,按照右边的余数对应存进一个 h a s h hash hash 表里(说是这么说,但是我还是常用 m a p map map u n o r d e r e d unordered unordered_ m a p map map ),然后同理枚举方程左边的 i i i ,查找是否有 j j j i i i 方程两边余数匹配,即同余(这时候就用上先前的 h a s h hash hash 表来查找),若匹配则直接得到答案 x = i ⋅ t − j x=i\cdot t-j x=itj

i , j i,j i,j [ 1 , t ] [1,t] [1,t] 的值时,我们可以得到所有 x ∈ [ 1 , m − 1 ] x\in[1,m-1] x[1,m1] 的解,显然符合先前所得的答案范围 [ 1 , φ ( m ) ] [1,\varphi(m)] [1,φ(m)]
但是这个算法是可以通过的,时间为 Θ ( m ) \Theta(\sqrt m) Θ(m )
此算法与原先纯暴力最大的不同,就是我们将枚举进行了分块,将枚举范围分为了两部分,各为 m \sqrt m m ,这种思想有点像折半搜索(砍两瓣,分开搜索),将暴力复杂度拆开了。

这就是 B S G S \mathbf{BSGS} BSGS ,也就是一种优化了的暴力算法。

回忆一下思路,其实也很清晰,就是把答案分成两部分分别枚举然后对应查找,于是代码也很好实现。

int BSGS(int a,int b,long long p)
{
   map<int,int> vis;
   int t=sqrt(p)+1,mi=1;
   for(int i=1;i<=t;i++)
   {
       if(i!=0) mi=(__int128)(mi*a)%p;
       vis[(__int128)(b*mi)%p]=i;
   }
   a=mi;
   if(!a) return (b%p==0)?1:-1; //特判,若a能被模数整除,那么当且仅当b也能被模数整除的情况下存在最小正数解 1,否则无解
   mi=1;
   for(int i=1;i<=t;i++)
   {
     map<int,int>::iterator it;
     if(i!=0) mi=(__int128)(mi*a)%p;  //暴力求余
     it=vis.find(mi);  //查找对应
     if(it!=vis.end())
       if((__int128)i*t-(it->second)>=0) return (__int128)(i*t-(it->second));
   }
   return -1;
}

总结

回过头来, B S G S \mathbf{BSGS} BSGS 真的不难理解,其实只要思路正确,暴力都能成为好算法。

奉上例题:

P4884
P2485
P4861
P4454
P3306

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值