BSGS(Baby-Step-Giant-Step)算法,用来解决 这样的问题。
具体讲BSGS算法之前,先介绍一下离散对数。
在整数中,离散对数(英语:Discrete logarithm)是一种基于同余运算和原根的一种对数运算。简单来说,离散对数就是在同余意义下的对数运算。
算法
接下来讲BSGS的算法流程,原始的BSGS要求 是素数,拓展BSGS则可以处理 不是素数的情形。
我们令 ,那么 可以表示为 这样的形式,则 ,其中 , 。
我们接下来就可以在 的时间内枚举 ,我们令 ,则 ,就可以用ExtendedGCD求解出 ,因为 是素数,除了 是 的情况要特判以外总有 ,即这个方程总是有解的。
现在我们求出了 ,怎么快速知道 的值呢,由于是模 意义,所以直接套上对数肯定不合适,那我们可以查表啊!先用 的时间内将 的值全部插进哈希表里面,最后直接 查表就可以了。
代码
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;
}
算法
不要求 是素数,主要思想就是约简 ,使它变成一个素数。
首先,要了解同余有以下性质:
,
特殊的,
那么我们就可以来讲如何消去因子了。
我们可以每次消去 ,如果 不能整除 ,那肯定是没有解的.
接下来考虑 的情况。
这样子就给出了单步的计算过程,具体操作就是在 和 中除掉 , 用一个临时变量 每次乘 ,同时用累加器 每次累加 , 不变。
最后,问题就转化为 ,接下来就又是 的活儿了。
但是有一点需要注意,在后面的时候我们默认了 是大于等于 的,但是很明显 的解也是可能存在的。那么我们只需要在之前做 次枚举排除这种情况就可以了。
代码
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;
}