BSGS(Baby step Giant step)
BSGS(Baby step Giant step)简称,拔山盖世,不是个事,哈哈哈,不开玩笑了,它的名字叫大步小步算法,主要用来解决形如 a x a^x ax≡ b b b(mod p)的高次线性同余方程==,其中a,p互质,求最小正整数解x的问题
解题步骤如下:
step1.令m=ceil(
p
\sqrt p
p ),也就是对p开二次根号然后向上取整。
step2.令 x = i ∗ m − j x=i*m-j x=i∗m−j,1<=i<=m,0<=j<=m-1,此时原式可以表示为 a i ∗ m − j a^{i*m - j} ai∗m−j≡b(mod p) -> a i ∗ m a^{i*m} ai∗m ≡ b × a j b×a^j b×aj(mod p)。
step3.我们把等式右边
b
×
a
j
b×a^j
b×aj,(0<=j<=m-1)的结果存入哈希表,
其中键为
b
×
a
j
b×a^j
b×aj,值为j,如果键有重复出现,那么用j值大的覆盖j值小的,因为
x
=
i
∗
m
−
j
x=i*m-j
x=i∗m−j,j越大,x越小。
step4.在枚举左边的i,如果 a i ∗ m a^{i*m} ai∗m能在哈希表中找到,那么最小正整数x就是此时的 i ∗ m − j i*m-j i∗m−j。
如果遍历完i都找不到对应的j值的话,说明无解。
模板代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
unordered_map<ll, ll>mp;
/*快速幂*/
ll quik_pow(ll dishu,ll zhishu,ll mod)
{
ll ans=1;
while(zhishu)
{
if(zhishu&1)
{
ans=ans*dishu%mod;
zhishu--;
}
else
{
dishu=dishu*dishu%mod;
zhishu>>=1;
}
}
return ans;
}
int main()
{
ll p,b,n;
cin>>p>>b>>n;
ll m = ceil(sqrt(p));//第一步对模p开二次根号向上取整
/*第二步,遍历等式右边的j从0到m-1,把答案存入哈希表*/
for(ll j=0;j<m;j++)
{
ll number = quik_pow(b,j,p);
ll ans = n%p*number%p;
mp[ans]=j;
}
/*第三步,遍历等式左边的i值从1到m,如果找到对应的j值输出答案*/
for(ll i=1;i<=m;i++)
{
ll ans = quik_pow(b,i*m,p);
if(mp.count(ans)!=0)
{
cout<<i*m-mp[ans];
return 0;
}
}
/*遍历完i值之后都找不到一个j值说明此时无解*/
cout<<"no solution";
return 0;
}
来一道模板题练练手P3846 [TJOI2007] 可爱的质数/【模板】BSGSAC代码就是上面的模板
EXBSGS(Extend Baby step Giant step)
那么问题来了,BSGS只适用于
a
x
≡
b
(
m
o
d
p
)
a^x≡b(mod p)
ax≡b(modp),a,p互质的情况,那么如果a,p不互质呢?,这时候就需要用到扩展BSGS算法。
思路:如果a,p不互质,那么我们就想方法让它们互质,想必你一定是一头雾水,下面我们就用迭代的思想,来解决这个问题
推导:
a^x≡b(mod p),等价于
a
x
+
p
y
=
b
a^x+py=b
ax+py=b,我们令g1=gcd(a,p),那么
a
(
x
−
1
)
∗
a
/
g
1
+
p
∗
y
/
g
1
=
b
/
g
1
a^{(x-1)}*a/g1+p*y/g1 = b/g1
a(x−1)∗a/g1+p∗y/g1=b/g1,显然迭代过程中要判断b是否能整除gcd(a,p)这里是g1,如果不能那么说明无解
此时
a
(
x
−
n
)
∗
a
/
g
1
≡
b
/
g
1
a^{(x-n)}*a/g1≡b/g1
a(x−n)∗a/g1≡b/g1(mod p/g1),--------①
不妨写成
a
(
x
−
n
)
∗
D
n
≡
b
n
a^{(x-n)}*Dn ≡ bn
a(x−n)∗Dn≡bn (mod pn), --------②
等价于
a
(
x
−
n
)
≡
b
n
∗
D
n
−
1
a^{(x-n)}≡bn* Dn^{-1}
a(x−n)≡bn∗Dn−1(mod pn),-------③
n是迭代次数
这时候判断a与pn是否互质,如果不互质,按照上面的方法继续迭代,直到互质或者Dn=bn,由②式我们知道,迭代过程中如果Dn=bn,原式就等价于
a
(
x
−
n
)
≡
1
a^{(x-n)}≡1
a(x−n)≡1(mod pn),也即②式两边同除Dn,此时解为x0=0=x-n,所以原方程的解x为x=x0+n=n
迭代到最后
a
(
x
−
n
)
∗
D
n
≡
b
n
a^{(x-n)}*Dn≡bn
a(x−n)∗Dn≡bn(mod pn),a,pn互质
D
n
=
∏
i
=
1
n
a
g
i
Dn = \prod_{i=1}^{n} \frac{a}{gi}
Dn=∏i=1ngia
b
n
=
b
∏
i
=
1
n
g
i
bn = \frac{b}{\prod_{i=1}^{n}gi}
bn=∏i=1ngib
p
n
=
p
∏
i
=
1
n
g
i
pn = \frac{p}{\prod_{i=1}^{n}gi}
pn=∏i=1ngip
然后解出最后迭代出来的方程的解x0=x-n,正解即为x=x0+n(n是迭代次数)
当然在解
a
x
≡
b
a^x≡b
ax≡b(mod p),a,p不互质的方程的时候,是要分情况讨论的
①a=0,若b≠0,则无解,如果b=0,那么x=1
②若b=1,那么x=0
③b=0,且a≠0,那么使用exbsgs迭代,使得pn为1,这样 a ( x − n ) ∗ D n ≡ 0 a^{(x-n)}*Dn≡0 a(x−n)∗Dn≡0(mod 1)才有解,如果迭代不能使pn为1,那么无解
④a≠0,且b≠0,那么按照上述常规exbsgs解法解题即可
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
unordered_map<ll, ll>mp;
ll P;
ll ksm(ll dishu,ll zhishu)
{
ll ans=1;
while(zhishu)
{
if(zhishu&1)
{
ans=ans*dishu%P;
zhishu--;
}
else{
dishu=dishu*dishu%P;
zhishu>>=1;
}
}
return ans;
}
void exbsgs(ll A,ll B)
{
if(A==0)
{
if(B==0)
{
printf("1\n");
return ;
}
else
{
printf("No Solution\n");
return ;
}
}
if(B==1)
{
printf("0\n");
return ;
}
if(B==0)
{
int count=0;
ll d=__gcd(A,P);
while(d!=1)
{
count++;
P/=d;
if(P==1)
{
printf("%d\n",count);
return ;
}
d=__gcd(A,P);
}
}
if(B==1)
{
printf("0\n");
return ;
}
ll count=0;
ll k=1;
ll d=__gcd(A,P);
while(d!=1)
{
if(B%d)
{
printf("No Solution\n");
return ;
}
P/=d,B/=d;k=k*(A/d)%P;
count++;
if(B==k)
{
printf("%lld\n",count);
return ;
}
d=__gcd(A,P);
}
ll m=ceil(sqrt(P));
ll s=B;
mp.clear();
for(int j=0;j<m;j++)
{
mp[s]=j;
s=s*A%P;
}
s=k;
k=ksm(A,m);
for(int i=1;i<=m;i++)
{
s=s*k%P;
if(mp[s])
{
printf("%lld\n",i*m-mp[s]+count);
return ;
}
}
printf("No Solution\n");
return ;
}
int main()
{
ll A,B;
cin>>A>>P>>B;
while(A&&B&&P)
{
exbsgs(A,B);
cin>>A>>P>>B;
}
return 0;
}
来一道exgsbs模板题练练手P4195 【模板】扩展BSGSAC代码就是上面的模板
如果鄙人的博客能够让你有哪怕一点点的收获,都是鄙人最大的荣幸,鄙人厚着脸皮向您讨个点赞,谢谢。