本文是系列文章的第三篇。我们将在零知识证明电路的背景下介绍有限域。前两章分别是 P vs NP 及其在零知识证明中的应用 和 算术电路 。
在关于算术电路的上一章中,我们指出过一个局限性:我们无法对数字 $2/3$ 进行编码,因为它无法用二进制精确表示。我们还指出过,我们没有明确的方法来处理溢出(overflow)。
这两个问题都可以通过一种在一般密码学中很流行的算术变体来无缝处理,它被称为 有限域(finite fields) 。
给定一个素数 p ,我们可以通过取整数集合 $\set{0, 1, 2, …, p-1}$ 并定义加法和乘法在模 $p$ 的情况下进行,来构建一个包含 p 个元素的有限域。我们将首先将自己限制在元素个数为素数的域中。
例如,如果素数 $p$ 是 $7$ ,那么有限域中的元素就是 $\set{0, 1, 2, 3, 4, 5, 6}$ 。任何超出此范围的数字( $≥ p$ 或 $< 0$ )总是通过模运算映射到该范围内的一个“等价”数字。“等价”的专业术语是 同余(congruent) 。
<aside> 💡 💡 打个比方:有限域就像一个『时钟』。$p=7$ 的域,就是表盘上只有 $0$ 到 $6$ 七个刻度。无论你算出多大的数,都要绕着表盘转,最后落在这七个刻度上的某一个——比如时针走 $12$ 格,在只有 $7$ 格的表盘上其实就停在第 $5$ 格。这样一来,数字永远跑不出这个小圈子,既不会『溢出』也不用担心小数。
</aside>
模运算计算的是数字除以素数时的余数。例如,如果我们的模是 7,数字 12 同余 于 5,即 $12 \pmod 7 = 5$ ,数字 14 同余于 0。同样地,当我们把两个数字相加时,比如 3 + 5,得到的和 8 同余于 1(8 mod 7 = 1)。下面的动画说明了这一点:
<aside> 💡 💡 一句话理解『同余』:两个数除以 $p$ 后余数一样,就算『同余』,在这个域里它们被当成同一个东西。就像时钟上 $13$ 点和下午 $1$ 点指针指向同一处——$13$ 和 $1$ 对 $12$ 来说是同余的。所以 $12 \pmod 7 = 5$ 的意思就是:$12$ 绕过表盘一圈后,正好停在 $5$ 这个刻度。
</aside>

动画:3 + 5 在模 7 下绕回等于 1(数轴环绕示意)
在 Python 中,上述计算可以通过以下方式计算:
p = 7
result = (3 + 5) % p
print(result) # prints 1
在本章中,无论何时执行计算,我们都将结果表示为 $0…(p-1)$ 范围内的数字,即我们的有限域 p 中的元素集合。例如,2 * 5 = 10,我们将其“化简”为 3(模 7)。
注意 3 + 5 是如何“溢出” 6 这个限制的。 在有限域中,溢出并不是一件坏事,我们将溢出行为定义为计算的一部分。 在模 7 的有限域中,5 + 3 被定义为 1。
下溢(Underflows)的处理方式也类似。例如, $3 - 5 = - 2$ ,但在模 7 中我们得到 $5$ ,因为 $7 - 2 = 5$ 。
<aside> 💡 💡 这里最绕的是负数怎么办:把表盘想成一个环,往前走会绕回来,往后退也一样。$3-5$ 要往回退 $2$ 格,但表盘上没有负数,于是从 $0$ 往回退就绕到了最大的那头——退 $2$ 格落在 $5$(因为 $7-2=5$)。负号在这里不代表『欠债』,只代表『反方向绕』。
</aside>
在典型的编程语言中,我们将有限域中的加法写为 (6 + 1) % 7 == 0 ,但在数学符号中,我们通常表示为
$$ 0 = 6 + 1 \pmod 7 $$
或者更一般地说,