【模板】BSGS & Extended BSGS

BSGS(Baby-Step-Giant-Step)算法,用来解决 A x B ( mod C ) ( 0 x < C ) 这样的问题。

具体讲BSGS算法之前,先介绍一下离散对数。

在整数中,离散对数(英语:Discrete logarithm)是一种基于同余运算和原根的一种对数运算。简单来说,离散对数就是在同余意义下的对数运算。

B S G S 算法

接下来讲BSGS的算法流程,原始的BSGS要求 C 是素数,拓展BSGS则可以处理 C 不是素数的情形。

我们令 m = C ,那么 x 可以表示为 i m + j 这样的形式,则 A x = A m i A j ,其中 0 i < m , 0 j < m

我们接下来就可以在 Θ ( C ) 的时间内枚举 i ,我们令 D = A m i ,则 D A j B ( mod C ) ,就可以用ExtendedGCD求解出 A j ,因为 C 是素数,除了 A C 的情况要特判以外总有 G C D ( D , C ) = 1 ,即这个方程总是有解的。

现在我们求出了 A j ,怎么快速知道 j 的值呢,由于是模 C 意义,所以直接套上对数肯定不合适,那我们可以查表啊!先用 Θ ( C ) 的时间内将 A k , 0 k < m 的值全部插进哈希表里面,最后直接 Θ ( 1 ) 查表就可以了。

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;
}

E x t e n d e d   B S G S 算法

E x t e n d e d   B S G S 不要求 C 是素数,主要思想就是约简 C ,使它变成一个素数。

首先,要了解同余有以下性质:

( k , m ) = d , k a k b ( mod m ) a b ( mod m d )

特殊的, k a k b ( mod m ) a b ( mod m )

那么我们就可以来讲如何消去因子了。

我们可以每次消去 G C D ( A , C ) ,如果 G C D ( A , C ) 不能整除 B ,那肯定是没有解的.

接下来考虑 G C D ( A , C )   |   B 的情况。

A x B ( mod m ) A x 1 G C D ( A , C ) A G C D ( A , C ) B G C D ( A , C ) G C D ( A , C ) ( mod m G C D ( A , C ) G C D ( A , C ) ) A x 1 A G C D ( A , C ) B G C D ( A , C ) ( mod m G C D ( A , C ) )

这样子就给出了单步的计算过程,具体操作就是在 B C 中除掉 G C D ( A , C ) , 用一个临时变量 D 每次乘 A / G C D ( A , C ) ,同时用累加器 c n t 每次累加 1 A 不变。

最后,问题就转化为 D A x c n t B ( mod C ) ,接下来就又是 B S G S 的活儿了。

但是有一点需要注意,在后面的时候我们默认了 x 是大于等于 c n t 的,但是很明显 x < c n t 的解也是可能存在的。那么我们只需要在之前做 Θ ( log 2 C ) 次枚举排除这种情况就可以了。

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;
}

猜你喜欢

转载自blog.csdn.net/Diogenes_/article/details/81361966