同态加密中的 NTT——从多项式乘法到数论变换
如果你看过全同态加密的实现(BFV、BGV、CKKS),一定会反复遇到一个词:NTT(Number Theoretic Transform,数论变换)。它是 FHE 性能的核心,没有它,同态加密的多项式运算会慢到不可用。
这篇文章从零讲清楚 NTT 是什么、为什么用它、以及它在同态加密里到底干了什么。
问题:多项式乘法的瓶颈
基于 RLWE 的同态加密方案里,密文是多项式,明文也是多项式。加密、解密、同态加法、同态乘法——所有操作都发生在多项式环上:
其中
直接做两个
NTT 的思路:换个坐标系算
FFT 的经典思路:多项式有两种表示方式。
系数表示(coefficient representation):
点值表示(evaluation representation):
系数表示下,
FFT 的作用就是在两种表示之间高效转换:
NTT 就是有限域上的 FFT。 把复数单位根
数学核心:有限域里的单位根
FFT 能工作,靠的是复数域中单位根的两个性质:
(折半引理)
NTT 需要模
这样的
这就是为什么同态加密方案里的模数都很”讲究”——比如 CKKS 的模数都是 2N | q-1 这种形式的素数。不是随便挑的素数都能拿来用。
一旦找到了
正变换(NTT)——系数 → 点值,蝴蝶操作使用
1 | for len = 2, 4, 8, ..., N: |
逆变换(INTT)——点值 → 系数,蝴蝶操作使用
在 HE 里到底怎么用的
以 BFV 为例,两个密文多项式
1 | 1. NTT(ct_0) → ct_0 的 NTT 表示 |
总复杂度:两次正变换 + N 次模乘 + 一次逆变换 =
实际实现中还有一个关键优化——负包裹卷积(negacyclic convolution)。多项式环是 x^N + 1 而不是 x^N - 1,这需要在变换前后做一次”扭转”(twisting):
- NTT 之前:
- INTT 之后:
其中
一个 Python 示例
下面用 Python 写一个最小可跑的 NTT(不做任何优化,纯粹展示结构):
1 | def ntt(a, q, w): |
实际工程中(OpenFHE、SEAL、Lattigo 等库),NTT 高度优化:使用了位逆序重排、SIMD 向量化、预计算旋转因子表、甚至 GPU 加速。但核心结构和上面一样。
NTT 的局限和替代
NTT 不是万能的。它的主要限制:
- **模数必须满足
**,这限制了参数选择。非 NTT 友好的素数就不能用。 - 只适用于 2 的幂次,
通常是 2 的幂。 - 对自举不够友好——自举需要更大规模的多项式运算,有些方案开始用 GSM(Galois Summation Method)等替代方案。
对于非 2 的幂次或非标准模数的情况,有混合基 NTT(mixed-radix NTT)、ECFFT(elliptic curve FFT)等替代品,但那是另一个话题了。
总结
NTT 是同态加密性能的基石。一句话概括:
把
的多项式乘法变成 ,靠的是在有限域里找到一个”单位根”,然后用和 FFT 完全相同的分治策略。
理解了 NTT,就理解了为什么 HE 方案要那样选参数,以及为什么”一次同态乘法”的计算成本是可以接受的。