大步小步法

BSGS

大步小步算法(baby step giant step,BSGS),是一种用来求解离散对数(即模意义下对数)的算法,即给出 a x ≡ b ( m o d m ) a^{x} \equiv b\pmod{m} axb(modm) a , b , m a,b,m a,b,m的值(这里保证a和m互质),求解x。

实际上,大步小步算法就是对暴力枚举的一个简单的改进,使用了类似meet in the middle的思想。我们把 x x x拆成 A t − B At-B AtB,拆分成这种形式是为了后面移项后指数上能是正的,然后原式就可以化为 a A t − B ≡ b ( m o d m ) a^{At-B } \equiv b \pmod{m} aAtBb(modm),即 a A t ≡ b a B ( m o d m ) a^{At} \equiv ba^{B} \pmod{m} aAtbaB(modm)。对于这个式子,如果我们固定一个 t t t值的话,随着 B B B的取值不同 A A A的取值也会跟着变化(反过来说也一样)。然后我们预计算出右侧所有可能的取值并存下来,接着计算出左边可能的值,当发现某个值已经在右边出现过,这时的 A t − B At-B AtB 就是我们要求的 x x x

B可能的取值有 m m o d    t m \mod t mmodt个,A可能的取值有 ⌊ m / t ⌋ \left \lfloor m/t \right \rfloor m/t个,不难看出,取 t = ⌈ m ⌉ t=\left \lceil \sqrt{m} \right \rceil t=m 是最好的。此时取 A , B ∈ [ 1 , t ] A,B\in [1,t] A,B[1,t]可以保证把 x ∈ [ 1 , m − 1 ] x\in [1,m-1] x[1,m1]全部枚举一遍。时间复杂度为 O ( m ) O(\sqrt{m} ) O(m )

至于如何判定右边的数有没有出现过,用哈希表是最简单的(STL中的unordered_map是用哈希表实现的,一般可以直接使用),如果用map,复杂度会多一个log 。下面给出洛谷上的模板题和实现代码。
P3846 [TJOI2007] 可爱的质数/【模板】BSGS

ll BSGS(ll a, ll b, ll m)
{
    unordered_map<ll, ll> hs;
    ll cur = 1, t = sqrt(m) + 1;
    for (ll B = 1; B <= t; B++)
        {
            cur = cur * a % m;
            hs[b * cur % m] = B;
        }
    ll now = cur; // 此时cur为a^t
    for (int A = 1; A <= t; A++)
        {
            auto it = hs.find(now);
            if (it != hs.end())
                return A * t - it->second;
            now = now * cur % m;
        }
    return -1;
}

扩展BSGS

上面的算法只针对 a a a m m m互质的情形,如果不互质,又该如何计算?我们尝试把它转化成 a a a m m m互质的情形。

a a a m m m不互质,则存在他们的最大公因数 d = g c d ( a , m ) d=gcd(a,m) d=gcd(a,m)。将最大公因数除去即可转化成互质的情况,即 a x ≡ b ( m o d m ) a^{x} \equiv b \pmod{m} axb(modm)都除以 d d d,得到 a d a x − 1 ≡ b d ( m o d m d ) \frac{a}{d} a^{x-1} \equiv \frac{b}{d} \pmod{\frac{m}{d}} daax1db(moddm),将 a d \frac{a}{d} da记作系数 k k k。若 d d d不能整除 b b b,则无解;反之,就能直接用大步小步法求解了(左侧多了一个系数,但这显然影响不大,把大步小步算法稍作修改即可)。需要注意的是,此时用大步小步法求出来的是 x − 1 x-1 x1,最后结果还需要加一才行。

参考代码如下:

ll exBSGS(ll a, ll b, ll m)
{
    a %= m;
    b %= m;
    if (b == 1 || m == 1)
        return 0;
    ll d = gcd(a, m);
    if (b % d != 0)
        return -1;
    ll k = a / d;
    b /= d;
    m /= d;
    unordered_map<ll, ll> hs;
    ll cur = 1, t = sqrt(m) + 1;
    for (ll B = 1; B <= t; B++)
    {
        cur = cur * a % m;
        hs[b * cur % m] = B;
    }
    ll now = k * cur % m; // 此时cur为a^t,注意乘上系数
    for (int A = 1; A <= t; A++)
    {
        auto it = hs.find(now);
        if (it != hs.end())
            return A * t - it->second + 1; // 别忘了加一
        now = now * cur % m;
    }
    return -1;
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

h0l10w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值