【数学】Baby Step,Giant Step
给定整数 \(a,b,p\) 且 \(a,p\) 互质,请求出高次同余方程
的非负整数解.
首先,
又由欧拉定理知
所以出现了周期.
所以 \(x\) 在区间 \([0,\varphi(p))\) 中一定有,否则就是无解.
那么暴力就是遍历 \(0\sim \varphi(p)-1\) 去试每一个,时间复杂度为 \(\mathcal{O}(\varphi(p))\).
\(\rm Baby\ Step,Giant\ Step\),简称 \(\rm BSGS\) 算法,俗称大步小步算法或拔山盖世.
当 \(a,p\) 不互质时就得用 \(\rm exBSGS\) 了.
把 \(x\) 表示成带余除法的样子:\(x=it+j\),其中 \(t\) 是某个整数(取值待会讨论),那么有 \(0\le i\le\left\lfloor\dfrac{\varphi(p)}{t} \right\rfloor,0\le j\le t-1\).
\(\rm Baby\ Step\):\(\forall j\in [0,t-1]\),将 \((b\cdot a^{-j})\bmod p\) 扔进 \(\rm Hash\) 表.
\(\rm Giant\ Step\):\(\forall i\in\left[0,\left\lfloor \dfrac{\varphi(p)}{t}\right\rfloor\right]\),在 \(\rm Hash\) 表中找是否有 \((a^t)^i\bmod p\),若有,则 \(x=it+j\) 就是其中一个解.
\(\rm Baby\ Step\) 的时间复杂度为 \(\mathcal{O}(t\log t)\),\(\rm Giant\ Step\) 的时间复杂度为 \(\mathcal{O}\left(\left\lfloor\dfrac{\varphi(p)}{t} \right\rfloor\log t\right)\),所以总的时间复杂度就是 \(\mathcal{O}\left(\max\left(t,\left\lfloor\dfrac{\varphi(p)}{t} \right\rfloor\right)\log t\right)\),这个很像分块的时间,当取 \(t=\left\lfloor\sqrt{\varphi(p)} \right\rfloor\) 时最优.
但是有一个问题,上式右边是 \(b\cdot a^{-j}\),导致需要处理逆元,肥肠麻烦.
所以我们改为令 \(x=it-j\).
\(x=it-j\),那么有 \(0\le i\le \left\lceil\dfrac{\varphi(p)}{t}\right\rceil\),\(0\le j\le t-1\).
\(\rm Baby\ Step\):\(\forall j\in [0,t-1]\),将 \((b\cdot a^{j})\bmod p\) 扔进 \(\rm Hash\) 表.
\(\rm Giant\ Step\):\(\forall i\in\left[0,\left\lceil \dfrac{\varphi(p)}{t}\right\rceil\right]\),在 \(\rm Hash\) 表中找是否有 \((a^t)^i\bmod p\),若有且 \(it-j\ge 0\),则 \(x=it-j\) 就是其中一个解.
总的时间复杂度是 \(\mathcal{O}\left(\max\left(t,\left\lceil\dfrac{\varphi(p)}{t} \right\rceil\right)\log t\right)\),当取 \(t=\left\lceil\sqrt{\varphi(p)} \right\rceil\) 时最优.
如果用 \(\rm Hash\) 是 \(\mathcal{O}(\sqrt{p})\),如果用 \(\rm map\) 是 \(\mathcal{O}(\sqrt{p}\log\sqrt{p})\).
你会发现它和求单个数逆元一样,时间都只与模数有关,很有意思.
P3846 [TJOI2007] 可爱的质数/【模板】BSGS
对于本题,要求出最小解,因为 \(0\le j\le t-1\),所以要使 \(x\) 最小只需使 \(i\) 最小,按顺序遍历,第一次找到就返回.
update:
int val = (ll)b * aj % p;
hash[val] = j;
有可能存在两个 \(j\) 使得 \(b\cdot a^j\bmod p\) 相等,但因为我们是按顺序遍历,所以一定是大的覆盖小的,就会使得 \(it-j\) 更小,这正是题目要求的。所以不用担心哈希冲突的问题。
\(\text{Code}\)
注意:在查找时最好不要用
int j = hash[ai] - 1;
if (j >= 0 && i * t - j >= 0)
{
return i * t - j;
}
因为在查找不到时会新建一个,导致浪费大量时间.
#include <iostream>
#include <cstdio>
#include <map>
#include <cmath>
typedef long long ll;
using namespace std;
int phi(int p)
{
int ans = p;
for (int i = 2; (ll)i * i <= (ll)p; i++)
{
if (p % i == 0)
{
ans = ans / i * (i - 1);
while (p % i == 0)
{
p /= i;
}
}
}
if (p != 1)
{
ans = ans / p * (p - 1);
}
return ans;
}
int bsgs(int a, int b, int p)
{
b %= p;
map<int, int> hash;
int t = ceil(sqrt(phi(p))), aj = 1;
for (int j = 0; j < t; j++)
{
int val = (ll)b * aj % p;
hash[val] = j;
aj = (ll)aj * a % p;
}
a = aj;
int ai = 1;
for (int i = 0; i <= t; i++)
{
if (hash.find(ai) != hash.end())
{
int j = hash[ai];
if (i * t - j >= 0)
{
return i * t - j;
}
}
ai = (ll)ai * a % p;
}
return -1;
}
int main()
{
int a, b, p;
scanf("%d%d%d", &p, &a, &b);
int res = bsgs(a, b, p);
if (res == -1)
{
puts("no solution");
}
else
{
printf("%d\n", res);
}
return 0;
}
矩阵 \(\rm BSGS\)
【BZOJ4128】Matrix
给定矩阵 \(A,B\) 和模数 \(P\),求最小的 \(x\),满足 \(A^x\equiv B\pmod P\).
用矩阵乘法 \(+\) 矩阵 \(\rm Hash\) 即可
\(\text{Code}\)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <cmath>
using namespace std;
const int MAXN = 75;
const int BASE = 233;
const int MOD = 19260817;
int n, p;
struct matrix
{
int a[MAXN][MAXN];
void init()
{
memset(a, 0, sizeof(a));
}
void build()
{
for (int i = 1; i <= n; i++)
{
a[i][i] = 1;
}
}
matrix operator *(const matrix &x)const
{
matrix res;
res.init();
for (int k = 1; k <= n; k++)
{
for (int i = 1; i <= n; i++)
{
if (!a[i][k])
{
continue;
}
for (int j = 1; j <= n; j++)
{
res.a[i][j] = (res.a[i][j] + a[i][k] * x.a[k][j]) % p;
}
}
}
return res;
}
int hash()
{
int ans = 0;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
ans = (ans * BASE + a[i][j]) % MOD;
}
}
return ans;
}
};
int bsgs(matrix a, matrix b)
{
map<int, int> hash;
int t = ceil(sqrt(p));
matrix aj;
aj.init(), aj.build();
for (int j = 0; j < t; j++)
{
matrix val = b * aj;
hash[val.hash()] = j;
aj = aj * a;
}
a = aj;
matrix ai;
ai.init(), ai.build();
for (int i = 0; i <= t; i++)
{
int x = ai.hash();
if (hash.find(x) != hash.end())
{
int j = hash[x];
if (i * t - j >= 0)
{
return i * t - j;
}
}
ai = ai * a;
}
}
int main()
{
scanf("%d%d", &n, &p);
matrix a;
a.init();
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
scanf("%d", &a.a[i][j]);
}
}
matrix b;
b.init();
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
scanf("%d", &b.a[i][j]);
}
}
printf("%d\n", bsgs(a, b));
return 0;
}