BSGS 大步小步算法 求解高次同余方程【模板】
B
a
b
y
S
t
e
p
G
i
a
n
t
S
t
e
p
BabyStepGiantStep
BabyStepGiantStep 算法,缩写为
B
S
G
S
BSGS
BSGS,即大步小步算法,,拔山盖世算法
用于求解同余方程 a x ≡ b ( m o d p ) a^x≡b(mod p) ax≡b(modp) ,其中 ( a , p ) = 1 (a,p)=1 (a,p)=1
首先,我们可以先分析一下
x
x
x的范围(这个地方我觉得网上很多题解都没有讲到,只是列出了范围而已,或许在dalao们眼中这个是显而易见的 )
由于 ( a , p ) = 1 (a,p)=1 (a,p)=1,由费马小定理可得: a p − 1 ≡ 1 ( m o d p ) a^{p-1}≡1(mod p) ap−1≡1(modp),而 a 0 ≡ 1 ( m o d p ) a^0≡1(mod p) a0≡1(modp),所以解的循环节小于 p p p,那么,如果选择暴力枚举的话,就应该从0到 p − 1 p-1 p−1进行枚举,依次验证。
但是如果 p p p的范围比较大的话,就凉凉了,此时BSGS就应运而生
令
t
=
⌊
p
⌋
t=⌊\sqrt{p}⌋
t=⌊p⌋
将
x
x
x表示为
x
=
i
∗
t
−
j
x=i*t-j
x=i∗t−j,
0
≤
i
≤
t
0≤i≤t
0≤i≤t,
0
≤
j
≤
t
0≤j≤t
0≤j≤t
那么
a
i
∗
t
−
j
≡
b
(
m
o
d
p
)
a^{i*t-j}≡b(mod p)
ai∗t−j≡b(modp)
两边同乘以
a
j
a^j
aj得
a
i
∗
t
≡
b
∗
a
j
(
m
o
d
p
)
a^{i*t}≡b*a^j(mod p)
ai∗t≡b∗aj(modp)
(
a
t
)
i
≡
b
∗
a
j
(
m
o
d
p
)
(a^t)^i≡b*a^j(mod p)
(at)i≡b∗aj(modp)
先枚举
j
,
0
≤
j
≤
t
j,0≤j≤t
j,0≤j≤t,把
b
∗
a
j
m
o
d
p
b*a^jmod p
b∗ajmodp插入到Hash表中
再枚举
i
,
0
≤
i
≤
t
i,0≤i≤t
i,0≤i≤t,在Hash表中查
(
a
t
)
i
m
o
d
p
(a^t)^imod p
(at)imodp的值是否存在于Hash表中,若存在,则找到了一个解为
x
=
i
∗
t
−
j
x=i*t-j
x=i∗t−j
如果枚举完之后找不到,则方程无解
例题&板子:
P
O
J
2417
D
i
s
c
r
e
t
e
L
o
g
g
i
n
g
POJ2417 Discrete Logging
POJ2417DiscreteLogging
传送门:POJ2417
B
L
=
=
N
(
m
o
d
P
)
B^L == N (mod P)
BL==N(modP)求解最小的L
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
#include<map>
#include<cmath>
using namespace std;
#define LL long long
LL a,b,p;
map<int,int> Hash;
map<int,bool> vis;
LL Pow(LL x,LL y)
{
LL ans=1;
while(y>0)
{
if(y&1) ans=(ans*x)%p;
x=(x*x)%p;
y>>=1;
}
return ans;
}
void Init()
{
Hash.clear();
vis.clear();
}
LL BSGS()
{
Init();
LL t=sqrt(p);
b%=p;
for(int j=0;j<=t;j++)
{
LL tmp=b*Pow(a,j)%p;
Hash[tmp]=j;
vis[tmp]=1;
}
a=Pow(a,t);
for(int i=0;i<=t;i++)
{
LL tmp=Pow(a,i);
if(vis[tmp])
{
int j=Hash[tmp];
if(j>=0&&i*t-j>=0)
return i*t-j;
}
}
return -1;
}
int main()
{
while(~scanf("%lld %lld %lld",&p,&a,&b))
{//a^x%p==b%p
LL ans=BSGS();
if(ans==-1) printf("no solution\n");
else printf("%lld\n",ans);
}
return 0;
}