【模板】BSGS & Extended BSGS

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

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

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

BSGS B S G S 算法

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

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

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

现在我们求出了 Aj A j ,怎么快速知道 j j 的值呢,由于是模 C 意义,所以直接套上对数肯定不合适,那我们可以查表啊!先用 Θ(C) Θ ( C ) 的时间内将 Ak,0k<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 是素数,主要思想就是约简 C ,使它变成一个素数。

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

(k,m)=d ( k , m ) = d , kakb(modm) k a ≡ k b ( mod m ) ab(modmd) a ≡ b ( mod m d )

特殊的, kakb(modm) k a ≡ k b ( mod m ) ⇒ ab(modm) a ≡ b ( mod m )

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

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

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

AxAx1GCD(A,C)AGCD(A,C)Ax1AGCD(A,C)BBGCD(A,C)GCD(A,C)BGCD(A,C)(modm)(modmGCD(A,C)GCD(A,C))(modmGCD(A,C)) 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 B C 中除掉 GCD(A,C) G C D ( A , C ) , 用一个临时变量 D D 每次乘 A/GCD(A,C) ,同时用累加器 cnt c n t 每次累加 1 1 A 不变。

最后,问题就转化为 DAxcntB(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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值