BSGS(Baby-Step-Giant-Step)算法,用来解决 Ax≡B(modC)(0≤x<C) A x ≡ B ( mod C ) ( 0 ≤ x < C ) 这样的问题。
具体讲BSGS算法之前,先介绍一下离散对数。
在整数中,离散对数(英语:Discrete logarithm)是一种基于同余运算和原根的一种对数运算。简单来说,离散对数就是在同余意义下的对数运算。
BSGS B S G S 算法
接下来讲BSGS的算法流程,原始的BSGS要求 C C 是素数,拓展BSGS则可以处理 不是素数的情形。
我们令 m=⌈C−−√⌉ m = ⌈ C ⌉ ,那么 x x 可以表示为 这样的形式,则 Ax=Ami∗Aj A x = A m i ∗ A j ,其中 0≤i<m 0 ≤ i < m , 0≤j<m 0 ≤ j < m 。
我们接下来就可以在 Θ(C−−√) Θ ( C ) 的时间内枚举 i i ,我们令 ,则 D∗Aj≡B(modC) D ∗ A j ≡ B ( mod C ) ,就可以用ExtendedGCD求解出 Aj A j ,因为 C C 是素数,除了 是 C C 的情况要特判以外总有 ,即这个方程总是有解的。
现在我们求出了 Aj A j ,怎么快速知道 j j 的值呢,由于是模 意义,所以直接套上对数肯定不合适,那我们可以查表啊!先用 Θ(C−−√) Θ ( C ) 的时间内将 Ak,0≤k<m A k , 0 ≤ k < m 的值全部插进哈希表里面,最后直接 Θ(1) Θ ( 1 ) 查表就可以了。
BSGS B S G S 代码
class Hash {
private:
static const int HASHMOD = 3894229;
int top, hash[HASHMOD + 10], stack[1 << 16];
LL value[HASHMOD + 10];
int locate(int x) {
int h = x % HASHMOD;
while (hash[h] != -1 && hash[h] != x) ++h;
return h;
}
public:
Hash() : top(0) {
memset(hash, 0xff, sizeof hash);
}
void insert(int x, int v) {
int h = locate(x);
if (hash[h] == -1)
hash[h] = x, value[h] = v, stack[++top] = h;
}
int query(int x) {
int h = locate(x);
return hash[h] == x ? value[h] : -1;
}
void clear() {
while (top)
hash[stack[top--]] = -1;
}
} hash;
struct Triple {
LL x, y, z;
Triple() {}
Triple(LL x, LL y, LL z) : x(x), y(y), z(z) {}
};
Triple ExtendedGCD(LL a, LL b) {
if (b == 0) return Triple(1, 0, a);
Triple last = ExtendedGCD(b, a % b);
return Triple(last.y, last.x - a / b * last.y, last.z);
}
LL BSGS(LL A, LL B, LL C) {
LL root = static_cast< int >(std::ceil(std::sqrt(C)));
hash.clear();
LL base = 1;
for (LL i = 0; i < root; i++) {
hash.insert(base, i);
base = base * A % C;
}
LL j = -1;
for (LL i = 0; i < root; i++) {
Triple ret = ExtendedGCD(D, C);
int c = C / ret.z;
ret.x = (ret.x * B / ret.z % c + c) % c;
j = hash.query(ret.x);
if (j != -1) return i * root + j;
D = D * base % C;
}
return -1;
}
Extended BSGS E x t e n d e d B S G S 算法
Extended BSGS E x t e n d e d B S G S 不要求 C C 是素数,主要思想就是约简 ,使它变成一个素数。
首先,要了解同余有以下性质:
(k,m)=d ( k , m ) = d , ka≡kb(modm) k a ≡ k b ( mod m ) ⇒ ⇒ a≡b(modmd) a ≡ b ( mod m d )
特殊的, ka≡kb(modm)⇒ k a ≡ k b ( mod m ) ⇒ a≡b(modm) a ≡ b ( mod m )
那么我们就可以来讲如何消去因子了。
我们可以每次消去 GCD(A,C) G C D ( A , C ) ,如果 GCD(A,C) G C D ( A , C ) 不能整除 B B ,那肯定是没有解的.
接下来考虑 的情况。
这样子就给出了单步的计算过程,具体操作就是在 B B 和 中除掉 GCD(A,C) G C D ( A , C ) , 用一个临时变量 D D 每次乘 ,同时用累加器 cnt c n t 每次累加 1 1 , 不变。
最后,问题就转化为 D∗Ax−cnt≡B(modC) D ∗ A x − c n t ≡ B ( mod C ) ,接下来就又是 BSGS B S G S 的活儿了。
但是有一点需要注意,在后面的时候我们默认了 x x 是大于等于 cnt c n t 的,但是很明显 x<cnt x < c n t 的解也是可能存在的。那么我们只需要在之前做 Θ(log2C) Θ ( log 2 C ) 次枚举排除这种情况就可以了。
Extended BSGS E x t e n d e d B S G S 代码
class Hash {
private:
static const int HASHMOD = 3894229;
int top, hash[HASHMOD + 10], stack[1 << 16];
LL value[HASHMOD + 10];
int locate(int x) {
int h = x % HASHMOD;
while (hash[h] != -1 && hash[h] != x) ++h;
return h;
}
public:
Hash() : top(0) {
memset(hash, 0xff, sizeof hash);
}
void insert(int x, int v) {
int h = locate(x);
if (hash[h] == -1)
hash[h] = x, value[h] = v, stack[++top] = h;
}
int query(int x) {
int h = locate(x);
return hash[h] == x ? value[h] : -1;
}
void clear() {
while (top)
hash[stack[top--]] = -1;
}
} hash;
struct Triple {
LL x, y, z;
Triple() {}
Triple(LL x, LL y, LL z) : x(x), y(y), z(z) {}
};
Triple ExtendedGCD(LL a, LL b) {
if (b == 0) return Triple(1, 0, a);
Triple last = ExtendedGCD(b, a % b);
return Triple(last.y, last.x - a / b * last.y, last.z);
}
LL ExtendedBSGS(LL A, LL B, LL C) {
LL tmp = 1, cnt = 0, D = 1;
for (int i = 0; i < 32; i++) {
if (tmp == B) return i;
tmp = tmp * A % C;
}
for (Triple ret; (ret = ExtendedGCD(A, C)).z != 1; cnt++) {
if (B % ret.z) return -1;
B /= ret.z; C /= ret.z;
D = D * A / ret.z % C;
}
LL root = static_cast< int >(std::ceil(std::sqrt(C)));
hash.clear();
LL base = 1;
for (LL i = 0; i < root; i++) {
hash.insert(base, i);
base = base * A % C;
}
LL j = -1;
for (LL i = 0; i < root; i++) {
Triple ret = ExtendedGCD(D, C);
int c = C / ret.z;
ret.x = (ret.x * B / ret.z % c + c) % c;
j = hash.query(ret.x);
if (j != -1) return i * root + j + cnt;
D = D * base % C;
}
return -1;
}