什么是阶
若
(
a
,
m
)
=
1
(a,m)=1
(a,m)=1,则使
a
l
≡
1
(
m
o
d
m
)
a^l\equiv1~(mod~m)
al≡1 (mod m) 的最小
l
l
l,称之为
a
a
a 关于模
m
m
m 的阶,记为
o
r
d
m
a
ord_m a
ordma
举个例子:
比如要求
2
2
2 模
7
7
7 的阶,可以一一列举
2
2
2 的幂次:
2
1
≡
2
(
m
o
d
7
)
2^1 \equiv 2~(mod~7)
21≡2 (mod 7)
2
2
≡
4
(
m
o
d
7
)
2^2 \equiv 4~(mod~7)
22≡4 (mod 7)
2
3
≡
1
(
m
o
d
7
)
2^3 \equiv 1~(mod~7)
23≡1 (mod 7)
2
4
≡
2
(
m
o
d
7
)
2^4 \equiv 2~(mod~7)
24≡2 (mod 7)
2
5
≡
4
(
m
o
d
7
)
2^5 \equiv 4~(mod~7)
25≡4 (mod 7)
2
6
≡
1
(
m
o
d
7
)
2^6 \equiv 1~(mod~7)
26≡1 (mod 7)
于是可以看出,
2
2
2 模
7
7
7 的阶为
3
3
3。
阶的性质
- 根据欧拉定理,我们有
a
φ
(
m
)
≡
1
(
m
o
d
m
)
a^{\varphi(m)}\equiv 1~(mod~m)
aφ(m)≡1 (mod m)。
于是 o r d m a ∣ φ ( m ) ord_m a \mid \varphi(m) ordma∣φ(m)。 - 当 a x ≡ 1 ( m o d m ) a^x\equiv1~(mod~m) ax≡1 (mod m) 时, o r d m a ∣ x ord_m a \mid x ordma∣x
- 阶相当于最小循环节,即 a x ≡ a x + o r d m a ( m o d m ) a^x\equiv a^{x+ord_m a}~(mod~m) ax≡ax+ordma (mod m)。因此 a 1 , a 2 , . . . . , a o r d m a a^1,a^2,....,a^{ord_m a} a1,a2,....,aordma 构成模 m m m 意义下的一个既约剩余系。从上述例子也可以看出,如 2 1 ≡ 2 4 ( m o d 7 ) 2^1 \equiv 2^4~(mod~7) 21≡24 (mod 7)。
-
o
r
d
m
a
t
=
o
r
d
m
a
gcd
(
o
r
d
m
a
,
t
)
ord_m a^t=\frac{ord_m a}{\gcd(ord_m a,t)}
ordmat=gcd(ordma,t)ordma,证明如下:
当 a t ∗ l ≡ 1 ( m o d m ) a^{t*l}\equiv 1~(mod~m) at∗l≡1 (mod m)时, o r d m a ∣ t ∗ l , t ∣ t ∗ l ord_m a\mid t*l,t\mid t*l ordma∣t∗l,t∣t∗l,因此 l l l 最小时, t ∗ l t*l t∗l 应为 l c m ( t , o r d m a ) lcm(t,ord_m a) lcm(t,ordma),可以推得 l = o r d m a gcd ( o r d m a , t ) l=\frac{ord_m a}{\gcd(ord_m a,t)} l=gcd(ordma,t)ordma
原根
当
o
r
d
m
a
=
φ
(
m
)
ord_m a=\varphi(m)
ordma=φ(m) 时,称
a
a
a 为关于
m
m
m 的原根。
也就是说,使得
a
x
≡
1
(
m
o
d
m
)
a^x\equiv 1~(mod~m)
ax≡1 (mod m) 成立的最小
x
x
x 为
φ
(
m
)
\varphi(m)
φ(m)。
举个例子:
7
7
7 的原根是
3
3
3,因为:
3
1
≡
3
(
m
o
d
7
)
3^1\equiv3~(mod~7)
31≡3 (mod 7)
3
2
≡
2
(
m
o
d
7
)
3^2\equiv2~(mod~7)
32≡2 (mod 7)
3
3
≡
6
(
m
o
d
7
)
3^3\equiv6~(mod~7)
33≡6 (mod 7)
3
4
≡
4
(
m
o
d
7
)
3^4\equiv4~(mod~7)
34≡4 (mod 7)
3
5
≡
5
(
m
o
d
7
)
3^5\equiv5~(mod~7)
35≡5 (mod 7)
3
6
≡
1
(
m
o
d
7
)
3^6\equiv1~(mod~7)
36≡1 (mod 7)
于是只有
3
φ
(
7
)
≡
1
(
m
o
d
7
)
3^{\varphi(7)}\equiv 1~(mod~7)
3φ(7)≡1 (mod 7)。
再举一个非素数的例子,比如
5
5
5 是
18
18
18 的一个原根。
φ
(
18
)
=
6
\varphi(18)=6
φ(18)=6
5
1
≡
5
(
m
o
d
18
)
5^1\equiv 5~(mod~18)
51≡5 (mod 18)
5
2
≡
7
(
m
o
d
18
)
5^2\equiv 7~(mod~18)
52≡7 (mod 18)
5
3
≡
17
(
m
o
d
18
)
5^3\equiv 17~(mod~18)
53≡17 (mod 18)
5
4
≡
13
(
m
o
d
18
)
5^4\equiv 13~(mod~18)
54≡13 (mod 18)
5
5
≡
11
(
m
o
d
18
)
5^5\equiv 11~(mod~18)
55≡11 (mod 18)
5
6
≡
1
(
m
o
d
18
)
5^6\equiv 1~(mod~18)
56≡1 (mod 18)
于是最小的
x
x
x 使得
5
x
≡
1
(
m
o
d
18
)
5^x\equiv 1~(mod~18)
5x≡1 (mod 18) 是
φ
(
18
)
\varphi(18)
φ(18),也就是
6
6
6。
原根的性质
- 当
g
g
g 为原根时,
g
1
,
g
2
,
.
.
.
.
,
g
φ
(
m
)
g^1,g^2,....,g^{\varphi(m)}
g1,g2,....,gφ(m) 构成模
m
m
m 意义下的既约剩余系。
从上面 5 5 5 是 18 18 18 的原根可以看到, 5 1 5^1 51 到 5 6 5^6 56 模 18 18 18 之后恰好是 6 6 6 个和 18 18 18 互质的数。
具体证明也不难,因为 g g g 和 m m m 互质,因此 g x g^x gx 和 m m m 互质,总共有 φ ( m ) \varphi(m) φ(m) 个 x x x,于是生成了 φ ( m ) \varphi(m) φ(m) 个与 m m m 互质的数,也就是 m m m 的既约剩余系。 - 如果
m
m
m 有原根,那么
m
m
m 总共有
φ
(
φ
(
m
)
)
\varphi(\varphi(m))
φ(φ(m)) 个原根。
证明一下:
设 g g g 为 m m m 的一个原根。那么所有原根肯定存在于 g 1 , g 2 , . . . , g φ ( m ) g^1,g^2,...,g^{\varphi(m)} g1,g2,...,gφ(m) 中,因为原根肯定与 m m m 互质。那么我们考虑 g g g 的幂次。
当 o r d m g t = φ ( m ) ord_m g^t=\varphi(m) ordmgt=φ(m) 时, g t g^t gt 为 m m m 的原根,我们来看看这样的 t t t 有多少个。由阶的性质 4 4 4, o r d m g t = φ ( m ) gcd ( φ ( m ) , t ) = φ ( m ) ord_m g^t=\frac{\varphi(m)}{\gcd(\varphi(m),t)}=\varphi(m) ordmgt=gcd(φ(m),t)φ(m)=φ(m) 时, gcd ( φ ( m ) , t ) = 1 \gcd(\varphi(m),t)=1 gcd(φ(m),t)=1,也就是说,这样的 t t t 有 φ ( φ ( m ) ) \varphi(\varphi(m)) φ(φ(m)) 个。 - 从上面的证明我们可以看到,若 g g g 为 m m m 的一个原根,则所有原根集合为 { g s ∣ 1 ≤ s ≤ φ ( m ) , ( s , φ ( m ) = 1 ) } \{g^s\mid 1\leq s \leq \varphi(m),(s,\varphi(m)=1)\} {gs∣1≤s≤φ(m),(s,φ(m)=1)}
原根的作用
原根的最大意义在于,它可以映射
m
m
m 的既约剩余系,而且每个元素一一对应。
比如我们在求
x
a
≡
b
(
m
o
d
m
)
x^a\equiv b~(mod~m)
xa≡b (mod m) (
m
m
m 是质数)时,我们可以设
g
t
≡
x
(
m
o
d
m
)
g^t\equiv x~(mod~m)
gt≡x (mod m),其中
g
g
g 为
m
m
m 的原根,不难证明这样的
t
t
t 一定存在。且一个
t
t
t 与一个
x
x
x 一一对应。
那么即是求
(
g
t
)
a
≡
(
g
a
)
t
≡
b
(
m
o
d
m
)
(g^t)^a\equiv (g^{a})^t\equiv b~(mod~m)
(gt)a≡(ga)t≡b (mod m) 的
t
t
t。因为
g
a
g^a
ga 已知,所以用
B
S
G
S
BSGS
BSGS 求
t
t
t 即可。
原根还在
N
T
T
NTT
NTT 中有着重要的作用。
于是学习原根其实是在为学 NTT 做铺垫。
原根存在的条件
当
m
=
2
,
4
,
p
k
,
2
∗
p
k
m=2,4,p^k,2*p^k
m=2,4,pk,2∗pk (
p
p
p 为奇素数)时,
m
m
m 的原根存在。其余情况不存在原根。
证明的话,我不会证。。
如何求原根
因为原根密度很大,大约是
n
0.25
n^{0.25}
n0.25,所以可以考虑暴力枚举找到一个原根。
当枚举到
a
a
a 时,如何快速判断
a
a
a 是不是原根呢?注意
a
,
m
a,m
a,m 要互质。
a
a
a 不是原根的条件是,存在
l
<
φ
(
m
)
l<\varphi(m)
l<φ(m),使得
a
l
≡
1
(
m
o
d
m
)
a^l\equiv 1~(mod~m)
al≡1 (mod m)。
设
φ
(
m
)
=
∏
p
i
k
i
\varphi(m)=\prod p_i^{k_i}
φ(m)=∏piki
由于
l
l
l 是
φ
(
m
)
\varphi(m)
φ(m) 的约数,所以我们预处理
φ
(
m
)
\varphi(m)
φ(m) 的素因子,然后枚举素因子
p
i
p_i
pi,判断是否
a
φ
(
m
)
p
i
≡
1
(
m
o
d
m
)
a^{\frac{\varphi(m)}{p_i}} \equiv 1~(mod~m)
apiφ(m)≡1 (mod m) 即可。
单次判断复杂度是
O
(
l
o
g
2
n
)
O(log^2n)
O(log2n) 的。
这里有一道模板题
代码如下
#include <bits/stdc++.h>
#include<ext/pb_ds/hash_policy.hpp>
#include<ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
struct custom_hash {
static uint64_t splitmix64(uint64_t x) {
x += 0x9e3779b97f4a7c15;
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
return x ^ (x >> 31);
}
size_t operator()(uint64_t x) const {
static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();
return splitmix64(x + FIXED_RANDOM);
}
};
LL z = 1;
int read(){
int x, f = 1;
char ch;
while(ch = getchar(), ch < '0' || ch > '9') if(ch == '-') f = -1;
x = ch - '0';
while(ch = getchar(), ch >= '0' && ch <= '9') x = x * 10 + ch - 48;
return x * f;
}
int ksm(int a, int b, int p){
int s = 1;
while(b){
if(b & 1) s = z * s * a % p;
a = z * a * a % p;
b >>= 1;
}
return s;
}
int phi(int x){
int ret = x;
for(int i = 2; i <= x / i; i++){
if(x % i == 0){
ret = ret / i * (i - 1);
while(x % i == 0) x /= i;
}
}
if(x > 1) ret = ret / x * (x - 1);
return ret;
}
vector<int> d;
void get(int x){
for(int i = 2; i <= x / i; i++){
if(x % i == 0){
d.push_back(i);
while(x % i == 0) x /= i;
}
}
if(x > 1) d.push_back(x);
}
int main(){
int i, j, flag, n, p, m;
p = read(); m = phi(p);
get(m);
for(i = 2; i <= p; i++){
flag = 0;
for(auto x: d){
if(ksm(i, m / x, p) == 1){
flag = 1;
break;
}
}
if(!flag){
printf("%d", i);
return 0;
}
}
return 0;
}