BSGS (Baby Step Giant Step) : \text{BSGS (Baby Step Giant Step)}: BSGS (Baby Step Giant Step):
给定正整数 a , b , p , a ⊥ p a,b,p,a \perp p a,b,p,a⊥p ,求满足
a x ≡ b ( mod p ) a^{x} \equiv b(\text{ mod } p) ax≡b( mod p)
的最小非负整数 x x x
首先由欧拉定理的推论有:
a x ≡ a x mod φ ( p ) ( mod p ) a^x\equiv a^{x\text{ mod }\varphi (p)}(\text{ mod }p) ax≡ax mod φ(p)( mod p)
所以 a x a^x ax 在模 p p p 意义下的最小循环节为 φ ( p ) \varphi(p) φ(p) ,那么只需要考虑 x ∈ [ 0 , φ ( p ) − 1 ] x \in [0,\varphi(p)-1] x∈[0,φ(p)−1] 即可,为了简便避免算欧拉函数,我们对欧拉函数进行放缩 φ ( p ) ≤ p + 1 \varphi(p) \le p + 1 φ(p)≤p+1,那就是枚举 x ∈ [ 0 , p ] x \in [0,p] x∈[0,p]。那么我们对暴力枚举的算法做一个优化:令 x = k t − y x =kt-y x=kt−y , k = ⌊ p ⌋ + 1 ,k=\lfloor \sqrt{p} \rfloor +1 ,k=⌊p⌋+1,则原式为
a k t ≡ b a y ( mod p ) a^{kt}\equiv ba^{y} (\text{ mod }p) akt≡bay( mod p)
y y y 的范围是 [ 0 , k − 1 ] [0,k-1] [0,k−1] ,所以可以枚举每个 y y y ,预处理右边的值,插入到一个哈希表中,再枚举左边的 t ∈ [ 1 , k ] t\in[1,k] t∈[1,k] 如果从哈希表找到值,那么就是答案,特判 t = 0 t=0 t=0 的情况,时间复杂度 O ( p ) O(\sqrt{p}) O(p)
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
int a, b, p;
int bsgs(int a, int b, int p) {
if (1 % p == b % p) return 0;
int k = sqrt(p) + 1;
unordered_map<int, int> mp;
for (int i = 0, j = b % p; i < k; i ++) {
mp[j] = i;
j = j * a % p;
}
int ak = 1;
for (int i = 0; i < k; i ++) ak = ak * a % p;
for (int i = 1, j = ak; i <= k; i ++) {
if (mp.count(j)) return i * k - mp[j];
j = j * ak % p;
}
return -1;
}
signed main() {
while (cin >> a >> p >> b, a || b || p) {
int res = bsgs(a, b, p);
if (res == -1) {
cout << "No Solution" << endl;
} else {
cout << res << endl;
}
}
}
exBSGS : \text{exBSGS}: exBSGS:
给定正整数 a , b , p a,b,p a,b,p , a , p a,p a,p 不一定互质,求满足
a x ≡ b ( mod p ) a^{x} \equiv b(\text{ mod } p) ax≡b( mod p)
的最小非负整数 x x x
分情况来看,如果 x = 0 x=0 x=0 时,满足 1 ≡ b ( mod p ) 1 \equiv b(\text{ mod } p) 1≡b( mod p),答案就是 0 0 0
如果 gcd ( a , p ) = 1 \gcd(a,p)=1 gcd(a,p)=1,那么就直接用朴素 BSGS \text{BSGS} BSGS 算法
如果 gcd ( a , p ) > 1 \gcd(a,p) > 1 gcd(a,p)>1,由裴蜀定理得
a x + k p = b a^x+kp = b ax+kp=b
设 gcd ( a , p ) = d \gcd(a,p)=d gcd(a,p)=d,如果 d ∤ p d\nmid p d∤p 那么无解,否则,等式两边同除 d d d 得
a d a x − 1 + k p d = b d \frac{a}{d}a^{x-1}+k\frac{p}{d} = \frac{b}{d} daax−1+kdp=db
等价于同余方程:
a d a x − 1 ≡ b d ( mod p d ) \frac{a}{d}a^{x-1} \equiv\frac{b}{d} (\text{ mod }\frac{p}{d}) daax−1≡db( mod dp)
由于 gcd ( a d , p d ) = 1 \gcd(\frac{a}{d},\frac{p}{d})=1 gcd(da,dp)=1 ,所以把 a d \frac{a}{d} da 移到等式右边,就是乘 a d \frac{a}{d} da 的逆元
a x − 1 ≡ b d ( a d ) − 1 ( mod p d ) a^{x-1} \equiv\frac{b}{d} (\frac{a}{d})^{-1}(\text{ mod }\frac{p}{d}) ax−1≡db(da)−1( mod dp)
用新变量替换:
( a ′ ) x ≡ b ′ ( mod p ′ ) (a')^{x} \equiv b'(\text{ mod } p') (a′)x≡b′( mod p′)
由于 x ≥ 1 x \ge 1 x≥1,这样就可以递归地用 BSGS \text{BSGS} BSGS 求解了,新的解就为 x + 1 x+1 x+1,逆元可以用扩展欧几里得算法求解。
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
int a, b, p;
int exgcd(int a, int b, int& x, int& y) {
if (!b) {
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int bsgs(int a, int b, int p) {
if (1 % p == b % p) return 0;
int k = sqrt(p) + 1;
unordered_map<int, int> mp;
for (int i = 0, j = b % p; i < k; i ++) {
mp[j] = i;
j = j * a % p;
}
int ak = 1;
for (int i = 0; i < k; i ++) ak = ak * a % p;
for (int i = 1, j = ak; i <= k; i ++) {
if (mp.count(j)) return i * k - mp[j];
j = j * ak % p;
}
return -1;
}
int exbsgs(int a, int b, int p) {
b = (b % p + p) % p;
if (1 % p == b % p) return 0;
int x, y;
int d = exgcd(a, p, x, y);
if (d > 1) {
if (b % d) return -2e9;
exgcd(a / d, p / d, x, y);
return exbsgs(a, b / d * x % (p / d), p / d) + 1;
}
return bsgs(a, b, p);
}
signed main() {
while (cin >> a >> p >> b, a || b || p) {
int res = exbsgs(a, b, p);
if (res < 0) {
cout << "No Solution" << endl;
} else {
cout << res << endl;
}
}
}