大步小步算法英文名: baby-step gaint-step (BSGS) .
该算法可在
Θ
(
p
)
\Theta \left ( \sqrt{p} \right )
Θ(p)的时间复杂度内求解
a
x
≡
b
m
o
d
p
a^{x} \equiv b\ mod\ p
ax≡b mod p,其中
a
a
a与
p
p
p互质,方程的解
x
x
x满足
0
≤
x
≤
p
0 \leq x \leq\ p
0≤x≤ p
令 x = A ⌈ p ⌉ − B x=A\left \lceil \sqrt{p} \right \rceil-B x=A⌈p⌉−B,其中 0 ≤ A , B ≤ ⌈ p ⌉ 0\leq A,B\leq \left \lceil \sqrt{p} \right \rceil 0≤A,B≤⌈p⌉
则有 a A ⌈ p ⌉ − B ≡ b a^{A\left \lceil \sqrt{p} \right \rceil-B}\equiv b aA⌈p⌉−B≡b,即 a A ⌈ p ⌉ ≡ b a B a^{A\left \lceil \sqrt{p} \right \rceil}\equiv ba^{B} aA⌈p⌉≡baB
由于a、b是确定的,所以我们可以先枚举右边 b a B ba^{B} baB存入hash/map中,再枚举左边 a A ⌈ p ⌉ a^{A\left \lceil \sqrt{p} \right \rceil} aA⌈p⌉的A,从而计算出所有的x
P3846 [TJOI2007] 可爱的质数/【模板】BSGS
代码如下
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main()
{
int p, a, b;
cin >> p >> a >> b;
int m = (int)sqrt(p);
if(m * m != p) m ++;
map<int, int> hash;
int s = 1;
for(int i = 1; i <= m; ++ i)
{
s = (LL)s * a % p;
b = (LL)b * a % p;
hash[b] = i;
}
bool flag = false;
a = s;
for(int i = 1; i <= m; ++ i)
{
if(hash[a])
{
printf("%d\n", i * m - hash[a]);
flag = true;
break;
}
a = (LL)a * s % p;
}
if(!flag) printf("no solution");
return 0;
}
扩展大步小步算法
上边的算法只针对 a a a与 p p p互质的情形,如果两者不互质,怎么计算?
当 x > 0 x>0 x>0时, a x ≡ b ( m o d p ) a^{x}\equiv b(mod\ p) ax≡b(mod p)等价于 a x − 1 a + n p = b a^{x-1}a+np=b ax−1a+np=b由裴蜀定理可知该方程有解的条件是 b b b是 g c d ( a , p ) gcd(a,p) gcd(a,p)的倍数
设 d = g c d ( a , p ) d=gcd(a,p) d=gcd(a,p),则有 a x − 1 a b + n p d = b d \frac{a^{x-1}a}{b}+\frac{np}{d}=\frac{b}{d} bax−1a+dnp=db,
即 a b a x − 1 ≡ b d ( m o d p d ) \frac{a}{b}a^{x-1}\equiv \frac{b}{d}(mod\ \frac{p}{d}) baax−1≡db(mod dp)
这时如果 g c d ( a , p d ) = 1 gcd(a,\frac{p}{d})=1 gcd(a,dp)=1,就可以直接使用BSGS求解,左侧多出一个系数,修改一下BGSG即可,解出的 x x x加上 1 1 1即是原方程的一个解,否则可以继续递归下去,直到互质为止
BSGS算出来的是最小解,而扩展BSGS算出来的不一定是最小解
注意:递归时需要特判,如果 b ≡ 1 b\equiv 1 b≡1直接返回 x = 0 x=0 x=0即可,当 a ≡ b ≡ 0 a\equiv b\equiv0 a≡b≡0时应该返回1,一般认为 0 0 = 1 0^0=1 00=1
时间复杂度: Θ ( n l o g n ) \Theta (\sqrt{n}logn) Θ(nlogn)
具体代码如下
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
map<int, int> hs;
int BSGS(int a, int b, int p, int k)
{
hs.clear();
int m = sqrt(p) + 1, s = 1;
for(int B = 1; B <= m; ++ B)
{
s = 1ll * s * a % p;
hs[1ll * b * s % p] = B;
}
k = 1ll * k * s % p;
for(int i = 1; i <= m; ++ i)
{
int j = hs.count(k) ? hs[k] : -1;
if(j >= 0 && i * m - j >= 0) return i * m - j;
k = 1ll * k * s % p;
}
return -1;
}
int exBSGS(int a, int b, int p)
{
if(b == 1 || p == 1) return 0;
int d = gcd(a, p), k = 1, cnt = 0;
while(d != 1)
{
if(b % d) return -1;
cnt ++;
b /= d, p /= d;
k = (LL)k * a / d % p;
if(k == b) return cnt;
d = gcd(a, p);
}
int res = BSGS(a, b, p, k);
if(res < 0) return -1;
return res + cnt;
}
int main()
{
int a, b, p;
while(~scanf("%d%d%d", &a, &p, &b), (a || p || b))
{
int x = exBSGS(a % p, b % p, p);
if(x < 0) puts("No Solution");
else printf("%d\n", x);
}
return 0;
}
上边exBSGS函数中有一句if(k == b) return cnt,转化为公式是
a c n t d c n t × a x − c n t ≡ b d c n t ( m o d p d c n t ) \frac{a^{cnt}}{d^{cnt}}\times a^{x-cnt}\equiv \frac{b}{d^{cnt}}(mod\ \frac{p}{d^{cnt}}) dcntacnt×ax−cnt≡dcntb(mod dcntp)
其中 a c n t d c n t \frac{a^{cnt}}{d^{cnt}} dcntacnt就是 k k k, b d c n t \frac{b}{d^{cnt}} dcntb就是 b b b, k = b k=b k=b时相当于
a c n t d c n t ≡ b d c n t ( m o d p d c n t ) \frac{a^{cnt}}{d^{cnt}}\equiv\frac{b}{d^{cnt}}(mod\ \frac{p}{d^{cnt}}) dcntacnt≡dcntb(mod dcntp)
这个等同于
a c n t ≡ b ( m o d p ) a^{cnt}\equiv b(mod\ p) acnt≡b(mod p)
此时cnt就是我们要求的 x x x,直接返回即可