阶
我们都知道欧拉定理,对于 a ∈ Z a\in \Z a∈Z, m ∈ N ∗ m\in\N^* m∈N∗,且 ( a , m ) = 1 (a,m)=1 (a,m)=1,则有 a φ ( m ) ≡ 1 ( m o d m ) a^{\varphi(m)}\equiv1\pmod m aφ(m)≡1(modm),其中 ( a , m ) (a,m) (a,m) 为 gcd ( a , m ) \gcd(a,m) gcd(a,m) 的简写。即每经过 φ ( m ) \varphi(m) φ(m) 就绕了一圈。
但事实上可能存在更小的 n ∣ φ ( m ) n|\varphi(m) n∣φ(m),使得 a n ≡ 1 ( m o d m ) a^n\equiv1\pmod m an≡1(modm) 成立,最小的使得这个式子成立的 n n n 称为 a a a 模 m m m 的阶,记作 ord m ( a ) \text{ord}_m(a) ordm(a) 或者 δ m ( a ) \delta_m(a) δm(a)。
它有一些性质:
- δ m ( a ) ∣ φ ( m ) \delta_m(a)\mid\varphi(m) δm(a)∣φ(m)
- δ m ( a b ) = δ m ( a ) δ m ( b ) ↔ ( δ m ( a ) , δ m ( b ) ) = 1 \delta_m(ab)=\delta_m(a)\delta_m(b)\leftrightarrow(\delta_m(a),\delta_m(b))=1 δm(ab)=δm(a)δm(b)↔(δm(a),δm(b))=1
- δ m ( a k ) = δ m ( a ) ( δ m ( a ) , k ) \delta_m(a^k)=\frac{\delta_m(a)}{(\delta_m(a),k)} δm(ak)=(δm(a),k)δm(a)
这里忽略证明,感兴趣的话可以看 这里。
怎么计算 δ m ( a ) \delta_m(a) δm(a)? 由于 a φ ( m ) ≡ 1 ( m o d m ) a^{\varphi(m)}\equiv1\pmod m aφ(m)≡1(modm), δ m ( a ) ∣ φ ( m ) \delta_m(a)|\varphi(m) δm(a)∣φ(m),所以可以记 t = φ ( m ) t=\varphi(m) t=φ(m),然后每次尝试把 t t t 除以 φ ( m ) \varphi(m) φ(m) 的质因数,配合 Pollard-Rho 分解质因数是 O ( m 1 4 + log m ) O(m^{\frac{1}{4}}+\log m) O(m41+logm) 的。
原根
我去,原
对于 m ∈ Z m\in\Z m∈Z,若有 ( g , m ) = 1 (g,m)=1 (g,m)=1,满足 δ m ( g ) = φ ( m ) \delta_m(g)=\varphi(m) δm(g)=φ(m),则称 g g g 为 m m m 的原根。
它也有一些性质:
- 一个正整数 m m m 存在原根,当且仅当它可以表示成 2 , 4 , p k , 2 p k 2,4,p^k,2p^k 2,4,pk,2pk 中的一个。
- 若 g g g 为 m m m 的原根,则有 g φ ( m ) p ≢ 1 ( m o d m ) g^{\frac{\varphi(m)}{p}}\not\equiv1\pmod m gpφ(m)≡1(modm),其中 p p p 为 m m m 任意一个质因数。
- m m m 最小原根的大小为 O ( m 0.25 ) O(m^{0.25}) O(m0.25)。
- 若 g g g 为 m m m 的原根,且对于 k ∈ N ∗ k\in \N^* k∈N∗,有 ( k , φ ( m ) ) = 1 (k,\varphi(m))=1 (k,φ(m))=1 ,则 g k g^k gk 也是 m m m 的一个原根。
- m m m 若存在原根,则它的原根个数是 O ( φ ( φ ( m ) ) ) O(\varphi(\varphi(m))) O(φ(φ(m)))。
第一点让我们知道一个数有没有原根( O ( m log m ) O(m\log m) O(mlogm))预处理, O ( 1 ) O(1) O(1) 判断)。
第二点就是判定一个数是不是 m m m 原根的方法(依次选取 p p p 并判断)复杂度 O ( log m ) O(\log m) O(logm)。
第三点保证我们可以在 O ( m 0.25 log m ) O(m^{0.25}\log m) O(m0.25logm) 时间内找到最小原根(暴力枚举)。
第四点和第五点让我们可以在 O ( φ ( m ) log m ) O(\varphi(m)\log m) O(φ(m)logm) 时间内找到其他原根。
于是就可以在 O ( m log m ) O(m\log m) O(mlogm) 时间内找出 m m m 的所有原根。
板题:P6091 【模板】原根
离散对数
对于一个正整数 m m m 和其原根 g g g(假设其存在),满足 g x ≡ b ( m o d m ) g^x\equiv b\pmod m gx≡b(modm),其中 0 ≤ x < φ ( m ) 0\le x<\varphi(m) 0≤x<φ(m),则称 x x x 为 b b b 以 g g g 为底模 m m m 的指数,记作 ind g b \text{ind}_g b indgb。显然这样的 x x x 是唯一的。
它同样有一些性质:
对于 ( a , m ) = 1 (a,m)=1 (a,m)=1, ( b , m ) = 1 (b,m)=1 (b,m)=1,有
- ind g a b ≡ ind g a + ind g b ( m o d φ ( m ) ) \text{ind}_gab\equiv\text{ind}_ga+\text{ind}_gb\pmod{\varphi(m)} indgab≡indga+indgb(modφ(m))
- ind g a k = k ind g a ( m o d φ ( m ) ) \text{ind}_ga^k=k\,\text{ind}_ga\pmod{\varphi(m)} indgak=kindga(modφ(m))
- 若 r r r 也为 m m m 的原根,则 ind g a = ind r a × ind g r ( m o d φ ( m ) ) \text{ind}_ga=\text{ind}_ra\times\text{ind}_gr\pmod{\varphi(m)} indga=indra×indgr(modφ(m))
- 若 a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm),则 ind g a = ind g b \text{ind}_ga=\text{ind}_gb indga=indgb
那么如何求解离散对数呢?
BSGS 算法
BSGS 全称 Baby-step Gaint-step,中文大步小步算法。对于正整数
a
a
a,
b
b
b,
m
m
m,它可以用来求解
a
x
≡
b
(
m
o
d
m
)
a^x\equiv b\pmod m
ax≡b(modm)
其中
(
a
,
m
)
=
1
(a,m)=1
(a,m)=1,
m
m
m 不一定为质数。
怎么做呢?令 x = A t − B x=At-B x=At−B,其中 1 ≤ A ≤ ⌈ φ ( m ) t ⌉ 1\le A\le \left\lceil\frac{\varphi(m)}{t}\right\rceil 1≤A≤⌈tφ(m)⌉, 1 ≤ B ≤ t 1\le B\le t 1≤B≤t,这样 A t − B At-B At−B 能取遍 0 0 0 到 φ ( m ) − 1 \varphi(m)-1 φ(m)−1。
于是有
a
A
t
≡
a
B
b
(
m
o
d
m
)
a^{At}\equiv a^{B}b\pmod m
aAt≡aBb(modm)
于是可以枚举所有 B B B,把 a B b m o d m a^Bb\bmod m aBbmodm 可能的取值记下来(用哈希表或平衡树),再从小到大枚举 A A A,判断是否有和 a A t a^{At} aAt 同余的 a B b a^Bb aBb,若有,则答案为 A t − B At-B At−B。如果全部枚举完都没有符合条件的,就说明无解。
显然 t t t 取 ⌊ m ⌋ + 1 \left\lfloor \sqrt m\right\rfloor+1 ⌊m⌋+1 时间复杂度最小。
代码:
map<ll, int> mp;
ll BSGS(ll a, ll b, ll p) {
mp.clear(), a %= p, b %= p;
ll cur = 1, t = sqrt(p) + 1;
lp(i, 1, t) (cur *= a) %= p, mp[b * cur % p] = i; // 枚举 A
ll now = cur;
lp(i, 1, t) { // 枚举 B
if(mp.count(now)) return i * t - mp[now]; // 找到了最小的答案
(now *= cur) %= p;
}
return -INF; // 无解
}
exBSGS
再看这个式子:
a
x
≡
b
(
m
o
d
m
)
a^x\equiv b\pmod m
ax≡b(modm)
若
(
a
,
m
)
=
1
(a,m)=1
(a,m)=1 不一定成立,就需要用到 exBSGS。
注意到这个式子可以写成
a
a
x
−
1
+
m
k
=
b
aa^{x-1}+mk=b
aax−1+mk=b
由斐蜀定理,其有解的充要条件是
b
∣
(
a
,
m
)
b\mid(a,m)
b∣(a,m),否则无解直接返回,两边同除以
d
=
(
a
,
m
)
d=(a,m)
d=(a,m),得
a
d
a
x
−
1
+
m
d
k
=
b
d
\frac{a}{d}a^{x-1}+\frac{m}{d}k=\frac{b}{d}
daax−1+dmk=db
即
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}}
daax−1≡db(moddm)
此时若
(
a
,
m
d
)
=
1
(a,\frac{m}{d})=1
(a,dm)=1,就能用 BSGS 求解,只不过多了个
a
d
\frac{a}{d}
da 系数,稍微改下 BSGS 就行。否则继续递归下去,进行
k
k
k 次后变成
a
k
D
a
x
−
k
≡
b
D
(
m
o
d
m
D
)
\frac{a^k}{D}a^{x-k}\equiv\frac{b}{D}\pmod{\frac{m}{D}}
Dakax−k≡Db(modDm)
其中
D
=
∏
i
=
1
k
d
i
D=\prod_{i=1}^k d_i
D=∏i=1kdi。
那么这样的过程最多进行 max { max ( c i , c i ′ ) min ( c i , c i ′ ) } = O ( log m ) \max\{\frac{\max(c_i,c_i')}{\min(c_i,c_i')}\}=O(\log m) max{min(ci,ci′)max(ci,ci′)}=O(logm) 次,其中 c i c_i ci 为 a a a 的质因数 p i p_i pi 的幂次, c i ′ c_i' ci′ 为 m m m 的质因数 p i p_i pi 的幂次。于是总时间复杂度为 O ( m polylog ) O(\sqrt m \text{polylog}) O(mpolylog),若使用哈希就只有一个 log,使用 map 有两个 log。
需要注意若 a k a^k ak 模 m m m 为 b b b(没有除以 d d d 的),就直接返回 k k k。
实现上,使用非递归实现,速度较快。
代码:
map<ll, int> mp;
ll BSGS(ll a, ll b, ll p, ll k = 1) {
mp.clear(), a %= p, b %= p;
ll cur = 1, t = sqrt(p) + 1;
lp(i, 1, t) (cur *= a) %= p, mp[b * cur % p] = i;
ll now = cur * k % p; // 多乘了个系数
lp(i, 1, t) {
if(mp.count(now)) return i * t - mp[now];
(now *= cur) %= p;
}
return -INF; // 不能返回 -1,因为还加了 i + 1
}
ll exBSGS(ll a, ll b, ll m, ll k = 1) {
ll A = a %= m, B = b %= m, M = m, cur = 1;
for(int i = 0; ; ++i) {
if(cur == B) return i; // a ^ k % m = b 的情况
(cur *= A) %= M; ll d = __gcd(a, m);
if(b % d) return -INF; // 判无解
if(d == 1) return BSGS(a, b, m, k * a % m) + i + 1;
b /= d, m /= d, k = k * a / d % m;
}
}