抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

今日、海を見た。もう怖くない

《退役人的自我救赎系列》——其二,也就是差不多算是初等数论的知识。

一句话简介:NTT 即快速数论变换,是一种可以在nlognn\log n 的时间内完成多项式乘法的算法——的一部分。

前置知识

FFT

可以看我之前写过的一篇文章:基础知识:FFT - 简单入门 - 七海の参考書 (shiraha.cn)

FFT 需要使用复数——这样就无法回避大量的浮点运算,然后精度就会爆炸;但是由于已经证明了在复数域内,具有循环卷积特性的唯一变换是DFT,所以在复数域中不存在具有循环卷积性质的更简单的离散正交变换;因此,我们就提出了以数论为基础的具有循环卷积性质的快速数论变换NTT):它的特点在于用有限域上的单位根来取代复平面上的单位根。

上面这段话是上网抄的。虽然我现在还理解不了,但是有一件事情十分清楚——和 FFT 利用单位根的性质减少运算量一样,NTT 利用了原根的性质来减少运算量,达到了同样的复杂度。

定义

a,pN+a, p\in\N^+ 满足gcd(a,p)=1\gcd(a, p)=1p>1p>1,那么:

对于使得an1 mod pa^n \equiv 1\ \text{mod}\ p 成立的最小的nn,我们称之为aapp 的阶,记作δp(a)\delta_p(a)ordpa\text{ord}_pa

性质

1. 对于i[0,δp(a))i\in[0, \delta_p(a)),所有的ai mod pa_i\ \text{mod}\ p 都互不相同

反证法:令有jkj\ne k 在该范围内并且模意义下相同,那么显然有ajk1 mod pa^{|j-k|}\equiv1\ \text{mod}\ p,且jk|j-k| 也属于该范围内,和定义矛盾。

2. 对于任何an1 mod pa^n \equiv 1\ \text{mod}\ p,有δp(a)n\delta_p(a) \mid n

显然。否则,把nn 表示为kδp(a)+mk\delta_p(a) + m,显然m(0,δp(a))m \in (0, \delta_p(a)) 且满足am1 mod pa^m \equiv 1\ \text{mod}\ p,和性质 1 冲突。

推论 1:若有gcd(a,p)=1\gcd(a, p)=1,那么δp(a)ϕ(p)\delta_p(a) \mid \phi(p)

由欧拉定理可知:若gcd(a,p)=1\gcd(a, p) = 1,则aϕ(p)1 mod pa^{\phi(p)} \equiv 1 \ \text{mod} \ p。那么由性质 2 可得δp(a)ϕ(p)\delta_p(a) \mid \phi(p)

3. 若qZ+q\in\Z^+pp 是素数,那么δp(qi)=δp(q)    gcd(δp(q),i)=1\delta_p(q^i)=\delta_p(q) \iff \gcd(\delta_p(q), i)=1

首先,我们记qi=qgcd(δp(q),i)rq^i = q^{\gcd(\delta_p(q), i)\cdot r},显然r=igcd(δp(q),i)Z+r=\frac i{\gcd(\delta_p(q), i)} \in\Z^+

由性质 2 和题设条件欧拉函数的性质,可得:δp(q)ϕ(p)=(p1)\delta_p(q) \mid \phi(p) = (p-1) (好像没用)

那么,有:(gi)δp(q)gcd(δp(q),i)=(gδp(q))igcd(δp(q),i)=grδp(q)1 mod p(g^i)^{\frac{\delta_p(q)}{\gcd(\delta_p(q), i)}}=(g^{\delta_p(q)})^{\frac i{\gcd(\delta_p(q), i)}}=g^{r\cdot \delta_p(q)}\equiv1\ \text{mod}\ p


假设gcd(δp(q),i)1\gcd(\delta_p(q), i)\ne1,那么gcd(δp(q),i)>1\gcd(\delta_p(q), i)>1,那么δp(q)gcd(δp(q),i)<δp(q)\frac{\delta_p(q)}{\gcd(\delta_p(q), i)}<\delta_p(q)

因为(gi)δp(q)gcd(δp(q),i)1 mod p(g^i)^{\frac{\delta_p(q)}{\gcd(\delta_p(q), i)}}\equiv1\ \text{mod}\ p,由性质 2 可得δp(qi)δp(q)gcd(δp(q),i)\delta_p(q^i)\mid\frac{\delta_p(q)}{\gcd(\delta_p(q), i)},因此δp(qi)δp(q)gcd(δp(q),i)\delta_p(q^i)\le\frac{\delta_p(q)}{\gcd(\delta_p(q), i)}

综上所述,可得:δp(qi)δp(q)gcd(δp(q),i)<δp(q)\delta_p(q^i)\le\frac{\delta_p(q)}{\gcd(\delta_p(q), i)}<\delta_p(q)

gcd(δp(q),i)1δp(qi)δp(q)\gcd(\delta_p(q), i)\ne1\Rightarrow \delta_p(q^i)\ne\delta_p(q)必要条件得证。


继承上述的证明,若有gcd(δp(q),i)=1\gcd(\delta_p(q), i)=1,那么δp(q)gcd(δp(q),i)=δp(q)\frac{\delta_p(q)}{\gcd(\delta_p(q), i)}=\delta_p(q)

由上述证明就可以得到:δp(qi)δp(q)\delta_p(q^i)\le\delta_p(q)

因为giδp(q)gcd(δp(q),i)1 mod pg^{i\cdot\frac{\delta_p(q)}{\gcd(\delta_p(q), i)}}\equiv1\ \text{mod}\ p,和性质 2 和 4 可知:δp(q)iδp(qi)\delta_p(q)\mid i\cdot\delta_p(q^i)

因为gcd(δp(q),i)=1\gcd(\delta_p(q), i)=1,所以上式δp(q)δp(qi)上式\Rightarrow\delta_p(q)\mid \delta_p(q^i),也就是δp(q)δp(qi)\delta_p(q)\le\delta_p(q^i)

综上所述,δp(qi)δp(q)δp(q)δp(qi)\because \delta_p(q^i)\le\delta_p(q) \and \delta_p(q)\le\delta_p(q^i)δp(q)=δp(qi)\therefore \delta_p(q)=\delta_p(q^i)

gcd(δp(q),i)=1δp(qi)=δp(q)\gcd(\delta_p(q), i)=1\Rightarrow \delta_p(q^i)=\delta_p(q)充分条件得证。


综上所述,gcd(δp(q),i)=1\gcd(\delta_p(q), i)=1δp(q)=δp(qi)\delta_p(q)=\delta_p(q^i)充分必要条件

4.δp(ab)=δp(a)gcd(δp(a),b)\delta_p(a^b) = \frac{\delta_p(a)}{\gcd(\delta_p(a), b)},其中a,p,bZ+a, p, b \in \Z^+

由阶的定义可知:(ab)δp(ab)abδp(ab)1 mod p(a^b)^{\delta_p(a^b)} \equiv a^{b\cdot\delta_p(a^b)}\equiv1\ \text{mod} \ p

又由性质 2,可以得到:δp(a)bδp(ab)δp(a)gcd(δp(a),b)bgcd(δp(a),b)δp(ab)\delta_p(a)\mid b\cdot\delta_p(a^b) \Rightarrow \frac{\delta_p(a)}{\gcd(\delta_p(a), b)}\mid \frac{b}{\gcd(\delta_p(a), b)}\cdot\delta_p(a^b)

显然,因为gcd(δp(a)gcd(δp(a),b),bgcd(δp(a),b))=1\gcd(\frac{\delta_p(a)}{\gcd(\delta_p(a), b)}, \frac{b}{\gcd(\delta_p(a), b)})=1,因此:上式δp(a)gcd(δp(a),b)δp(ab)上式\Rightarrow \frac{\delta_p(a)}{\gcd(\delta_p(a), b)}\mid\delta_p(a^b)

又因为定义:abδp(a)gcd(δp(a),b)aδp(a)bgcd(δp(a),b)1 mod pa^{b\cdot\frac{\delta_p(a)}{\gcd(\delta_p(a), b)}}\equiv a^{\delta_p(a)\cdot\frac{b}{\gcd(\delta_p(a), b)}} \equiv 1\ \text{mod}\ pbgcd(δp(a),b)\frac{b}{\gcd(\delta_p(a), b)} 显然是整数。

所以由性质 2 可得:δp(ab)δp(a)gcd(δp(a),b)\delta_p(a^b)\mid\frac{\delta_p(a)}{\gcd(\delta_p(a), b)}

综上所述:δp(a)gcd(δp(a),b)δp(ab)  δp(ab)δp(a)gcd(δp(a),b)\because \frac{\delta_p(a)}{\gcd(\delta_p(a), b)}\mid\delta_p(a^b)\ \and\ \delta_p(a^b)\mid\frac{\delta_p(a)}{\gcd(\delta_p(a), b)}δp(ab)=δp(a)gcd(δp(a),b)\therefore \delta_p(a^b)=\frac{\delta_p(a)}{\gcd(\delta_p(a), b)}

5.pN+, a,bZp \in \N^+,\ a, b\in\Zgcd(a,p)=gcd(b,p)=1\gcd(a, p) = \gcd(b, p) = 1,那么gcd(δp(a),δp(b))=1\gcd(\delta_p(a),\delta_p(b))=1    \iffδp(ab)=δp(a)δp(b)\delta_p(ab)=\delta_p(a)\delta_p(b)

由定义可得aδp(a)1 mod pa^{\delta_p(a)}\equiv1\ \text{mod}\ pbδp(b)1 mod pb^{\delta_p(b)}\equiv1\ \text{mod}\ p,那么:(ab)lcm(δp(a),δp(b))1 mod p(ab)^{\text{lcm}(\delta_p(a),\delta_p(b))}\equiv1\ \text{mod}\ p

由性质 2,可得:δp(ab)lcm(δp(a),δp(b))\delta_p(ab)\mid\text{lcm}(\delta_p(a),\delta_p(b))

δp(ab)=δp(a)δp(b)\delta_p(ab)=\delta_p(a)\delta_p(b) 成立,那么:上式δp(a)δp(b)lcm(δp(a),δp(b))上式 \Rightarrow \delta_p(a)\delta_p(b)\mid\text{lcm}(\delta_p(a),\delta_p(b))

又因为lcm(δp(a),δp(b))=δp(a)δp(b)gcd(δp(a),δp(b))\text{lcm}(\delta_p(a),\delta_p(b)) = \frac{\delta_p(a)\delta_p(b)}{\gcd(\delta_p(a),\delta_p(b))},可得gcd(δp(a),δp(b))=1\gcd(\delta_p(a),\delta_p(b))=1

δp(ab)=δp(a)δp(b)\delta_p(ab)=\delta_p(a)\delta_p(b)\Rightarrowgcd(δp(a),δp(b))=1\gcd(\delta_p(a),\delta_p(b))=1必要性得证。


又由阶的定义:(ab)δp(ab)1 mod p(ab)^{\delta_p(ab)}\equiv1\ \text{mod}\ p,可以进行如下推导:

(ab)δp(ab)(ab)δp(ab)δp(b)aδp(ab)δp(b)bδp(ab)δp(b)aδp(ab)δp(b)11 mod p(ab)^{\delta_p(ab)}\equiv(ab)^{\delta_p(ab)\delta_p(b)}\equiv a^{\delta_p(ab)\delta_p(b)}\cdot b^{\delta_p(ab)\delta_p(b)}\equiv a^{\delta_p(ab)\delta_p(b)}\cdot 1\equiv1\ \text{mod}\ p

由性质 2,可得:δp(a)δp(ab)δp(b)\delta_p(a)\mid\delta_p(ab)\delta_p(b);因为gcd(δp(a),δp(b))=1\gcd(\delta_p(a),\delta_p(b))=1,所以δp(a)δp(ab)\delta_p(a)\mid\delta_p(ab)

同理,可得:δp(b)δp(ab)\delta_p(b)\mid\delta_p(ab);因为gcd(δp(a),δp(b))=1\gcd(\delta_p(a),\delta_p(b))=1,所以δp(a)δp(b)δp(ab)\delta_p(a)\delta_p(b)\mid\delta_p(ab)

由定理可得:aδp(a)aδp(a)δp(b)1 mod pa^{\delta_p(a)}\equiv a^{\delta_p(a)\delta_p(b)}\equiv1\ \text{mod}\ pbδp(b)bδp(a)δp(b)1 mod pb^{\delta_p(b)}\equiv b^{\delta_p(a)\delta_p(b)}\equiv1\ \text{mod}\ p,因此:

aδp(a)δp(b)bδp(a)δp(b)(ab)δp(a)δp(b)1 mod pa^{\delta_p(a)\delta_p(b)} \cdot b^{\delta_p(a)\delta_p(b)} \equiv (ab)^{\delta_p(a)\delta_p(b)} \equiv1\ \text{mod}\ p

由性质 2,可得δp(ab)δp(a)δp(b)\delta_p(ab)\mid\delta_p(a)\delta_p(b);因此,可证δp(ab)=δp(a)δp(b)\delta_p(ab)=\delta_p(a)\delta_p(b)

gcd(δp(a),δp(b))=1\gcd(\delta_p(a),\delta_p(b))=1\Rightarrowδp(ab)=δp(a)δp(b)\delta_p(ab)=\delta_p(a)\delta_p(b)充分性得证。


综上所述:gcd(δp(a),δp(b))=1\gcd(\delta_p(a),\delta_p(b))=1δp(ab)=δp(a)δp(b)\delta_p(ab)=\delta_p(a)\delta_p(b)充分必要条件

原根

定义

mN+,gZm \in \N^+,g\in\Z,若有δm(g)=ϕ(m)\delta_m(g)=\phi(m)gcd(m,g)=1\gcd(m,g)=1,那么称gg 是模mm 的一个原根。

若整数gg 模正整数mm 的阶(这要求它们互质)和ϕ(m)\phi(m) 相等,那么gg 是模mm 的一个原根。

性质

……我一定是哪里变得奇怪了才会想着抄录全部性质并键证它们== 哪天闲的没事干再补全吧()

7Q6A_S_AWQFY__Q5NEZI_E.jpg

定理

原根的存在条件

判断对于一个整数pp 是否存在原根:

  • 对于整数p=2,4p=2,4,它们的原根显然存在
  • 奇素数pp 的原根存在;对于αN+\alpha\in\N^+pαp^\alpha 的原根存在
  • 对于奇素数ppαN+\alpha\in\N^+2pα2p^\alpha 的原根存在
  • pp 不符合上述的任何条件,则对于aZ\forall a\in\Zgcd(a,p)=1\gcd(a, p)=1,都有δp(a)<ϕ(p)\delta_p(a) < \phi(p),即pp 不存在原根

与之相关的一些定理:

  • 对于奇素数pp,若gg 是其原根,则gg 或者g+pg+pp2p^2 的原根
  • 对于奇素数ppαN+\alpha \in \N^+,若gg 是模pαp^\alpha 的原根,则gg g+pαg+p^\alpha 中的奇数是2pα2p^\alpha 的原根

简单地说,对于奇素数ppαN+\alpha\in\N^+,有原根的数包括:{2,4,pα,2pα}\{2,4,p^\alpha,2p^\alpha\}

求法

对于一个数nn,如果它存在原根,那么首先找到它的最小原根并令其为gg;那么,nn 的所有原根都可以表示为gkg^kkk 是正整数并且满足gcd(ϕ(n),k)=1\gcd(\phi(n),k)=1,共ϕ(ϕ(n))\phi(\phi(n)) 个。

最小原根gg 的大小已经证明是不会超过n4\sqrt[4]{n} 的,所以可以通过暴力枚举来确定,然后按照定义要求来验证某个数字是否是原根——但是定义要求δn(g)=ϕ(n)\delta_n(g)=\phi(n),我们无法对于每一个备选gg 都枚举i[1,ϕ(n))i\in[1,\phi(n)) 来计算gig^i,观察它不和11 同余来判定它是阶。

注意到阶的性质 2 的推论 1,我们可以知道δn(g)ϕ(n)\delta_n(g)\mid\phi(n);也就是说,对于备选gg,如果它模nn 下的阶不满足原根的要求,而是另有k<ϕ(n)k < \phi(n) 存在,那么它满足kϕ(n)k\mid\phi(n);那么,我们只需要检查ϕ(n)\phi(n) 的所有因数就可以找到可能存在的kk 了,而不需要枚举整个[1,ϕ(n))[1,\phi(n));具体地,若ϕ(n)\phi(n) 的质因子被记为p1,,prp_1,\dots,p_r,那么实际上我们只需要检查所有的ϕ(n)pi\frac{\phi(n)}{p_i} 即可——它覆盖了所有的真因数的倍数,检查它们等同于检查了所有的真因数。

综上所述,找到最小原根gg 所需要的时间是O(n4logn)\mathcal{O}(\sqrt[4]n\cdot\log n) 的;利用最小原根gg 求出所有原根所需要的时间是O(ϕ(n)lognϕ(n)4)\mathcal O (\phi(n)\cdot\log n^{\frac{\phi(n)}4}) 的。完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
template<int n>
auto &linear_sieve() {
static vector<int> prime;
static array<int, n + 1> vis;
for (int i = 2; i <= n; ++i) {
if (!vis[i])
prime.push_back(i), vis[i] = i;
for (auto &pp : prime) {
if ((llong) i * pp > n) break;
vis[i * pp] = pp;
if (i % pp == 0) break;
}
}
static auto ret = make_pair(ref(prime), ref(vis));
return ret;
}

llong fast_pow(llong a, llong b, llong mod) {
llong ans = 1;
while (b) {
if (b & 1u) ans = (ans * a) % mod;
a = (a * a) % mod, b >>= 1u;
}
return ans % mod;
}

constexpr int N = 1e6 + 5;

bool check_primitive_root(
const set<int> &de, llong g,
llong phi, llong n) {
if (fast_pow(g, phi, n) == 1) {
return none_of(de.begin(), de.end(), [&](int p) {
return fast_pow(g, phi / p, n) == 1;
});
} else return false;
}

signed main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
const auto [p, dc] = linear_sieve<N>();
const auto decompose = [&dc = dc]
(llong x, set<int> &de) {
de.clear();
for (auto ii = x; ii > 1;) {
de.insert(dc[ii]);
ii /= dc[ii];
}
};
bitset<N> has_primitive_root;
has_primitive_root.set(1).set(2).set(4);
for (llong pp : p) {
if (pp == 2) continue;
for (auto now = pp; now < N; now *= pp) {
has_primitive_root.set(now);
if (now * 2 < N) has_primitive_root.set(now * 2);
}
}
vector<llong> ans;
for (auto T = read(); T--;) {
auto n = read(), d = read(), phi = n;
if (has_primitive_root[n]) {
set<int> de_n, de_phi;
decompose(n, de_n);
for (auto pi : de_n)
(phi /= pi) *= (pi - 1);
decompose(phi, de_phi);
llong g = 0, lim = n;
for (int i = 1; i <= lim; ++ i)
if (check_primitive_root(de_phi, i, phi, n))
{ g = i; break; }
if (!g) {
cout << 0 << endl << endl;
continue;
}
ans.clear();
for (auto gi = g, i = 1ll;
i <= phi; ++ i, (gi *= g) %= n) {
if (gcd(i, phi) == 1)
ans.push_back(gi);
}
sort(ans.begin(), ans.end());
cout << ans.size() << endl;
for (auto id = d - 1; id < ans.size(); id += d)
cout << ans[id] << ' ';
cout << endl;
} else cout << 0 << endl << endl;
}
return 0;
}

首先筛出质数,再筛出所有的可能有原根的数——这一步是O(nlogn)\mathcal O (n\log n) 的;对于一个有原根的数nn,首先利用筛的结果(当然也可以直接用线性筛直接算好了存起来)根据定义求出ϕ(n)\phi(n),然后再利用筛维护的数据(或者埃氏筛直接存起来)分解其质因数存好备用;暴力枚举,并且利用上面说的方法来检查其是否为原根,求出最小原根——这一步是理论O(n4logn)\mathcal O (\sqrt[4]n\log n) 的;最后再利用最小原根生成所有的原根:这需要重复ϕ(n)\phi(n) 次,每次使用gcd\gcd 检查——这一步是O(ϕ(n)logn)\mathcal O(\phi(n)\log n) 的。

上面的代码可以通过 P6091 【模板】原根。需要注意虽然最小原根有这个理论界限,但是求的时候只遍历到n14\lceil n^\frac14\rceil 似乎会暴毙……

阶和原根

那么如何理解这两个抽象的概念呢?

后记

参考资料

评论