BSGS
前言
\(\text{BabyStepGiantStep}\)算法,即大步小步算法
又名 拔山盖世 或 北上广深 算法
讲解
按照惯例,我们先来一波百度百科
自学时间
百度百科竟然没有BSGS,我佛了
\(\text{BSGS}\)是用来求解关于\(x\)的方程,\(P\)是质数
一般是找最小的\(x\)
1.普通BSGS
即\((A,P)=1\)的情况
由费马小定理,得\(A^{P-1}≡1\pmod P\)
\(\because A^0≡1\pmod P\)
\(\therefore A\)的幂存在循环节,且循环节长度为\(P-1\)
但其实\(P\)不为质数也很明显存在循环节
(1).暴力
我们直接暴力检验循环节的每一个元素就好了
时间复杂度为\(O(P)\)
(2).普通BSGS
而如果我们采用\(\text{BSGS}\)算法:
先把\(x\)写成\(im-j\)的形式(\(m\)自取)
原式可化为:
对于右边每一个\(BA^j\),我们将其存进\(map\)或者哈希表中,然后枚举\(i\),在其中查找\(A^{im}\)是否出现过
找到即有解
明显时间复杂度为\(O(max(P/m,m))\)
当\(m=\sqrt{P}\)时时间复杂度最优
所以普通的\(\text{BSGS}\)的时间复杂度为\(O(\sqrt{P})\)
点击查看代码
void BSGS(int A,int B,int P)
{
if(B == 1) {Put(0,'\n'); return;}
int m = ceil(sqrt(P)),now = B % P;
for(int i = 0;i < m;++ i) ins(now,i),now = 1ll * now * A % P;
int Am = qpow(A,m,P); now = 1;
for(int i = 1;i <= m;++ i)
{
now = 1ll * Am * now % P;
int dz = query(now);
if(dz != -1)
{
Put(1ll*i*m - dz,'\n');
return;
}
}
printf("no solution\n");
}
2.扩展BSGS
如果\((A,P)\neq1\)怎么办?
令\(d=(A,P)\),再将原方程化为:
明显\(d|B\),否则无解
继续改写方程:
这样的话\(\frac{A}{d}\)就成为了一个系数
不断检查\((A,\frac{P}{d})\),直到互质
此时方程可化为:
注意此时的\(d\)是并不是最初的\(d\),而是做了\(cnt\)次操作累计起来的\(d\)
其中我的代码将\(\frac{y^{cnt}}{d}\)定为\(a\)
点击查看代码
void EXBSGS(int A,int B,int P)
{
if(B == 1) {Put(0,'\n'); return;}
int a = 1,cnt = 0,d;
while(122520)
{
if((d = gcd(A,P)) == 1) break;
if(B % d) {printf("No Solution\n");return;}
P /= d; B /= d; ++cnt; a = 1ll * a * A / d % P;
if(B == a) {Put(cnt,'\n');return;}
}
int m = ceil(sqrt(P)),now = B;
for(int i = 0;i < m;++ i) ins(now,i),now = 1ll * now * A % P;
int Am = qpow(A,m,P); now = a;
for(int i = 1;i <= m;++ i)
{
now = 1ll * Am * now % P;
int dz = query(now);
if(dz != -1)
{
Put(1ll*i*m - dz + cnt,'\n');
return;
}
}
printf("No Solution\n");
}
练习
普通\(\text{BSGS}\)
扩展\(\text{BSGS}\)
代码
Discrete Logging
点击查看代码
//12252024832524
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 10005;
const int MOD = 122520;
int head[MOD],tot;
struct node
{
int val,ID,nxt;
}hs[46341 + 5];
int qpow(int x,int y,int P)
{
int ret = 1;
while(y) {if(y & 1) ret = 1ll * ret * x % P;x = 1ll * x * x % P;y >>= 1;}
return ret;
}
void ins(int x,int ID)
{
hs[++tot].val = x;
x %= MOD;
hs[tot].ID = ID;
hs[tot].nxt = head[x];
head[x] = tot;
}
int query(int x)
{
for(int i = head[x % MOD]; i ;i = hs[i].nxt)
if(hs[i].val == x) return hs[i].ID;
return -1;
}
void BSGS(int A,int B,int P)
{
if(B == 1) {Put(0,'\n'); return;}
int m = ceil(sqrt(P)),now = B % P;
for(int i = 0;i < m;++ i) ins(now,i),now = 1ll * now * A % P;
int Am = qpow(A,m,P); now = 1;
for(int i = 1;i <= m;++ i)
{
now = 1ll * Am * now % P;
int dz = query(now);
if(dz != -1)
{
Put(1ll*i*m - dz,'\n');
return;
}
}
printf("no solution\n");
}
int main()
int Mod,a;
while(~scanf("%d %d",&Mod,&a))
{
memset(head,0,sizeof(head)); tot = 0;
BSGS(a,Read(),Mod);
}
return 0;
}
Clever Y
点击查看代码
void EXBSGS(int A,int B,int P)
{
if(B == 1) {Put(0,'\n'); return;}
int a = 1,cnt = 0,d;
while(122520)
{
if((d = gcd(A,P)) == 1) break;
if(B % d) {printf("No Solution\n");return;}
P /= d; B /= d; ++cnt; a = 1ll * a * A / d % P;
if(B == a) {Put(cnt,'\n');return;}
}
int m = ceil(sqrt(P)),now = B;
for(int i = 0;i < m;++ i) ins(now,i),now = 1ll * now * A % P;
int Am = qpow(A,m,P); now = a;
for(int i = 1;i <= m;++ i)
{
now = 1ll * Am * now % P;
int dz = query(now);
if(dz != -1)
{
Put(1ll*i*m - dz + cnt,'\n');
return;
}
}
printf("No Solution\n");
}
int main()
{
int A,B,Mod;
while(~scanf("%d",&A))
{
Mod = Read(); B = Read();
if(!A && !Mod && !B) return 0;
memset(head,0,sizeof(head)); tot = 0;
EXBSGS(A % Mod,B % Mod,Mod);
}
return 0;
}