给定整数 a , b , p a,b,p a,b,p 且 a , p a,p a,p 互质,请求出高次同余方程
a x ≡ b ( m o d p ) a^x\equiv b\pmod p ax≡b(modp)
的非负整数解.
首先,
a 0 ≡ 1 ( m o d p ) a^0\equiv 1\pmod p a0≡1(modp)
又由欧拉定理知
a φ ( p ) ≡ 1 ( m o d p ) a^{\varphi(p)}\equiv 1\pmod p aφ(p)≡1(modp)
所以出现了周期.
所以 x x x 在区间 [ 0 , φ ( p ) ) [0,\varphi(p)) [0,φ(p)) 中一定有,否则就是无解.
那么暴力就是遍历 0 ∼ φ ( p ) − 1 0\sim \varphi(p)-1 0∼φ(p)−1 去试每一个,时间复杂度为 O ( φ ( p ) ) \mathcal{O}(\varphi(p)) O(φ(p)).
B
a
b
y
S
t
e
p
,
G
i
a
n
t
S
t
e
p
\rm Baby\ Step,Giant\ Step
Baby Step,Giant Step,简称
B
S
G
S
\rm BSGS
BSGS 算法,俗称大步小步算法或拔山盖世.
当 a , p a,p a,p 不互质时就得用 e x B S G S \rm exBSGS exBSGS 了.
把
x
x
x 表示成带余除法的样子:
x
=
i
t
+
j
x=it+j
x=it+j,其中
t
t
t 是某个整数(取值待会讨论),那么有
0
≤
i
≤
⌊
φ
(
p
)
t
⌋
,
0
≤
j
≤
t
−
1
0\le i\le\left\lfloor\dfrac{\varphi(p)}{t} \right\rfloor,0\le j\le t-1
0≤i≤⌊tφ(p)⌋,0≤j≤t−1.
a
x
≡
b
(
m
o
d
p
)
a
i
t
+
j
≡
b
(
m
o
d
p
)
a
i
t
≡
b
⋅
a
−
j
(
m
o
d
p
)
(
a
t
)
i
≡
b
⋅
a
−
j
(
m
o
d
p
)
a^x\equiv b\pmod p\\ a^{it+j}\equiv b\pmod p\\ a^{it}\equiv b\cdot a^{-j}\pmod p\\ (a^t)^i\equiv b\cdot a^{-j}\pmod p
ax≡b(modp)ait+j≡b(modp)ait≡b⋅a−j(modp)(at)i≡b⋅a−j(modp)
B
a
b
y
S
t
e
p
\rm Baby\ Step
Baby Step:
∀
j
∈
[
0
,
t
−
1
]
\forall j\in [0,t-1]
∀j∈[0,t−1],将
(
b
⋅
a
−
j
)
m
o
d
p
(b\cdot a^{-j})\bmod p
(b⋅a−j)modp 扔进
H
a
s
h
\rm Hash
Hash 表.
G i a n t S t e p \rm Giant\ Step Giant Step: ∀ i ∈ [ 0 , ⌊ φ ( p ) t ⌋ ] \forall i\in\left[0,\left\lfloor \dfrac{\varphi(p)}{t}\right\rfloor\right] ∀i∈[0,⌊tφ(p)⌋],在 H a s h \rm Hash Hash 表中找是否有 ( a t ) i m o d p (a^t)^i\bmod p (at)imodp,若有,则 x = i t + j x=it+j x=it+j 就是其中一个解.
B a b y S t e p \rm Baby\ Step Baby Step 的时间复杂度为 O ( t log t ) \mathcal{O}(t\log t) O(tlogt), G i a n t S t e p \rm Giant\ Step Giant Step 的时间复杂度为 O ( ⌊ φ ( p ) t ⌋ log t ) \mathcal{O}\left(\left\lfloor\dfrac{\varphi(p)}{t} \right\rfloor\log t\right) O(⌊tφ(p)⌋logt),所以总的时间复杂度就是 O ( max ( t , ⌊ φ ( p ) t ⌋ ) log t ) \mathcal{O}\left(\max\left(t,\left\lfloor\dfrac{\varphi(p)}{t} \right\rfloor\right)\log t\right) O(max(t,⌊tφ(p)⌋)logt),这个很像分块的时间,当取 t = ⌊ φ ( p ) ⌋ t=\left\lfloor\sqrt{\varphi(p)} \right\rfloor t=⌊φ(p)⌋ 时最优.
但是有一个问题,上式右边是 b ⋅ a − j b\cdot a^{-j} b⋅a−j,导致需要处理逆元,肥肠麻烦.
所以我们改为令 x = i t − j x=it-j x=it−j.
x
=
i
t
−
j
x=it-j
x=it−j,那么有
0
≤
i
≤
⌈
φ
(
p
)
t
⌉
0\le i\le \left\lceil\dfrac{\varphi(p)}{t}\right\rceil
0≤i≤⌈tφ(p)⌉,
0
≤
j
≤
t
−
1
0\le j\le t-1
0≤j≤t−1.
a
x
≡
b
(
m
o
d
p
)
a
i
t
−
j
≡
b
(
m
o
d
p
)
(
a
t
)
i
≡
b
⋅
a
j
(
m
o
d
p
)
a^x\equiv b\pmod p\\ a^{it-j}\equiv b\pmod p\\ (a^t)^i\equiv b\cdot a^j\pmod p
ax≡b(modp)ait−j≡b(modp)(at)i≡b⋅aj(modp)
B
a
b
y
S
t
e
p
\rm Baby\ Step
Baby Step:
∀
j
∈
[
0
,
t
−
1
]
\forall j\in [0,t-1]
∀j∈[0,t−1],将
(
b
⋅
a
j
)
m
o
d
p
(b\cdot a^{j})\bmod p
(b⋅aj)modp 扔进
H
a
s
h
\rm Hash
Hash 表.
G i a n t S t e p \rm Giant\ Step Giant Step: ∀ i ∈ [ 0 , ⌈ φ ( p ) t ⌉ ] \forall i\in\left[0,\left\lceil \dfrac{\varphi(p)}{t}\right\rceil\right] ∀i∈[0,⌈tφ(p)⌉],在 H a s h \rm Hash Hash 表中找是否有 ( a t ) i m o d p (a^t)^i\bmod p (at)imodp,若有且 i t − j ≥ 0 it-j\ge 0 it−j≥0,则 x = i t − j x=it-j x=it−j 就是其中一个解.
总的时间复杂度是 O ( max ( t , ⌈ φ ( p ) t ⌉ ) log t ) \mathcal{O}\left(\max\left(t,\left\lceil\dfrac{\varphi(p)}{t} \right\rceil\right)\log t\right) O(max(t,⌈tφ(p)⌉)logt),当取 t = ⌈ φ ( p ) ⌉ t=\left\lceil\sqrt{\varphi(p)} \right\rceil t=⌈φ(p)⌉ 时最优.
如果用 H a s h \rm Hash Hash 是 O ( p ) \mathcal{O}(\sqrt{p}) O(p),如果用 m a p \rm map map 是 O ( p log p ) \mathcal{O}(\sqrt{p}\log\sqrt{p}) O(plogp).
你会发现它和求单个数逆元一样,时间都只与模数有关,很有意思.
P3846 [TJOI2007] 可爱的质数/【模板】BSGS
对于本题,要求出最小解,因为 0 ≤ j ≤ t − 1 0\le j\le t-1 0≤j≤t−1,所以要使 x x x 最小只需使 i i i 最小,按顺序遍历,第一次找到就返回.
update:
int val = (ll)b * aj % p;
hash[val] = j;
有可能存在两个 j j j 使得 b ⋅ a j m o d p b\cdot a^j\bmod p b⋅ajmodp 相等,但因为我们是按顺序遍历,所以一定是大的覆盖小的,就会使得 i t − j it-j it−j 更小,这正是题目要求的。所以不用担心哈希冲突的问题。
Code \text{Code} 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;
}
矩阵 B S G S \rm BSGS BSGS
【BZOJ4128】Matrix
给定矩阵 A , B A,B A,B 和模数 P P P,求最小的 x x x,满足 A x ≡ B ( m o d P ) A^x\equiv B\pmod P Ax≡B(modP).
用矩阵乘法 + + + 矩阵 H a s h \rm Hash Hash 即可
Code \text{Code} 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;
}