『笔记』BSGS

写在前面

开始之前先来两首 \(music\)

BSGS

简介

BSGS(baby-step giant-step),大步小步算法。

又被称为拔山盖世算法,又被称为北上广深算法。。。。

作用

求解满足

\[a^x \equiv b \pmod p \]

的最小自然数 \(x\)

其中 \(a \perp p\) ,方程的解 \(x\) 满足 $ 0 \leq x < p$ 且数据范围较大,无法直接枚举在 \(O(p)\) 时间内通过。

\[x=A \left \lceil \sqrt{p} \right \rceil-B \]

其中

\[0 \leq A,B \leq \left \lceil \sqrt{p} \right \rceil \]

则有

\[a^{\left \lceil \sqrt{p} \right \rceil-B} \equiv b \pmod p \]

由费马小定理得

\[a^{\left \lceil \sqrt{p} \right \rceil} \equiv b \cdot a^B \pmod p \]

我们已知 \(a,b\) 的取值,所以直接枚举 \(B\),计算 \(b \cdot a^B\) ,将其插入 \(hash\) 表,然后计算 \(a^{A\left \lceil \sqrt{p} \right \rceil}\),枚举所有 \(A\) 的值,看 \(hash\) 表中是否存在对应的 \(b \cdot a^B\),即可得到所有的解 \(x\)

由于 \(A,B\) 均小于 \(\leq \left \lceil \sqrt{p} \right \rceil\) 所以总时间复杂度为 \(O(\sqrt{p})\),若使用 \(map\) 则多一个 \(\log\)

例题

T1

计算

\[a^x \equiv b \pmod p \]

的最小非负整数解,无解时返回 \(-1\)

int BSGS(int a, int b, int p)
{
	map<int, int> h;
    h.clear();
	b %= p;
	int t = (int)sqrt(p) + 1;
	for (int i = 1; i <= i; i++)
	{
		int tmp = (long long)b * Qpow(a, i, p) % p;
		h[tmp] = i;
	}
	a = Qpow(a, t, p);
	if (!a)
		return !b ? 1 : -1;
	for (int i = 0; i <= t; i++)
	{
		int tmp = Qpow(a, i, p);
		if (h.find(tmp) == h.end())
			return -1;
		else if (i * t - h[tmp] >= 0)
			return i * t - h[tmp];
	}
	return -1;
}

拓展 BSGS

简介

朴素的 \(BSGS\) 只有当 \(p\) 为质数是才可保证正确性,但是 \(OI\) 比赛中很多时候善良有爱的出题人并不想给出质数,那 \(BSGS\) 是否依旧可行呢?

自然,我们可以通过各种手段,使得 \(a \perp p\)

前置芝士

\(a \equiv b(\bmod m)\)\(d\)\(a, b\)\(m\) 的任一正公因数,则有

\[\cfrac{a}{d} \equiv \cfrac{b}{d}\left(\bmod \cfrac{m}{d}\right) \]

\(a \equiv b(\bmod m), d \mid m, d>0\), 则 \(a \equiv b(\bmod d)\)

\(a \equiv b(\bmod m)\), 则 $a k \equiv b k(\bmod m) $。

思路

求解满足

\[a^x \equiv b \pmod p \]

的最小自然数 \(x\)

其中 \(p\) 不是质数。

\(d=\gcd(a,p)\)

\(d \nmid b\)\(b=1\)\(x=0\) 。否则,由裴蜀定理得,原方程无解。

不妨假设 \(d \mid b\),将原方程同时除以 \(d_1\)

\[\cfrac{a}{d_{1}} \cdot a^{x-1} \equiv \cfrac{b}{d_{1}} \pmod{\cfrac{p}{d_{1}}} \]

此时对 \(a,p\) 再次进行判断,若 \(a\)\(\cfrac{p}{d_1}\) 仍旧不互质,那么再除。

\(d_2=\gcd(a,\cfrac{p}{d_1})\),如果,\(d \nmid b_2\) 则原方程出现无解情况。否则对原方程同时除以 \(d_2\),得

\[\cfrac{a^{2}}{d_{1} d_{2}} \cdot a^{x-2} \equiv \cfrac{b}{d_{1} d_{2}} \pmod{\cfrac{p}{d_{1} d_{2}}} \]

同样执行上述判读和除法操作,直到 \(d=1\)

此时有

\[a^{x-k} \cfrac{a^k}{\prod \limits_{i=1}^{k}d_i} \equiv \cfrac{b}{\prod \limits_{i=1}^{k}d_i}\pmod{\cfrac{p}{\prod\limits_{i=1}^{k}d_i}} \]

\[\begin{aligned} &P=\frac{p}{\prod \limits_{i=1}^{p} d_{i}},\\ &B=\frac{b}{\prod \limits_{i=1}^{k} d_{i}},\\ &C=\frac{a^{k}}{\prod \limits_{i=1}^{k} d_{i}},\\ &X=x-k \end{aligned} \]

则有

\[A^{X} C \equiv B \pmod{P} \]

此时的 \(A\) 是与 \(P\) 互质的,便可以用朴素的 \(BSGS\) 求解了。

/*

Name: P4195 【模板】扩展BSGS

Solution: 扩展BSGS
   

By Frather_

*/
#include <iostream>
#include <map>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#define int long long
#define InF 0x3f3f3f3f
#define kMax 10e5
#define kMin -10e5
#define kMod 998244353
using namespace std;
/*==================================================快读*/
inline int read()
{
    int X = 0, F = 1;
    char CH = getchar();
    while (CH < '0' || CH > '9')
    {
        if (CH == '-')
            F = -1;
        CH = getchar();
    }
    while (CH >= '0' && CH <= '9')
    {
        X = (X << 3) + (X << 1) + (CH ^ 48);
        CH = getchar();
    }
    return X * F;
}
/*===============================================定义变量*/
int a, p, b; //a^x=b (mod p)
/*=============================================自定义函数*/
int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;
}

int Qpow(int a, int p, int kmod)
{
    int res = 1;
    while (p)
    {
        if (p & 1)
            res = res * a % kmod;
        a = a * a % kmod;
        p >>= 1;
    }
    return res;
}

// int BSGS(int a, int b, int p, int g)
// {
//     map<int, int> hs;
//     hs.clear();
//     int t = (int)sqrt(p) + 1;
//     for (int i = 0; i < t; i++)
//     {
//         int v = 1ll * b * Qpow(a, i, p) % p;
//         hs[v] = i;
//     }
//     a = Qpow(a, t, p);
//     if (!a)
//         return !b ? 1 : -1;
//     for (int i = 0; i <= t; i++)
//     {
//         int v = Qpow(a, i, p);
//         // if (hs.find(v) != hs.end())
//         //     return -1;
//         // if (hs[v] >= 0 && i * t - hs[v] >= 0)
//         //     return i * t - hs[v];
//         int j = hs.find(v) == hs.end() ? -1 : hs[v];
//         if (j >= 0 && i * t - j >= 0)
//             return i * t - j;
//     }
//     return -1;
// }

int exgcd(int &x, int &y, int a, int b)
{
    if (!b)
    {
        x = 1;
        y = 0;
        return a;
    }
    int t = exgcd(y, x, b, a % b);
    y -= x * (a / b);
    return t;
}

int BSGS(int a, int b, int p, int g)
{
    map<int, int> hs;
    hs.clear();
    int t = ceil(sqrt(p));
    int x, y;
    exgcd(x, y, g, p);
    b = (b * x % p + p) % p;
    int q = Qpow(a, t, p);
    exgcd(x, y, q, p);
    q = (x * p + p) % p;
    for (int i = 1, j = 0; j <= t; j++, i = i * a % p)
        if (!hs.count(i))
            hs[i] = j;
    for (int i = b, j = 0; j <= t; j++, i = i * q % p)
        if (hs[i])
            return j * t + hs[i];
    return -1;
}
int exBSGS(int a, int b, int p)
{
    int res = 1;
    int k=0, g=1;
    if (b == 1)
        return 0;
    while ((g = gcd(a, p)) > 1)
    {
        if (b % g)
            return -1;
        k++;
        b /= g;
        p /= g;
        res = res * (a / g) % p;
        if (res == b)
            return k;
    }
    return (g = BSGS(a, b, p, res)) == -1 ? -1 : g + k;
}
/*=================================================主函数*/
signed main()
{
    while (true)
    {
        a = read();
        p = read();
        b = read();
        if (!a && !p && !b)
            return 0;
        a %= p;
        b %= p;
        int ans = exBSGS(a, b, p);
        if (ans < 0)
            printf("No Solution\n");
        else
            printf("%d\n", ans);
    }
    return 0;
}

最后

掌握的不是很好哇,如果有错误欢迎向作者提出,蟹蟹!

posted @ 2021-05-05 14:07  Frather  阅读(98)  评论(0编辑  收藏  举报