无理数取模怎么做?
这可能是个科普小品文, 也可能不是...
所以, 无理数取模怎么做? 这句话首先是想 neta 在 CTS 2019 国家队论文答辩时候的一个梗, 有个评委问 zzq "有理数取模怎么做". 而且也时不时会有小朋友问类似的问题.
首先, 让我们看看有理数取模是怎么做的. 对于 \(q = a / b\), 我们一般想要知道 "\(\bmod p\)" 的结果, 实际上是想找到和除法性质相同的数, 也就是 \(x\) 使得 \(bx = a\). 原本的 \(q\) 是在 \(\mathbb Q\) 的意义上这个问题的解, 而 \(\bmod p\) 的时候, 我们求的就是 \(\mathbb F_p\) 上的解 \(x\).
这应该是比较基本的理解. 当你理解了它, 可能就是你第一次认识到除了中学里会提到的有理数 \(\mathbb Q\), 实数 \(\mathbb R\) 和复数 \(\mathbb C\) 之外, 还有 \(\mathbb F_p\) 这种域. 在常见的计算中, 只用到加减乘除的时候, 我们用到的这些运算以及基本性质是一个域的公理所提供的.
那么下面就要问了, \(1/p\) 也是有理数, 或者 \(1\) 也能写成 \(p/p\), 我们该怎么对这种东西 \(\bmod p\) 呢?
一个没有错误的解释是这样的. 当我们讨论整数取模的时候, 如果把视角仅限制在除法之外, 那这是一个环同态 \(\mathbb Z \to \mathbb F_p\). 而因为这同态把 \(p\) 打到 \(0\), 所以 \(xp=1\) 这种方程在 \(\mathbb F_p\) 里是无解的, 所以这个同态势必不能提升到整个有理数 \(\mathbb Q\) 上. 所以有理数取模这个说法就是不太准确.
这个解释当然是没有错误, 但是如果就此打住, 就没有必要写这篇小品文了.
\(p\) 进数
无论你是学过一点数值算法, 还是学过多项式求逆之类的东西, 都应该知道了牛顿迭代法这个东西.
这个迭代的效率大概是每次精度翻倍的. 具体来说, 如果一个根 \(x^*\) 满足 \(f'(x^*)\neq 0\), 那么当 \(x_n\) 充分接近 \(x^*\) 的时候, 有 \((x_{n+1}-x^*) = O((x_n - x^*)^2)\). 类似地, 对于多项式全家桶而言, 上一步 \(x_n(t)\) 如果确定到了 \(x^* + O(t^n)\), 那么下一步 \(x_{n+1}(t)\) 就能确定到 \(x^* + O(t^{2n})\).
类似地, 如果我们有一个多项式方程 \(f(x)=0\), 它的一个 \(\bmod p^n\) 的解, 能否通过类似的方法求出 \(\bmod p^{2n}\) 的解呢? 道理是一样的.
对于一个模 \(p^n\) 意义下的解 \(x\), 也即 \(f(x) = 0 \pmod {p^n}\), 如果还有 \(f'(x)\neq 0 \pmod p\),
那我们不妨将写成
注意这里除完阶乘仍然都是整数, 所以我们可以直接取模 \(p^{2n}\), 得到
所以可以解得 \(v = (-f(x)/p^n)/f'(x) \pmod {p^n}\).
由此, 我们可以得到一个解 \(x' = x + p^n v \pmod {p^{2n}}\), 注意这个解在前缀的部分是和原来的部分相同的, 也即 \(x' \equiv x \pmod{p^n}\). 如此不断将 \(n\) 翻倍, 我们就可以得到一个 "精度" 越来越高的解.
那么, 类似于形式幂级数之于多项式, 一个想法就是这个精度越来越大的解实际上就是某个 "无穷精度" 的解截断得到的.
我们称这个 "无穷精度" 的数构成的世界叫做 \(p\) 进数.
一种类比小学学数学时候的写法, 就是写成一个无穷和 \(\sum_{i=0}^\infty a_i p^i\), 其中 \(a_i \in \{0,\dots,p-1\}\). 然后运算按照类似的进位规则进行, 注意对于每一位而言, 只需要进行有限次进位的考虑就能确定这一位是几, 所以尽管进位本身可能会一直进行下去, 但是每一位的运算都是有限的, 因此计算是可以被完全确定的.
一个稍微不依赖于具体表示方法的说法则是直接根据上面的迭代过程来理解, 也即一个无穷序列 \(\{x_n \in \mathbb Z / p^n\mathbb Z \}\), 也就是 \(x_n\) 是模 \(p^n\) 的, 而且要求满足 \(x_{n+1} \bmod p^n = x_n\).
当然还有一些更加一般叙述下的定义方法, 不过本文不谈.
这样定义出来的就是 \(p\) 进整数 \(\mathbb Z_p\). 它的分式域 (也就是从 \(\mathbb Z\) 得到 \(\mathbb Q\) 类似的过程, 考虑 \(a/b\)) 记为 \(p\) 进数 \(\mathbb Q_p\).
容易发现, \(\mathbb Q_p\) 也有一种表示方法, 都可以写成 \(\sum_{i\geq n} a_i p^i\) 的形式, 但这个时候 \(n\) 可以是负数.
这样一来有一个好处, \(\mathbb Z \to \mathbb Z_p\) 不会把非零的数打到 \(0\) 了, 因此我们可以将它提升到 \(\mathbb Q \to \mathbb Q_p\). 然后我们再在 \(\mathbb Q_p\) 里讨论什么东西是可以写作取模的结果 \(\mathbb F_p\) 的.
当我们知道两个数 \(a, b\) 到 "一定精度" 的时候, 也即 \(a' = a + O(p^n)\), \(b' = b + O(p^m)\), 这里 \(O\) 的含义可以理解成 \(a' - a\) 是 \(p^n\) 的倍数. 那么做加法的话, 精度会保留到 \(O(p^{\min(n,m)})\). 做乘法呢? 这需要我们知道 \(a, b\) 的最 "低" 位, 也即把它们写作 \(a = p^u (x + O(p^{n-u}))\) 和 \(b = p^v (y + O(p^{m-v}))\). 我们取 \(u, v\) 是满足 \(x, y\) 是 "单位" 的数, 也即 \(x\) 是整数, 且 \(1/x\) 也是整数.
注意, 对于 \(\mathbb Z\) 而言, 单位只有 \(\pm 1\), 很平凡. 但对于 \(\mathbb Z_p\) 而言, 单位就是 \(\mathbb Z_p\) 中那些不是 \(p\) 的倍数的, 这就很多了. 我们将其记为 \(\mathbb Z_p^\times\).
这样我们就有 \(ab = p^{u+v}(xy + O(p^{\min(n-u,m-v)}))\).
从这个角度来说, 我们可以从数值分析的说法来看待 "取模" 这件事, 无非就是保留了一个计算精度. 那么对于 \(p/p\) 这种东西, 如果是看成保留了 \(p^2\) 级别的精度, 那其实又可以解释成 \((p + O(p^2)) / (p + O(p^2)) = 1 + O(p)\) 了.
\(\sqrt 2\) 取模怎么做?
如果想知道 \(\sqrt 2 \bmod 7\) 的结果, 我们可以类比之前对于有理数取模的思考. \(\sqrt 2\) 是一个满足 \(x^2 = 2\) 的数, 那么在 \(\mathbb F_7\) 中, 我们发现 \(3^2 = 9 \equiv 2\), 所以 \(3\) 是 \(2\) 的一个平方根.
类似地, 如果一些无理数是通过代数方程的根给出的, 我们可以类似地考虑它在 \(\mathbb F_p\) 中的情况.
有的多项式在 \(\mathbb F_p\) 里是没有解的, 但是实际上, \(\sqrt 2\) 自己也不在 \(\mathbb Q\) 里. 这种时候, 无论对于 \(\mathbb Q\) 侧还是 \(\mathbb F_p\) 侧, 都是要进行扩域的.
\(\pi\) 取模怎么做?
还有另一种视角来看待 \(p\) 进数的构造, 对我们接下来要讲的故事来说比较重要.
\(\mathbb Q\) 上常见的绝对值是这样一个函数 \(|\cdot|\colon \mathbb Q \to \mathbb R_{\geq 0}\), 它满足
- \(|x| = 0 \iff x = 0\),
- \(|xy| = |x||y|\),
- 和三角不等式 \(|x+y| \leq |x| + |y|\).
但如果从 "\(p\) 的计算精度" 来考虑, 我们发现完全有其他类型的绝对值 \(|\cdot |_p\), 一般定义为 \(|x|_p = p^{-\nu(x)}\), 其中 \(\nu(x)\) 是最大的 \(k\) 满足 \(p^k \mid x\).
进一步地, 这种 \(p\) 进绝对值其实要比一般的绝对值性质更强, 也即满足 \(|x+y|_p \leq \max(|x|_p, |y|_p)\), 一般称为强三角不等式.
我们之前做牛顿迭代的时候, 假设得到了一个序列 \(x_1,\dots,x_n,\dots\), 那么这个序列的精度是越来越高的, 我们发现有 \(|x_n - x_m|_p \to 0\), 这是一个 Cauchy 序列.
对于 \(\mathbb Q\) 而言, Cauchy 序列本身未必在 \(\mathbb Q\) 上有一个极限, 但对于寻常绝对值而言, \(\mathbb Q \subset \mathbb R\), 后者继承了前者的绝对值, 而且任意 Cauchy 序列一定有极限.
那么对于 \(p\) 进数而言, 我们也可以考虑 \(\mathbb Q \subset \mathbb Q_p\), 我们可以用显而易见的方式定义 \(\mathbb Q_p\) 上的 \(p\) 进绝对值, 且和 \(\mathbb Q\) 上的一致. 并且因为我们 "允许了无限小数", 所以任意 Cauchy 序列也一定有极限: 直观上说, 就是每一位在序列充分靠后的位置就稳定了.
事实上, 一种构造 \(\mathbb Q_p\) 的视角是反过来的: 直接根据等价的 Cauchy 序列来定义 \(\mathbb Q_p\), 也即两串 \(\mathbb Q\) 上的序列 \(\{x_n\}, \{y_n\}\) 等价当且仅当 \(|x_n - y_n|_p \to 0\).
对于域 \(K\) 给定一个绝对值 \(|\cdot|\colon K \to \mathbb R_{\geq 0}\), 根据这个绝对值给出的 Cauchy 序列的等价类, 即记为 \(K\) 的完备化 \(\hat K\).
这个构造对于常用的绝对值 \(|\cdot |\) 就给出了 \(\hat {\mathbb Q} = \mathbb R\), 而对于 \(p\) 进绝对值 \(|\cdot |_p\) 就给出了 \(\hat {\mathbb Q}_p = \mathbb Q_p\). 当然前者在绝对值的定义里已经用到了 \(\mathbb R\), 所以不能作为最初构造实数的方法, 但可以纳入这个框架统一解释.
完备化 \(\hat K\) 的目的是为了让一个域上的 Cauchy 序列都有极限, 而我们一般扩大一个域还有另一个目的则是为了让多项式方程都有解. 这被称作代数闭包 \(\overline K\).
一般而言, 一个域 \(K\) 的代数闭包 \(\overline K\) 要满足的条件是每个多项式方程都有根, 并且每个元素都满足一个系数为 \(K\) 的多项式方程的根 (后者这个条件是为了让它是 "闭包", 也即没有包含额外冗余的东西).
代数闭包看似神秘, 但构造其实是接近一个形式游戏的. 我们在此仅勾勒构造的思路. 如果我们想要让某个不可约多项式 \(p(X) \in K[X]\) 有解, 只需要考虑扩域 \(k[X]/p(X)\). 这个形式元 \(X\) 就成了多项式 \(p\) 本身的一个解. 现在我们有两个扩域 \(K[X]/p(X)\) 和 \(K[Y]/p(Y)\), 我们想对 \(X, Y\) 进行四则运算, 就想办法把它们放在一个更大的扩域里面. 当我们做运算的时候, 每个式子都只涉及有限个形式元, 按照规则把它们放在一个扩域里面就可以了. 这种对所有有限情况的粘合实际上是所谓的一个正向极限.
对于 \(\mathbb R\) 而言, 大家熟悉 \(\overline{\mathbb R} = \mathbb C\), 只需要添加复数之后就完成了, 得到的 \(\mathbb C\) 既是代数闭的, 也是完备的.
然而对于 \(\mathbb Q_p\) 而言, 事情就不是那么简单了. 事实上, \(\overline{\mathbb Q_p}\) 虽然代数闭了, 但又不是完备的了. 尽管我们还没有定义整个 \(\overline{\mathbb Q_p}\) 上的绝对值是怎样的, 但可以证明的是存在唯一的将 \(|\cdot |_p\) 延拓到 \(\overline{\mathbb Q_p}\) 上的方式 (不难猜到对于有 \(n\) 个共轭的元素 \(x\), 它必然是 \(|x|_p = \left|\prod \sigma x\right|_p^{1/n}\), 但证明它满足三角不等式还需花些功夫), 并且可以构造一个其上的 Cauchy 序列, 但这个序列没有极限.
于是, 考虑再次将 \(\overline{\mathbb Q_p}\) 完备化, 得到 \(\widehat{\overline{\mathbb Q_p}}\). 这个域现在是完备的了, 看起来我们还需要再次取代数闭包, 一直做下去. 但事实上, 可以证明的是, \(\widehat{\overline{\mathbb Q_p}}\) 同样是代数闭的. 大概思路就是, 对于一个具体的多项式, 首先不妨设其无重根, 然后用 \(\overline {\mathbb Q_p}\) 中的元素逼近这个多项式的系数, 当误差充分小的时候, 每个根落在一些互不相交的 "圆盘" 之中, 并且这些根接下来随着误差的减小也构成一个 Cauchy 序列, 从而有极限, 这个极限则是原多项式在 \(\widehat{\overline{\mathbb Q_p}}\) 中的根.
这个域就是所谓的 \(p\) 进复数 \(\mathbb C_p\), 或者写作 \(\Omega_p\).
当一个域 \(K\) 上有满足强三角不等式的绝对值的时候, 容易验证 \(\mathcal O_K = \{x \in K : |x| \leq 1\}\) 是一个环, 可以看做是 \(K\) 上的 "整数". 例如 \(\mathbb Q_p\) 而言对应的整数就是 \(\mathbb Z_p\). 进一步可以验证, 这个环有一个唯一的极大理想 \(\mathfrak m = \{x \in K : |x| < 1\}\). 那么 \(\mathcal O / \mathfrak m\) 就是一个域, 对于 \(\mathbb Q_p\) 而言就是 \(\mathbb Z_p / p\mathbb Z_p = \mathbb F_p\).
我们容易发现, \(\overline{\mathbb Q_p}\) 的 \(\mathcal O / \mathfrak m\) 是一个包含 \(\mathbb F_p\) 的代数闭域, 进一步可以确定是 \(\mathbb F_p\) 的代数闭包. 而容易发现再做一次完备化, 得到的 \(\mathbb C_p\) 并没有让 \(\mathcal O / \mathfrak m\) 变大, 所以 \(\mathbb C_p\) 的 \(\mathcal O / \mathfrak m\) 依然是 \(\mathbb F_p\) 的代数闭包.
所以, 对于 \(\mathbb C_p\) 的 "数", 我们可以认为那些 "整数" \(\mathcal O\) 是可以 "取模" 的, 但取模的结果也是只保证落在 \(\overline{\mathbb F_p}\) 之内.
所以到底, \(\pi\) 可以取模吗?
我们熟悉的 \(\pi\) 是在 \(\mathbb C\) 里的, 我们首先要知道 \(\mathbb C\) 和 \(\mathbb C_p\) 有没有关系. 一个有趣的事实是, \(\mathbb C\) 和 \(\mathbb C_p\) 作为是同构的, 尽管这个同构必不可能保持绝对值. 也就是说, 我们确实可以在 \(\mathbb C_p\) 里找到一个数, 根据域的同构将 \(\pi\) 映到它.
但遗憾的是, 这个同构是利用选择公理得到的, 所以这并不代表我们现在能够在 \(\mathbb C_p\) 里找到真的写出一个 "自然的" \(\pi\). 所以从这个角度来说, 我们还是不知道 \(\pi\) 取模的结果是什么.
所以为什么我要写这篇小品呢... 也许有一天我们真的会用到比 \(\mathbb Z_p\) 大一点的东西上做计算 (真的吗?), 在这里稍微以科普的角度省略一些细节.
那么, 既然我们可以以近似精度表示一个复数, 能不能近似精度表示一个 \(\mathbb C_p\) 呢? 显然我们可以考虑从 Cauchy 列给予启发的方式入手, 结合维护数码的想法.
直观上, 对于 \(\mathbb Z_p\), 我们是提取一个个 \(\sum a_i p^i\) 的数码 \(a_i\). 类似的, 可以将 \(\mathbb C_p\) 的元素写成 \(\sum_i a_i p^i\), 但这里的 \(i\) 是有理数, 尽管对于非零项来说, 它们趋向于无穷.
另一个要解决的问题是, 原来在 \(\mathbb Z_p\) 里我们给每个 \(\mathbb F_p\) 里的原像直接找了个 \(\{0,\dots,p-1\}\), 但对于 \(\overline{\mathbb F_p}\) 来说这是不太对的. 注意到实际上 \(\overline{\mathbb F_p} = \bigcup \mathbb F_{p^n}\), (严格来说应该用 \(\varinjlim\) 体现它们粘合的方式), 我们要怎么找一个 \(\mathbb C_p\) 中的东西对应于某个 \(\mathbb F_q\) 呢? 一个方法是注意这些元素都满足 \(x^q = x\), 所以可以选取 \(\mathbb C_p\) 中提升出的那个 \(x^q=x\) 的解, 这被称为 Teichmüller 代表元. 当然, 计算的时候如何把两个 Teichmüller 代表元相加, 并计算出剩下部分的数码, 又是另一个问题了.