前言
( ext{BabyStepGiantStep})算法,即大步小步算法
又名 拔山盖世 或 北上广深 算法
讲解
按照惯例,我们先来一波百度百科
自学时间
百度百科竟然没有BSGS,我佛了
( ext{BSGS})是用来求解关于(x)的方程,(P)是质数
[A^x≡Bpmod P
]
一般是找最小的(x)
1.普通BSGS
即((A,P)=1)的情况
由费马小定理,得(A^{P-1}≡1pmod P)
(ecause A^0≡1pmod P)
( herefore A)的幂存在循环节,且循环节长度为(P-1)
但其实(P)不为质数也很明显存在循环节
(1).暴力
我们直接暴力检验循环节的每一个元素就好了
时间复杂度为(O(P))
(2).普通BSGS
而如果我们采用( ext{BSGS})算法:
先把(x)写成(im-j)的形式((m)自取)
原式可化为:
[A^{im}≡BA^{j}pmod P
]
对于右边每一个(BA^j),我们将其存进(map)或者哈希表中,然后枚举(i),在其中查找(A^{im})是否出现过
找到即有解
明显时间复杂度为(O(max(P/m,m)))
当(m=sqrt{P})时时间复杂度最优
所以普通的( ext{BSGS})的时间复杂度为(O(sqrt{P}))
点击查看代码
void BSGS(int A,int B,int P)
{
if(B == 1) {Put(0,'
'); 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,'
');
return;
}
}
printf("no solution
");
}
2.扩展BSGS
如果((A,P) eq1)怎么办?
令(d=(A,P)),再将原方程化为:
[A^x+kP=B(kin Z)
]
明显(d|B),否则无解
继续改写方程:
[frac{A}{d}A^{x-1}+kfrac{P}{d}=frac{B}{d}
]
这样的话(frac{A}{d})就成为了一个系数
不断检查((A,frac{P}{d})),直到互质
此时方程可化为:
[frac{y^{cnt}}{d}y^{x-cnt}≡frac{B}{d}pmod {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,'
'); return;}
int a = 1,cnt = 0,d;
while(122520)
{
if((d = gcd(A,P)) == 1) break;
if(B % d) {printf("No Solution
");return;}
P /= d; B /= d; ++cnt; a = 1ll * a * A / d % P;
if(B == a) {Put(cnt,'
');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,'
');
return;
}
}
printf("No Solution
");
}
练习
普通( ext{BSGS})
扩展( ext{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,'
'); 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,'
');
return;
}
}
printf("no solution
");
}
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,'
'); return;}
int a = 1,cnt = 0,d;
while(122520)
{
if((d = gcd(A,P)) == 1) break;
if(B % d) {printf("No Solution
");return;}
P /= d; B /= d; ++cnt; a = 1ll * a * A / d % P;
if(B == a) {Put(cnt,'
');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,'
');
return;
}
}
printf("No Solution
");
}
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;
}