<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Mathematics on kenji.blog</title><link>http://kenji.blog/zh-cn/categories/mathematics/</link><description>Recent content in Mathematics on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 22:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/zh-cn/categories/mathematics/index.xml" rel="self" type="application/rss+xml"/><item><title>利用费马小定理的加密基础与实现</title><link>http://kenji.blog/zh-cn/p/fermats-little-theorem-cryptography-implementation/</link><pubDate>Fri, 11 Sep 2026 22:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/p/fermats-little-theorem-cryptography-implementation/</guid><description>&lt;img src="http://kenji.blog/p/fermats-little-theorem-cryptography-implementation/img/eyecatch.jpg" alt="Featured image of post 利用费马小定理的加密基础与实现" />&lt;h2 id="1-引言支撑现代密码学的数学奥秘">1. 引言：支撑现代密码学的数学奥秘
&lt;/h2>&lt;p>在现代数字社会，尤其是基于互联网的通信中，“加密”已经成为不可或缺的基础设施技术。我们在网络浏览器上通过HTTPS安全地浏览网站、在网上银行进行金融交易、在即时通讯应用上进行私密交流，这一切的背后，都是由基于高级数学理论的加密协议在发挥作用。其中发挥关键作用的是“公钥加密算法”，其代表就是 &lt;strong>RSA加密&lt;/strong>。&lt;/p>
&lt;p>包括RSA加密在内的许多加密算法的安全性与合理性，都极大地依赖于17世纪法国数学家皮埃尔·德·费马（Pierre de Fermat）发现的一个非常优美且强大的定理。那就是 &lt;strong>费马小定理（Fermat&amp;rsquo;s Little Theorem）&lt;/strong>。此外，将其一般化的莱昂哈德·欧拉（Leonhard Euler）定理也在密码学理论中发挥了决定性作用。&lt;/p>
&lt;p>本文将从基础开始全面解析，纯数学的发现——费马小定理，是如何被应用到现代实用的加密技术中，特别是在“素数测试”和“RSA加密”中的。本篇将是一份非常详细的技术指南，涵盖数学证明、加密与解密的机制，以及使用 C++ 和 Python 进行的具体算法实现。&lt;/p>
&lt;hr>
&lt;h2 id="2-同余与模运算基础">2. 同余与模运算基础
&lt;/h2>&lt;p>要理解费马小定理，首先需要熟悉“模运算（同余式）”这一数学概念。模运算是一种关注除以某个固定数（称为模数）后所得“余数”的计算体系。因为类似于钟表表盘（12小时转一圈）的计算，所以也被称为“时钟算术”。&lt;/p>
&lt;p>当整数 $a$ 和 $b$ 除以正整数 $n$ 的余数相等时，在数学上可以这样描述：&lt;/p>
$$
a \equiv b \pmod n
$$
&lt;p>这读作“在模 $n$ 下 $a$ 和 $b$ 同余”。例如，17 除以 5 的余数是 2，12 除以 5 的余数也是 2。因此，可以写成如下形式：&lt;/p>
$$
17 \equiv 12 \pmod 5 \equiv 2 \pmod 5
$$
&lt;p>在模运算中，常规的四则运算（加、减、乘）依然成立。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>加法&lt;/strong>: 如果 $a \equiv b \pmod n$ 且 $c \equiv d \pmod n$，那么 $a + c \equiv b + d \pmod n$&lt;/li>
&lt;li>&lt;strong>减法&lt;/strong>: 如果 $a \equiv b \pmod n$ 且 $c \equiv d \pmod n$，那么 $a - c \equiv b - d \pmod n$&lt;/li>
&lt;li>&lt;strong>乘法&lt;/strong>: 如果 $a \equiv b \pmod n$ 且 $c \equiv d \pmod n$，那么 $a \times c \equiv b \times d \pmod n$&lt;/li>
&lt;li>&lt;strong>幂运算&lt;/strong>: 如果 $a \equiv b \pmod n$，那么对于任意自然数 $k$，都有 $a^k \equiv b^k \pmod n$&lt;/li>
&lt;/ol>
&lt;p>但是，关于 &lt;strong>除法&lt;/strong> 则需要特别注意。通常情况下，即使 $a \times c \equiv b \times c \pmod n$，也不能直接将两边同时除以 $c$ 得到 $a \equiv b \pmod n$。这只有在 $c$ 和 $n$ 互质（最大公约数为1）的情况下才成立。这个“模逆元”的概念，在后文所述的RSA加密密钥生成中极其重要。&lt;/p>
&lt;hr>
&lt;h2 id="3-费马小定理的数学背景与证明">3. 费马小定理的数学背景与证明
&lt;/h2>&lt;p>掌握了模运算的基础后，让我们进入正题，来看费马小定理。&lt;/p>
&lt;h3 id="31-定理的定义">3.1 定理的定义
&lt;/h3>&lt;p>费马小定理的表述如下：&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>费马小定理 (Fermat&amp;rsquo;s Little Theorem)&lt;/strong>
设 $p$ 为一个素数，且 $a$ 是任意一个不是 $p$ 的倍数的整数（即 $a$ 和 $p$ 互质）。此时，以下同余式成立：
&lt;/p>
$$ a^{p-1} \equiv 1 \pmod p $$
&lt;/blockquote>
&lt;p>另外，去掉“$a$ 不是 $p$ 的倍数”这一条件，将其表述为对所有整数 $a$ 都成立的形式也很常见。这种情况下，两边同时乘以 $a$，变成如下形式：&lt;/p>
$$
a^p \equiv a \pmod p
$$
&lt;h3 id="32-通过具体例子进行验证">3.2 通过具体例子进行验证
&lt;/h3>&lt;p>定理是否真的成立呢？让我们用具体的数字来验证一下。
假设素数 $p = 5$。则 $p-1 = 4$。选择一个不是 $p$ 的倍数的整数作为 $a$。&lt;/p>
&lt;ul>
&lt;li>当 $a = 2$ 时: $2^{5-1} = 2^4 = 16$。$16 \div 5 = 3$ 余 $1$。因此 $16 \equiv 1 \pmod 5$。（成立）&lt;/li>
&lt;li>当 $a = 3$ 时: $3^{5-1} = 3^4 = 81$。$81 \div 5 = 16$ 余 $1$。因此 $81 \equiv 1 \pmod 5$。（成立）&lt;/li>
&lt;li>当 $a = 4$ 时: $4^{5-1} = 4^4 = 256$。$256 \div 5 = 51$ 余 $1$。因此 $256 \equiv 1 \pmod 5$。（成立）&lt;/li>
&lt;/ul>
&lt;p>就像这样，无论选择什么样的 $a$（只要不是5的倍数），其4次方除以5的余数一定为1。这看起来像魔法，但其实源于素数所具有的优美性质。&lt;/p>
&lt;h3 id="33-定理的数学证明">3.3 定理的数学证明
&lt;/h3>&lt;p>为什么会成立这样的定理呢？这里介绍一种使用剩余类集合的优雅证明方法。&lt;/p>
&lt;p>考虑集合 $S = \{1, 2, 3, \dots, p-1\}$。这些是除以 $p$ 后余数为 $1$ 到 $p-1$ 的整数的代表元。
现在，考虑一个新集合 $T$，它是将 $S$ 中的每个元素都乘以与 $p$ 互质的整数 $a$ 得到的：
&lt;/p>
$$ T = \{1a, 2a, 3a, \dots, (p-1)a\} $$
&lt;p>考虑集合 $T$ 的各个元素除以 $p$ 的余数。令人惊讶的是，这些余数，虽然顺序可能发生改变，但它们组成的集合与原来的集合 $S$ 完全一致。
这是因为：&lt;/p>
&lt;ol>
&lt;li>$T$ 的元素不可能成为 $p$ 的倍数（因为 $a$ 和原来的元素都不是 $p$ 的倍数）。&lt;/li>
&lt;li>在 $T$ 中，不存在在模 $p$ 下同余的两个不同元素。如果 $ia \equiv ja \pmod p$ （其中 $i \neq j$），因为 $a$ 和 $p$ 互质，所以可以除以 $a$，得到 $i \equiv j \pmod p$，这导致矛盾。&lt;/li>
&lt;/ol>
&lt;p>因此，$S$ 中所有元素的乘积，与 $T$ 中所有元素的乘积，在模 $p$ 下是同余的。&lt;/p>
$$
(1a) \times (2a) \times \dots \times ((p-1)a) \equiv 1 \times 2 \times \dots \times (p-1) \pmod p
$$
&lt;p>整理左边，因为有 $p-1$ 个 $a$，所以：&lt;/p>
$$
a^{p-1} \cdot (p-1)! \equiv (p-1)! \pmod p
$$
&lt;p>因为 $(p-1)!$ 与 $p$ 互质，所以两边可以同时除以 $(p-1)!$，最终导出以下定理：&lt;/p>
$$
a^{p-1} \equiv 1 \pmod p
$$
&lt;p>这就是费马小定理的证明。&lt;/p>
&lt;hr>
&lt;h2 id="4-欧拉函数与欧拉定理">4. 欧拉函数与欧拉定理
&lt;/h2>&lt;p>费马小定理是关于“素数 $p$”的定理，而将其推广到“任意正整数 $n$”的人，正是莱昂哈德·欧拉。要理解RSA加密，这种推广是必不可少的。&lt;/p>
&lt;h3 id="41-欧拉函数-phin">4.1 欧拉函数 $\phi(n)$
&lt;/h3>&lt;p>欧拉的totient函数（或称欧拉的 $\phi$ 函数） $\phi(n)$，是一个表示“从 $1$ 到 $n$ 的整数中，与 $n$ 互质的数的个数”的函数。&lt;/p>
&lt;ul>
&lt;li>对于素数 $p$，从 $1$ 到 $p-1$ 的所有整数都与 $p$ 互质，因此 $\phi(p) = p - 1$。&lt;/li>
&lt;li>对于两个不同的素数 $p, q$，它们的乘积 $n = p \times q$，$\phi(n)$ 可以通过一个非常简单的公式求出：
$$ \phi(p \times q) = \phi(p) \times \phi(q) = (p - 1)(q - 1) $$&lt;/li>
&lt;/ul>
&lt;p>这个性质，构成了RSA加密中密钥生成的核心逻辑。&lt;/p>
&lt;h3 id="42-欧拉定理">4.2 欧拉定理
&lt;/h3>&lt;p>欧拉对费马小定理进行了如下的推广。&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>欧拉定理 (Euler&amp;rsquo;s Theorem)&lt;/strong>
对于正整数 $n$ 以及与其互质的整数 $a$，以下等式成立：
&lt;/p>
$$ a^{\phi(n)} \equiv 1 \pmod n $$
&lt;/blockquote>
&lt;p>如果 $n$ 是素数 $p$，那么 $\phi(p) = p - 1$，此时就变成了费马小定理本身（$a^{p-1} \equiv 1 \pmod p$）。换句话说，费马小定理只是欧拉定理的一个特例。&lt;/p>
&lt;hr>
&lt;h2 id="5-寻找巨大的素数费马素性测试">5. 寻找巨大的素数：费马素性测试
&lt;/h2>&lt;p>在加密技术（如RSA加密或Diffie-Hellman密钥交换等）中，需要高速地找出长达数百位的“巨大素数”。然而，为了判断一个巨大的数 $N$ 是否为素数，如果尝试用 $2$ 到 $\sqrt{N}$ 之间的所有数去试除，这种“试除法”可能需要耗费宇宙寿命般的时间。&lt;/p>
&lt;p>于是，利用费马小定理逆向思维的“概率素性测试”——&lt;strong>费马测试（Fermat Primality Test）&lt;/strong> 登场了。&lt;/p>
&lt;h3 id="51-什么是概率素性测试">5.1 什么是概率素性测试
&lt;/h3>&lt;p>根据费马小定理，如果 $p$ 是素数，那么对于任意的 $a$ ($1 &lt; a &lt; p$)，$a^{p-1} \equiv 1 \pmod p$ 必定成立。
取其逆否命题，可以说：“如果对于某个 $a$，$a^{p-1} \not\equiv 1 \pmod p$，那么 $p$ &lt;strong>绝对不是素数（是合数）&lt;/strong>”。&lt;/p>
&lt;p>因此，如果想判断 $N$ 是否为素数，可以随机选取几个 $a$，计算 $a^{N-1} \pmod N$ 看是否等于 $1$。如果只要有一次得出除 $1$ 以外的结果，就可以确定 $N$ 是合数。如果尝试多次结果都为 $1$，就可以以很高的概率判断 $N$ “可能是一个素数”。&lt;/p>
&lt;h3 id="52-算法解说与流程图">5.2 算法解说与流程图
&lt;/h3>&lt;p>费马测试的算法如下：&lt;/p>
&lt;div class="mermaid">flowchart TD
Start["开始"] --> Input["输入待判断的数 p 和测试次数 k"]
Input --> LoopStart["循环 i = 0 到 k-1"]
LoopStart --> Condition{"i &lt; k ?"}
Condition -- "Yes" --> RandomA["在 1 &lt; a &lt; p-1 范围内随机选取整数 a"]
RandomA --> Calc["计算模幂 a^(p-1) mod p"]
Calc --> CheckPrime{"结果是 1 吗？"}
CheckPrime -- "No" --> ReturnComposite["p 是合数（确定）"]
CheckPrime -- "Yes" --> Increment["i 递增"]
Increment --> Condition
Condition -- "No" --> ReturnPrime["p 可能是素数（概率性）"]
ReturnComposite --> End["结束"]
ReturnPrime --> End&lt;/div>
&lt;h3 id="53-卡迈克尔数伪素数的陷阱">5.3 卡迈克尔数（伪素数）的陷阱
&lt;/h3>&lt;p>费马测试速度极快，但也存在一个重大的缺点。那就是存在这样一种恶魔般的数：虽然是合数，但对于所有的 $a$，都满足 $a^{N-1} \equiv 1 \pmod N$。这被称为 &lt;strong>卡迈克尔数 (Carmichael numbers)&lt;/strong>。最小的卡迈克尔数是 $561$ ($3 \times 11 \times 17$)。&lt;/p>
&lt;p>因为卡迈克尔数的存在，单纯的费马测试无法做出绝对的素数判定。因此，在实际的加密系统（如OpenSSL等）中，标准使用的是改进了费马测试的 &lt;strong>米勒-拉宾（Miller-Rabin）素性测试&lt;/strong>。米勒-拉宾测试能够识破卡迈克尔数，从而将误判的概率实质上降至零。&lt;/p>
&lt;h3 id="54-高速模幂运算反复平方法">5.4 高速模幂运算（反复平方法）
&lt;/h3>&lt;p>在素性测试算法中需要计算 $a^{N-1} \pmod N$，但当 $N$ 非常巨大时，$a^{N-1}$ 的位数将是天文数字，计算机的内存根本装不下。
解决这个问题的方法是 &lt;strong>反复平方法（Exponentiation by Squaring）&lt;/strong> 或模幂运算。通过在计算的每一步都取模（mod N），始终保持数值小于 $N$，从而实现极高速（计算复杂度为 $O(\log N)$）的计算。&lt;/p>
&lt;hr>
&lt;h2 id="6-素性测试与模幂的实现">6. 素性测试与模幂的实现
&lt;/h2>&lt;p>那么，让我们用C++和Python来实现费马素性测试和反复平方法吧。&lt;/p>
&lt;h3 id="61-c-实现">6.1 C++ 实现
&lt;/h3>&lt;p>在C++中，标准的整数类型很容易溢出，因此处理巨大的数字通常需要高精度整数库（如GMP等），但这里为了理解算法，展示在64位整数（&lt;code>unsigned long long&lt;/code>）范围内的实现。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;random&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 高速模幂运算 (a^b mod m) - 反复平方法
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="nf">power_mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 当 b 的最低位为 1 时，结果乘以 a
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">__int128&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">result&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 为防止溢出扩展为128位
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// a 自身平方
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">__int128&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// b 右移（减半）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">/=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 费马素性测试
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">bool&lt;/span> &lt;span class="nf">fermat_is_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">iterations&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">random_device&lt;/span> &lt;span class="n">rd&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mt19937_64&lt;/span> &lt;span class="n">gen&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rd&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">uniform_int_distribution&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">dis&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">iterations&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="o">++&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dis&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">gen&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 如果 a^(p-1) mod p 不等于 1，则是合数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">power_mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 可能是素数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">num&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1000000007&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 已知的素数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">fermat_is_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">num&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">num&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34; is probably prime.&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">num&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34; is composite.&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="62-python-实现">6.2 Python 实现
&lt;/h3>&lt;p>Python的内置整数类型支持高精度整数，所以完全不需要担心溢出问题。此外，Python的内置函数 &lt;code>pow(a, b, m)&lt;/code> 内部就是使用了反复平方法，因此速度非常快。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">random&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">fermat_is_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">iterations&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> 使用费马素性测试进行概率素数判断
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">_&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">iterations&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 在 2 到 p-2 之间随机选取数字 a&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">random&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">randint&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 计算 a^(p-1) mod p。内置的 pow 函数速度很快。&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span> &lt;span class="c1"># 确定是合数&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">True&lt;/span> &lt;span class="c1"># 可能是素数&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 测试&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">number_to_test&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">104729&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="n">fermat_is_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">number_to_test&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">number_to_test&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> 可能是素数。&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">number_to_test&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> 是合数。&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="7-在rsa加密中的应用费马与欧拉的结晶">7. 在RSA加密中的应用：费马与欧拉的结晶
&lt;/h2>&lt;p>费马小定理（以及欧拉定理）最伟大的应用领域，就是1977年由Rivest、Shamir和Adleman三人开发的 &lt;strong>RSA加密&lt;/strong>。
RSA加密是一种革命性的“公钥加密”系统，它实现了一种机制：用于加密的密钥（公钥）对全世界公开，而用于解密的密钥（私钥）只有接收者本人知晓。&lt;/p>
&lt;p>这种非对称性，是基于“对巨大的合数进行质因数分解极其困难”的计算复杂性安全保证的。&lt;/p>
&lt;h3 id="71-rsa加密机制密钥生成加密解密">7.1 RSA加密机制（密钥生成、加密、解密）
&lt;/h3>&lt;p>让我们通过Mermaid的时序图来了解RSA加密的整体通信流程。&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant Alice["爱丽丝 (接收者)"]
participant Bob["鲍勃 (发送者)"]
Alice->>Alice: "生成巨大的素数 p, q"
Alice->>Alice: "计算 N = p * q, φ(N) = (p-1)(q-1)"
Alice->>Alice: "计算公钥 e 和私钥 d (e*d ≡ 1 mod φ(N))"
Alice->>Bob: "发送公钥 (N, e)"
Note over Bob: "准备明文 M (M &lt; N)"
Bob->>Bob: "计算密文 C = M^e mod N"
Bob->>Alice: "发送密文 C"
Alice->>Alice: "计算明文 M = C^d mod N 进行解密"&lt;/div>
&lt;p>以下将解说数学上的详细步骤。&lt;/p>
&lt;h4 id="步骤1密钥生成接收者爱丽丝的操作">步骤1：密钥生成（接收者爱丽丝的操作）
&lt;/h4>&lt;ol>
&lt;li>随机生成两个巨大的素数 $p$ 和 $q$（这里会用到前面提到的素性测试算法）。&lt;/li>
&lt;li>计算它们的乘积 $N = p \times q$。这个 $N$ 是公开的。&lt;/li>
&lt;li>利用欧拉函数，计算 $\phi(N) = (p-1)(q-1)$。&lt;/li>
&lt;li>选择一个与 $\phi(N)$ 互质的整数 $e$ （公开指数）（通常使用 $e = 65537$）。&lt;/li>
&lt;li>计算 $e$ 的模逆元 $d$ （私有指数）。即，找到满足以下条件的 $d$：
$$ e \cdot d \equiv 1 \pmod{\phi(N)} $$
这个计算需要用到 &lt;strong>扩展欧几里得算法&lt;/strong>。&lt;/li>
&lt;/ol>
&lt;p>至此，&lt;strong>公钥为 $(N, e)$&lt;/strong>，&lt;strong>私钥为 $(N, d)$&lt;/strong>。（$p, q, \phi(N)$ 应立即销毁或严格保密）。&lt;/p>
&lt;h4 id="步骤2加密发送者鲍勃的操作">步骤2：加密（发送者鲍勃的操作）
&lt;/h4>&lt;p>假设鲍勃想给爱丽丝发送消息 $M$（$M$ 是字符数字化后的数值，且 $0 \le M &lt; N$）。
鲍勃使用爱丽丝的公钥 $(N, e)$，进行如下计算生成密文 $C$：&lt;/p>
$$
C \equiv M^e \pmod N
$$
&lt;p>然后通过网络将这个 $C$ 发送给爱丽丝。&lt;/p>
&lt;h4 id="步骤3解密接收者爱丽丝的操作">步骤3：解密（接收者爱丽丝的操作）
&lt;/h4>&lt;p>收到密文 $C$ 的爱丽丝，使用只有自己知道的私钥 $d$ 进行如下计算：&lt;/p>
$$
M' \equiv C^d \pmod N
$$
&lt;p>令人惊奇的是，这个计算结果 $M'$ 与原始消息 $M$ 完全一致。&lt;/p>
&lt;h3 id="72-为什么能够解密数学证明">7.2 为什么能够解密？（数学证明）
&lt;/h3>&lt;p>在这里，费马小定理（欧拉定理）发挥了真正的价值。为什么 $C^d \pmod N$ 能够还原成 $M$ 呢？&lt;/p>
&lt;p>展开解密的公式。
因为 $C \equiv M^e \pmod N$，所以：
&lt;/p>
$$ C^d \equiv (M^e)^d \equiv M^{ed} \pmod N $$
&lt;p>在密钥生成步骤中，我们选择 $d$ 使得 $e \cdot d \equiv 1 \pmod{\phi(N)}$。这意味着存在一个整数 $k$，可以写成如下形式：
&lt;/p>
$$ e \cdot d = 1 + k \cdot \phi(N) $$
&lt;p>将此代入上面的公式：
&lt;/p>
$$ M^{ed} = M^{1 + k \cdot \phi(N)} = M \cdot M^{k \cdot \phi(N)} = M \cdot (M^{\phi(N)})^k \pmod N $$
&lt;p>此时，&lt;strong>欧拉定理&lt;/strong> ($M^{\phi(N)} \equiv 1 \pmod N$) 就登场了。（※严格来说，这要求 $M$ 和 $N$ 互质，但在RSA中，$M$ 和 $N$ 不互质的概率是天文数字级别的低，而且利用中国剩余定理可以证明即使不互质等式也成立）。&lt;/p>
&lt;p>应用欧拉定理，由于 $M^{\phi(N)} \equiv 1$，因此：
&lt;/p>
$$ M \cdot (1)^k \equiv M \pmod N $$
&lt;p>漂亮地还原了 $M$！几百年前费马和欧拉发现的数字规律，完美地保证了现代数字通信的机密性。&lt;/p>
&lt;hr>
&lt;h2 id="8-rsa加密的玩具实现-python">8. RSA加密的玩具实现 (Python)
&lt;/h2>&lt;p>仅凭理论可能难以有直观感受，所以我们用Python来实际实现一下RSA加密的密钥生成、加密、解密的过程。这是一个用于教学的“玩具实现（Toy Implementation）”，但所使用的数学原理与实际完全相同。&lt;/p>
&lt;p>求模逆元 $d$ 的“扩展欧几里得算法”也会包含在实现中。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;span class="lnt">84
&lt;/span>&lt;span class="lnt">85
&lt;/span>&lt;span class="lnt">86
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">random&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 求最大公约数&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">b&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">a&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 扩展欧几里得算法 (求 ax + by = gcd(a,b) 中的 x, y)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 用于寻找 e*d ≡ 1 (mod φ(N)) 中的 d&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">extended_gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">extended_gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">g&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">//&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">mod_inverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">extended_gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">g&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="ne">Exception&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;逆元不存在&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">phi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 素数生成函数（简易版：生成较小的素数）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">generate_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">p&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">random&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">getrandbits&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 用简易判断替代前面提到的费马测试&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">p&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># RSA 密钥生成&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">generate_keypair&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">16&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">p&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">generate_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">q&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">generate_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 防止 p 和 q 相同&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">q&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">q&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">generate_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">q&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">phi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># e 通常使用 65537 等素数，但这里随机选择&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">e&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">random&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">randrange&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">g&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">e&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">random&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">randrange&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 计算私钥 d&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">d&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_inverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 公钥 (e, n), 私钥 (d, n)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">encrypt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pk&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">plaintext&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">pk&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 计算 plaintext^e mod n&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cipher&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">ord&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">char&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">char&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">plaintext&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">cipher&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">decrypt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sk&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ciphertext&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sk&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 计算 cipher^d mod n，并转换回字符&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">plain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nb">chr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">char&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">char&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">ciphertext&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plain&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 运行示例&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;__main__&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;--- RSA加密 玩具实现 ---&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">public_key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">private_key&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">generate_keypair&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">12&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 使用12位素数&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;公钥 (e, n): &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">public_key&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;私钥 (d, n): &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">private_key&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Hello Math!&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">原始消息: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 加密&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">encrypted_msg&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">encrypt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">public_key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">message&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;密文: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">encrypted_msg&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 解密&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">decrypted_msg&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">decrypt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">private_key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">encrypted_msg&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;解密后的消息: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">decrypted_msg&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>运行这段代码，你会看到字符数组被转换成陌生的数字数组（密文），然后通过私钥又完美地恢复成了原始字符串。&lt;/p>
&lt;hr>
&lt;h2 id="9-结语数学之美与实用性的交汇点">9. 结语：数学之美与实用性的交汇点
&lt;/h2>&lt;p>17世纪皮埃尔·德·费马发现这个“小定理”时，没有人觉得这会有什么用处。费马本人也是出于纯粹的数学探求欲才去研究数论的。&lt;/p>
&lt;p>然而，在大约300年后的20世纪70年代，计算机网络黎明期，作为确立安全通信协议不可或缺的加密技术，费马定理迎来了戏剧性的复苏。基于费马小定理的素性测试技术和基于欧拉定理的RSA加密，字面意义上支撑了现代的互联网基础设施。&lt;/p>
&lt;p>我们每天不经意间发送的微信消息、在淘宝上的购物，这一切都在 $a^{p-1} \equiv 1 \pmod p$ 这个简单而优美的数学公式之上舞动。费马小定理告诉我们，无论数学多么抽象，总有一天它必然会为人类所用。&lt;/p>
&lt;p>在学习编程和密码学理论时，理解其基础的数学结构，将成为深入理解那些作为黑盒提供的库的行为、并设计出更加安全的系统的强大武器。&lt;/p></description></item><item><title>格密码（Lattice-based cryptography）的数学直觉</title><link>http://kenji.blog/zh-cn/p/lattice-based-cryptography-math-intuition/</link><pubDate>Fri, 11 Sep 2026 21:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/p/lattice-based-cryptography-math-intuition/</guid><description>&lt;img src="http://kenji.blog/p/lattice-based-cryptography-math-intuition/img/eyecatch.jpg" alt="Featured image of post 格密码（Lattice-based cryptography）的数学直觉" />&lt;h1 id="1-引言后量子密码pqc的黎明与格密码的崛起">1. 引言：后量子密码（PQC）的黎明与格密码的崛起
&lt;/h1>&lt;p>支撑现代社会数字基础设施的是以RSA密码和椭圆曲线密码（ECC）为首的公钥密码技术。这些密码方案的安全性建立在“整数分解问题”或“离散对数问题”等被认为使用传统经典计算机无法高效求解（需要指数级时间）的数学难题之上。&lt;/p>
&lt;p>然而，彼得·秀尔（Peter Shor）在1994年提出的“Shor算法”在密码学界引起了轩然大波。该算法在数学上证明了，一旦大规模量子计算机问世，便能在多项式时间内解决整数分解问题和离散对数问题。这意味着目前被广泛使用的公钥密码在未来将变得完全可以被破解。&lt;/p>
&lt;p>为了应对这种“量子计算机的威胁（Quantum Threat）”，亟需研究即使使用量子计算机也难以破解的新型密码方案。这就是被称为“后量子密码（Post-Quantum Cryptography: PQC）”或“抗量子计算密码”的领域。&lt;/p>
&lt;p>PQC有几种有力的候选方案。包括基于哈希的密码、基于编码的密码、多变量多项式密码、同源密码等，其中目前最受瞩目、也是NIST（美国国家标准与技术研究院）PQC标准化进程核心的便是“格密码（Lattice-based cryptography）”。与其他方案相比，格密码的加解密处理速度非常快，而且具有被称为“从最坏情况复杂度（Worst-case complexity）归约到平均情况复杂度（Average-case complexity）”的特性，在密码学理论中拥有极强的安全性证明。&lt;/p>
&lt;p>本文将从格密码的基础——“格（Lattice）”的数学定义出发，深入剖析格上的困难问题：SVP（最短向量问题）和CVP（最近向量问题），以及作为现代格密码心脏的“LWE问题（Learning With Errors）”，并结合数学公式、几何直觉以及具体的数值计算例子，进行透彻的讲解。&lt;/p>
&lt;h1 id="2-格lattice的数学定义与几何直觉">2. 格（Lattice）的数学定义与几何直觉
&lt;/h1>&lt;h2 id="21-向量空间与格">2.1 向量空间与格
&lt;/h2>&lt;p>在数学中，“格（Lattice）”是指在$n$维实向量空间 $\mathbb{R}^n$ 内规则排列的离散点的集合。它与线性代数中学习的向量空间（Vector Space）相似，但存在决定性的区别。向量空间是由基向量通过“实数系数”的线性组合表示的连续空间，而格则是通过基向量的“整数系数”的线性组合表示的离散空间。&lt;/p>
&lt;p>让我们给出严谨的数学定义。考虑在$m$维实向量空间 $\mathbb{R}^m$ 中的 $n$ 个（$n \le m$）线性无关向量 $\mathbf{b}_1, \mathbf{b}_2, \dots, \mathbf{b}_n$。将这些向量作为列向量构成的矩阵记为 $B = [\mathbf{b}_1, \mathbf{b}_2, \dots, \mathbf{b}_n] \in \mathbb{R}^{m \times n}$。这个 $B$ 被称为格的“基（Basis）”。&lt;/p>
&lt;p>由这个基 $B$ 生成的格 $\mathcal{L}(B)$ 定义如下：&lt;/p>
$$
\mathcal{L}(B) = \left\{ \sum_{i=1}^{n} x_i \mathbf{b}_i \mathrel{\bigg|} x_i \in \mathbb{Z} \right\} = \{ B \mathbf{x} \mid \mathbf{x} \in \mathbb{Z}^n \}
$$
&lt;p>这里的重点是，系数 $x_i$ 被限定为整数 $\mathbb{Z}$，而不是实数 $\mathbb{R}$。因此，它并不是空间中无限存在的连续点，而是形成了像等距排列的交叉点一样的“离散点集”。&lt;/p>
&lt;h2 id="22-几何学意象">2.2 几何学意象
&lt;/h2>&lt;p>以二维平面 $\mathbb{R}^2$ 为例。如果我们选择基向量 $\mathbf{b}_1 = \begin{pmatrix} 1 \\ 0 \end{pmatrix}$ 和 $\mathbf{b}_2 = \begin{pmatrix} 0 \\ 1 \end{pmatrix}$，由它们生成的格就是坐标平面上所有整数坐标 $(x, y) \in \mathbb{Z}^2$ 的集合。这是最简单的“方格”。&lt;/p>
&lt;p>然而，格并不总是正交的。例如，考虑基向量 $\mathbf{b}_1 = \begin{pmatrix} 2 \\ 1 \end{pmatrix}$ 和 $\mathbf{b}_2 = \begin{pmatrix} 1 \\ 3 \end{pmatrix}$，它们生成的点就像是倾斜扭曲的网格的交点。&lt;/p>
&lt;h2 id="23-基的非唯一性与幺模变换">2.3 基的非唯一性与幺模变换
&lt;/h2>&lt;p>有一个涉及格密码安全性根基的重要性质。那就是“生成同一个格的基有无数个”。&lt;/p>
&lt;p>例如，刚才由 $\mathbf{b}_1 = (1, 0)^T, \mathbf{b}_2 = (0, 1)^T$ 生成的 $\mathbb{Z}^2$ 格，即使使用 $\mathbf{b}'_1 = (1, 1)^T, \mathbf{b}'_2 = (2, 3)^T$ 这组基，也会生成完全相同的格 $\mathbb{Z}^2$。&lt;/p>
&lt;p>某个基 $B$ 和另一个基 $B'$ 生成相同格的充要条件是，存在一个整数元素构成的矩阵 $U \in \mathbb{Z}^{n \times n}$，其行列式为 $\det(U) = \pm 1$，并且可以表示为：
&lt;/p>
$$ B' = B U $$
&lt;p>
这样的矩阵 $U$ 被称为“幺模矩阵（Unimodular matrix）”。&lt;/p>
&lt;p>在密码学应用中，基本思路是使用“好基（接近正交且由短向量组成的基）”作为私钥，而使用“坏基（相互极度倾斜且由非常长的向量组成的基）”作为公钥。当维度较高时，从坏基计算出好基将变得非常困难。这就是格密码的基本直觉。&lt;/p>
&lt;h1 id="3-格上的计算困难问题">3. 格上的计算困难问题
&lt;/h1>&lt;p>格密码的安全性依赖于求解格上特定数学问题的困难性。这里介绍最基本也是最著名的两个问题。&lt;/p>
&lt;h2 id="31-最短向量问题shortest-vector-problem-svp">3.1 最短向量问题（Shortest Vector Problem: SVP）
&lt;/h2>&lt;p>SVP是格理论中最经典且最著名的问题。&lt;/p>
&lt;p>&lt;strong>定义（SVP）:&lt;/strong>
给定任意一个格的基 $B$，找到该格 $\mathcal{L}(B)$ 中除零向量以外欧几里得范数（长度）最小的向量 $\mathbf{v}$。&lt;/p>
&lt;p>用数学公式表示，即寻找使 $\min_{\mathbf{v} \in \mathcal{L}(B) \setminus \{\mathbf{0}\}} \| \mathbf{v} \|$ 成立的 $\mathbf{v}$。这个最小长度记作 $\lambda_1(\mathcal{L})$，被称为“格的第一连续极小值（First successive minimum）”。&lt;/p>
&lt;p>在二维或三维的低维度下，我们可以通过画图用肉眼找到最短向量。或者可以使用高斯格基约化算法等高效地求解。但是，当维度 $n$ 达到数百至数千这样的高维时，已知严格求解SVP是NP困难的。&lt;/p>
&lt;p>在实际密码中，通常不寻找严格的最短向量，而是寻找“近似短的向量”，即近似SVP（$\gamma$-SVP）。当近似系数 $\gamma$ 为多项式大小时，这个问题仍然被认为是非常困难的。&lt;/p>
&lt;h2 id="32-最近向量问题closest-vector-problem-cvp">3.2 最近向量问题（Closest Vector Problem: CVP）
&lt;/h2>&lt;p>CVP在格密码中也是极其重要的问题。&lt;/p>
&lt;p>&lt;strong>定义（CVP）:&lt;/strong>
给定任意格基 $B$ 和空间内任意目标向量 $\mathbf{t} \in \mathbb{R}^m$（不一定是格点），在格点中找到距离 $\mathbf{t}$ 最近的格点 $\mathbf{v} \in \mathcal{L}(B)$。&lt;/p>
&lt;p>用数学公式表示，即寻找使 $\min_{\mathbf{v} \in \mathcal{L}(B)} \| \mathbf{v} - \mathbf{t} \|$ 成立的格点 $\mathbf{v}$。&lt;/p>
&lt;p>与SVP一样，CVP在高维空间中也是NP困难的。从密码学应用的角度来看，后文将提到的LWE问题与CVP的特殊变种（Bounded Distance Decoding: BDD）有着密切的关系。&lt;/p>
&lt;h2 id="33-为什么高维情况下无法求解lll与bkz的极限">3.3 为什么高维情况下无法求解？（LLL与BKZ的极限）
&lt;/h2>&lt;p>作为求解高维格问题的著名算法，有LLL算法（Lenstra-Lenstra-Lovász algorithm）。LLL算法能在多项式时间内运行，可以将格基约化（Reduction）到某种程度上的“好基”。然而，LLL算法所能找到的最短向量，其长度与真实最短向量相比，具有指数级（$2^{\mathcal{O}(n)}$）的近似系数，因此不足以破解密码的安全性。&lt;/p>
&lt;p>如果使用基于LLL改进的更强力的基约化算法（如BKZ，Block Korkine-Zolotarev算法），可以找到更短的向量，但其计算量会随着块大小呈指数级增长。在格密码中，就是通过估算这种BKZ算法的运行时间来决定安全参数（例如维度 $n$ 的大小）。在目前的PQC标准参数中，维度 $n$ 通常选择在500到1000以上的值，据称即使使用超级计算机或未来的量子计算机，破解所需的时间也将超过宇宙的年龄。&lt;/p>
&lt;h1 id="4-lwe问题learning-with-errors的数学公式化">4. LWE问题（Learning With Errors）的数学公式化
&lt;/h1>&lt;p>大部分现代格密码都基于Oded Regev在2005年提出的“LWE问题（Learning With Errors）”。LWE问题的美妙之处在于其公式化的简洁性，以及它拥有“从最坏情况复杂度到平均情况复杂度的归约”这一强大的数学证明。&lt;/p>
&lt;h2 id="41-无噪声的线性方程组">4.1 无噪声的线性方程组
&lt;/h2>&lt;p>为了理解LWE问题，我们先来考虑一个没有噪声的简单线性方程组。
假设有一个未知的秘密向量 $\mathbf{s} \in \mathbb{Z}_q^n$（各个分量是 $0$ 到 $q-1$ 的整数）。这里 $q$ 是一个素数。&lt;/p>
&lt;p>选择随机的系数向量 $\mathbf{a}_1, \mathbf{a}_2, \dots \in \mathbb{Z}_q^n$，并计算它们与秘密向量 $\mathbf{s}$ 的内积对 $q$ 取模的结果：
$b_1 = \langle \mathbf{a}_1, \mathbf{s} \rangle \pmod q$
$b_2 = \langle \mathbf{a}_2, \mathbf{s} \rangle \pmod q$
$\vdots$&lt;/p>
&lt;p>如果给出足够数量（$n$个以上）的 $(\mathbf{a}_i, b_i)$ 数对，我们就可以使用线性代数中的“高斯消元法（Gaussian elimination）”轻松恢复出秘密向量 $\mathbf{s}$。这是一个可以在多项式时间内轻松求解的问题。&lt;/p>
&lt;h2 id="42-lwe问题的定义加入噪声">4.2 LWE问题的定义：加入噪声
&lt;/h2>&lt;p>那么，如果在该问题中加入微小的“噪声（误差）”会怎样呢？
这就是LWE问题的本质。&lt;/p>
&lt;p>对于未知的秘密向量 $\mathbf{s} \in \mathbb{Z}_q^n$，我们在每个方程的结果中加入一个小的误差 $e_i \in \mathbb{Z}_q$。
$b_i = \langle \mathbf{a}_i, \mathbf{s} \rangle + e_i \pmod q$&lt;/p>
&lt;p>在这里，$e_i$ 是均值为0，标准差相对较小（例如从类似正态分布的离散高斯分布中选取）的小整数值。
我们所得到的信息是随机向量 $\mathbf{a}_i$ 与加上误差计算得出的 $b_i$ 组成的数对列表：
$( \mathbf{a}_1, b_1 ), ( \mathbf{a}_2, b_2 ), \dots, ( \mathbf{a}_m, b_m )$&lt;/p>
&lt;p>用矩阵表示这个过程会非常简洁。
使用随机矩阵 $A \in \mathbb{Z}_q^{m \times n}$、秘密向量 $\mathbf{s} \in \mathbb{Z}_q^n$、误差向量 $\mathbf{e} \in \mathbb{Z}_q^m$，可以写成：
&lt;/p>
$$ \mathbf{b} = A \mathbf{s} + \mathbf{e} \pmod q $$
&lt;p>
提供给我们的只有 $A$ 和 $\mathbf{b}$。从中求解 $\mathbf{s}$ 的问题就是“搜索LWE问题（Search LWE problem）”。&lt;/p>
&lt;p>由于混入了误差 $e_i$，如果尝试使用高斯消元法，在方程加减的过程中误差会呈指数级放大，从而无法得到正确的答案。乍一看只是简单的线性方程组，但仅仅加入了这种微小的噪声，问题的难度就骤升至NP困难级别。&lt;/p>
&lt;h2 id="43-判定lwe问题decision-lwe">4.3 判定LWE问题（Decision LWE）
&lt;/h2>&lt;p>在密码学理论的证明中，频繁使用的是搜索LWE问题的一个变种——“判定LWE问题（Decision LWE problem）”。&lt;/p>
&lt;p>判定LWE问题是指，给定从以下两个分布中获取的样本列表，判定它们来自哪一个分布的问题：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>LWE分布&lt;/strong>: 刻意计算出的 $(A, \mathbf{b} = A\mathbf{s} + \mathbf{e} \pmod q)$&lt;/li>
&lt;li>&lt;strong>均匀随机分布&lt;/strong>: 由完全随机选择的矩阵 $A$ 和向量 $\mathbf{u}$ 构成的 $(A, \mathbf{u})$&lt;/li>
&lt;/ol>
&lt;p>令人惊讶的是，如果恰当地选择LWE问题的参数，从LWE分布中获得的样本数对，与完全随机的数据对之间将变得“计算上不可区分（Computationally Indistinguishable）”。这个性质成为了基于LWE的密码能够生成“与随机数无法区分的密文”的理论依据。&lt;/p>
&lt;h2 id="44-从最坏情况复杂度到平均情况复杂度的归约regev定理">4.4 从最坏情况复杂度到平均情况复杂度的归约（Regev定理）
&lt;/h2>&lt;p>Oded Regev最大的贡献，就是通过数学证明将LWE问题的难度与前文所述格问题（SVP和CVP）的难度联系在了一起。&lt;/p>
&lt;p>他使用量子归约（Quantum reduction）证明了：“如果存在能在平均情况下（对于随机选择的 $A$ 和 $\mathbf{e}$）在多项式时间内求解LWE问题的算法，那么就存在能在多项式时间内求解任意格的最坏情况（最困难的情况）Gap-SVP的量子算法”。（后来Peikert等人也证明了经典归约）。&lt;/p>
&lt;p>这在密码学理论中是一个梦幻般的性质。因为它消除了“密码被破解，可能只是因为我们恰好选择了一个弱密钥（平均情况的一部分）”的担忧，并提供了强有力的保证：“如果平均的LWE能被解决，那么格上所有最困难的问题都能被解决（所以LWE绝对是困难的）”。&lt;/p>
&lt;div class="mermaid">graph TD
A["最坏情况格问题 (Gap-SVP, SIVP)"] -->|量子/经典归约| B["平均情况LWE问题"]
B -->|密码学构造| C["基于LWE的密码系统 (PKE, KEM, FHE)"]
style A fill:#ffcccc,stroke:#ff0000,stroke-width:2px,color:#000
style B fill:#ccffcc,stroke:#00aa00,stroke-width:2px,color:#000
style C fill:#ccccff,stroke:#0000ff,stroke-width:2px,color:#000&lt;/div>
&lt;h1 id="5-使用lwe构建公钥密码方案regev密码">5. 使用LWE构建公钥密码方案（Regev密码）
&lt;/h1>&lt;p>理解了LWE问题的困难性之后，我们来看看Oded Regev提出的基本公钥密码方案，了解如何利用它进行加解密。这里我们将讲解加密1比特消息 $M \in \{0, 1\}$ 的最基本机制。&lt;/p>
&lt;h2 id="51-密钥生成key-generation">5.1 密钥生成（Key Generation）
&lt;/h2>&lt;ol>
&lt;li>作为系统参数，确定作为模数的素数 $q$、维度 $n$ 以及方程数量 $m$（$m > n \log q$）。&lt;/li>
&lt;li>作为私钥，随机选择向量 $\mathbf{s} \in \mathbb{Z}_q^n$。&lt;/li>
&lt;li>生成随机矩阵 $A \in \mathbb{Z}_q^{m \times n}$。&lt;/li>
&lt;li>从离散高斯分布等误差分布中选择一个小的误差向量 $\mathbf{e} \in \mathbb{Z}_q^m$。&lt;/li>
&lt;li>计算向量 $\mathbf{b} = A \mathbf{s} + \mathbf{e} \pmod q$。&lt;/li>
&lt;li>公钥（Public Key）为 $(A, \mathbf{b})$。&lt;/li>
&lt;li>私钥（Secret Key）为 $\mathbf{s}$。&lt;/li>
&lt;/ol>
&lt;p>公钥本身正是“LWE问题的一个实例”。由于从公钥 $(A, \mathbf{b})$ 求出私钥 $\mathbf{s}$ 等同于求解搜索LWE问题，因此其安全性得到了保障。&lt;/p>
&lt;h2 id="52-加密encryption">5.2 加密（Encryption）
&lt;/h2>&lt;p>爱丽丝使用鲍勃的公钥 $(A, \mathbf{b})$，对1比特的消息 $M \in \{0, 1\}$ 进行加密。&lt;/p>
&lt;ol>
&lt;li>选择一个随机的二进制向量（分量为0或1）$\mathbf{r} \in \{0, 1\}^m$。&lt;/li>
&lt;li>作为密文的前半部分，计算向量 $\mathbf{u} = A^T \mathbf{r} \pmod q$。（$A^T$ 是 $A$ 的转置矩阵。也就是说，将 $A$ 中对应 $\mathbf{r}$ 分量为1的行进行相加）。&lt;/li>
&lt;li>作为密文的后半部分，计算标量 $v = \mathbf{b}^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor \pmod q$。
（如果消息 $M$ 为0，则什么都不加；如果为 $1$，则加上正好是 $q$ 一半的值 $\lfloor \frac{q}{2} \rfloor$）。&lt;/li>
&lt;li>密文（Ciphertext）即为 $(\mathbf{u}, v)$。&lt;/li>
&lt;/ol>
&lt;p>加密直观的含义是，对于公钥矩阵 $A$ 和向量 $\mathbf{b}$，取其“随机子集的和”。由于判定LWE问题的困难性，该密文 $(\mathbf{u}, v)$ 看起来与完全随机的向量和均匀随机数不可区分（语义安全性：Semantic Security）。&lt;/p>
&lt;div class="mermaid">flowchart LR
M["消息 M 属于 {0,1}"] --> Enc
PK["公钥 (A, b)"] --> Enc
r["随机二进制向量 r"] --> Enc
subgraph Enc ["加密过程"]
direction TB
u_calc["u = A^T * r mod q"]
v_calc["v = b^T * r + M * floor(q/2) mod q"]
end
Enc --> CT["密文 (u, v)"]&lt;/div>
&lt;h2 id="53-解密decryption">5.3 解密（Decryption）
&lt;/h2>&lt;p>鲍勃使用私钥 $\mathbf{s}$ 对密文 $(\mathbf{u}, v)$ 进行解密。&lt;/p>
&lt;ol>
&lt;li>计算以下值： $D = v - \mathbf{s}^T \mathbf{u} \pmod q$&lt;/li>
&lt;li>如果计算结果接近 $0$，则输出 $M=0$；如果接近 $\lfloor \frac{q}{2} \rfloor$，则输出 $M=1$。&lt;/li>
&lt;/ol>
&lt;p>为什么这样就能解密呢？让我们从数学上展开来看。
回想一下 $\mathbf{b} = A \mathbf{s} + \mathbf{e}$。&lt;/p>
$$
\begin{aligned}
v - \mathbf{s}^T \mathbf{u} &amp;= (\mathbf{b}^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor) - \mathbf{s}^T (A^T \mathbf{r}) \\
&amp;= ((A \mathbf{s} + \mathbf{e})^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor) - \mathbf{s}^T A^T \mathbf{r} \\
&amp;= (\mathbf{s}^T A^T \mathbf{r} + \mathbf{e}^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor) - \mathbf{s}^T A^T \mathbf{r} \\
&amp;= \mathbf{e}^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor \pmod q
\end{aligned}
$$
&lt;p>在这里，公式中的 $\mathbf{s}^T A^T \mathbf{r}$ 被完美地抵消消失了！
剩下的只有 $\mathbf{e}^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor$。&lt;/p>
&lt;p>$\mathbf{e}$ 是分量极小的噪声向量，而 $\mathbf{r}$ 是分量为0或1的二进制向量。因此，它们的内积 $\mathbf{e}^T \mathbf{r}$（如果参数选择得当）也会保持在一个相对较小的值。&lt;/p>
&lt;ul>
&lt;li>如果 $M=0$，结果变为 $\mathbf{e}^T \mathbf{r}$，这是一个接近 $0$ 的小值。&lt;/li>
&lt;li>如果 $M=1$，结果变为 $\mathbf{e}^T \mathbf{r} + \lfloor \frac{q}{2} \rfloor$，它将位于 $q$ 的一半值 $\lfloor \frac{q}{2} \rfloor$ 的附近。&lt;/li>
&lt;/ul>
&lt;p>如果参数设计使得误差 $\mathbf{e}^T \mathbf{r}$ 的绝对值能够控制在 $\frac{q}{4}$ 以内，鲍勃只需观察计算结果是更接近 $0$ 还是 $\lfloor \frac{q}{2} \rfloor$，就能准确地判定（恢复）消息 $M$。这就是基于LWE的密码能够正常运作的精妙机制。&lt;/p>
&lt;div class="mermaid">flowchart LR
CT["密文 (u, v)"] --> Dec
SK["私钥 s"] --> Dec
subgraph Dec ["解密过程"]
direction TB
calc["计算 D = v - s^T * u mod q"]
check["检查 D 是更接近 0 还是 q/2"]
end
calc --> check
Dec --> M_out["恢复出的消息 M"]&lt;/div>
&lt;h1 id="6-使用具体数值的lwe密码玩具示例">6. 使用具体数值的LWE密码玩具示例
&lt;/h1>&lt;p>仅仅罗列公式可能很难有切实的感受，所以让我们实际设定非常小的数值参数，来追踪一下从加密到解密的计算过程。
（※在现实的密码系统中，为了确保安全性，通常会使用 $n$ 为500以上、$q$ 为数千以上的值）&lt;/p>
&lt;p>&lt;strong>【参数设定】&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>模数 $q = 17$ （素数。因此取值范围在 $0$ 到 $16$ 之间）&lt;/li>
&lt;li>维度 $n = 2$&lt;/li>
&lt;li>方程数量 $m = 4$&lt;/li>
&lt;li>假设要加密消息 $M = 1$。&lt;/li>
&lt;li>消息的偏移量：$\lfloor \frac{q}{2} \rfloor = \lfloor \frac{17}{2} \rfloor = 8$&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>【1. 密钥生成阶段】&lt;/strong>
鲍勃随机选择私钥 $\mathbf{s}$、矩阵 $A$ 以及误差向量 $\mathbf{e}$。
&lt;/p>
$$ \mathbf{s} = \begin{pmatrix} 3 \\ 4 \end{pmatrix} \in \mathbb{Z}_{17}^2 $$
$$ A = \begin{pmatrix} 2 &amp; 15 \\ 1 &amp; 8 \\ 14 &amp; 5 \\ 9 &amp; 10 \end{pmatrix} \in \mathbb{Z}_{17}^{4 \times 2} $$
$$ \mathbf{e} = \begin{pmatrix} 1 \\ -1 \\ 0 \\ 2 \end{pmatrix} \equiv \begin{pmatrix} 1 \\ 16 \\ 0 \\ 2 \end{pmatrix} \pmod{17} $$
&lt;p>接着计算公钥 $\mathbf{b}$。
&lt;/p>
$$ A \mathbf{s} = \begin{pmatrix} 2 &amp; 15 \\ 1 &amp; 8 \\ 14 &amp; 5 \\ 9 &amp; 10 \end{pmatrix} \begin{pmatrix} 3 \\ 4 \end{pmatrix} = \begin{pmatrix} 2\times 3 + 15\times 4 \\ 1\times 3 + 8\times 4 \\ 14\times 3 + 5\times 4 \\ 9\times 3 + 10\times 4 \end{pmatrix} = \begin{pmatrix} 6 + 60 \\ 3 + 32 \\ 42 + 20 \\ 27 + 40 \end{pmatrix} = \begin{pmatrix} 66 \\ 35 \\ 62 \\ 67 \end{pmatrix} $$
&lt;p>
将其对 17 取模。($66 = 17 \times 3 + 15$ 等)
&lt;/p>
$$ A \mathbf{s} \pmod{17} = \begin{pmatrix} 15 \\ 1 \\ 11 \\ 16 \end{pmatrix} $$
&lt;p>
加上误差向量 $\mathbf{e}$。
&lt;/p>
$$ \mathbf{b} = A \mathbf{s} + \mathbf{e} = \begin{pmatrix} 15 \\ 1 \\ 11 \\ 16 \end{pmatrix} + \begin{pmatrix} 1 \\ 16 \\ 0 \\ 2 \end{pmatrix} = \begin{pmatrix} 16 \\ 17 \\ 11 \\ 18 \end{pmatrix} \equiv \begin{pmatrix} 16 \\ 0 \\ 11 \\ 1 \end{pmatrix} \pmod{17} $$
&lt;p>公钥为 $A$ 和 $\mathbf{b} = (16, 0, 11, 1)^T$。&lt;/p>
&lt;p>&lt;strong>【2. 加密阶段】&lt;/strong>
爱丽丝加密消息 $M = 1$。
选择随机向量 $\mathbf{r}$。这里设 $\mathbf{r} = (1, 0, 1, 0)^T$。&lt;/p>
&lt;p>计算 $\mathbf{u}$。
&lt;/p>
$$ \mathbf{u} = A^T \mathbf{r} = \begin{pmatrix} 2 &amp; 1 &amp; 14 &amp; 9 \\ 15 &amp; 8 &amp; 5 &amp; 10 \end{pmatrix} \begin{pmatrix} 1 \\ 0 \\ 1 \\ 0 \end{pmatrix} = \begin{pmatrix} 2 \times 1 + 14 \times 1 \\ 15 \times 1 + 5 \times 1 \end{pmatrix} = \begin{pmatrix} 16 \\ 20 \end{pmatrix} \equiv \begin{pmatrix} 16 \\ 3 \end{pmatrix} \pmod{17} $$
&lt;p>计算 $v$。
&lt;/p>
$$ \mathbf{b}^T \mathbf{r} = (16, 0, 11, 1) \begin{pmatrix} 1 \\ 0 \\ 1 \\ 0 \end{pmatrix} = 16 \times 1 + 11 \times 1 = 27 \equiv 10 \pmod{17} $$
&lt;p>
加上对应消息 $M=1$ 的值 $\lfloor 17/2 \rfloor = 8$。
&lt;/p>
$$ v = \mathbf{b}^T \mathbf{r} + M \cdot 8 = 10 + 1 \times 8 = 18 \equiv 1 \pmod{17} $$
&lt;p>爱丽丝将密文 $(\mathbf{u}, v) = \left( \begin{pmatrix} 16 \\ 3 \end{pmatrix}, 1 \right)$ 发送给鲍勃。&lt;/p>
&lt;p>&lt;strong>【3. 解密阶段】&lt;/strong>
鲍勃收到密文后，使用私钥 $\mathbf{s} = (3, 4)^T$ 进行解密。
解密处理公式：计算 $D = v - \mathbf{s}^T \mathbf{u} \pmod{17}$。&lt;/p>
$$ \mathbf{s}^T \mathbf{u} = (3, 4) \begin{pmatrix} 16 \\ 3 \end{pmatrix} = 3 \times 16 + 4 \times 3 = 48 + 12 = 60 \equiv 9 \pmod{17} $$
$$ D = v - \mathbf{s}^T \mathbf{u} = 1 - 9 = -8 \pmod{17} $$
&lt;p>在这里，在模17的世界里，$-8$ 等于 $9$（$-8 + 17 = 9$）。
判定得到的值 $D = 9$ 是更接近 $0$ 还是 $8$（$\lfloor 17/2 \rfloor$）。
由于 $9$ 显然比 $0$ 更接近 $8$，所以鲍勃成功地正确恢复出了 $M = 1$！&lt;/p>
&lt;p>为什么会变成 $9$ 呢？回想一下刚才的证明。
误差部分为 $\mathbf{e}^T \mathbf{r} = (1, -1, 0, 2) (1, 0, 1, 0)^T = 1 \times 1 + 0 \times 1 = 1$。
因此，计算结果变为 $\mathbf{e}^T \mathbf{r} + M \cdot 8 = 1 + 8 = 9$，我们确认了计算得出了与理论一致的值。&lt;/p>
&lt;h1 id="7-走向实用化的演进ring-lwe-与-module-lwe">7. 走向实用化的演进：Ring-LWE 与 Module-LWE
&lt;/h1>&lt;p>到目前为止我们所解释的标准LWE问题（Standard LWE）拥有极其强大的安全性证明，但在实际应用中却有一个致命的弱点。那就是“密钥尺寸巨大”和“计算成本高昂”。&lt;/p>
&lt;p>在Standard LWE中，公钥包含一个巨大的矩阵 $A \in \mathbb{Z}_q^{m \times n}$。当参数 $n$ 达到数百至数千时，这个矩阵的尺寸会达到几兆字节，每次在互联网通信协议（如TLS）中传输都会显得过于沉重。另外，矩阵与向量的乘法需要 $\mathcal{O}(n^2)$ 的计算量。&lt;/p>
&lt;p>为了解决这个问题，人们引入了将多项式环（Polynomial rings）这种代数结构融入格中的“Ring-LWE（RLWE）”和“Module-LWE（MLWE）”。&lt;/p>
&lt;h2 id="71-ring-lwe-的直觉">7.1 Ring-LWE 的直觉
&lt;/h2>&lt;p>在Ring-LWE中，将向量和矩阵替换为多项式环 $\mathcal{R}_q = \mathbb{Z}_q[X]/(X^n + 1)$ 上的元素（多项式）。（这里的 $n$ 通常选择为2的幂）。&lt;/p>
&lt;p>Standard LWE的公钥是一个矩阵 $A$，而在Ring-LWE中则使用单一的多项式 $a(x)$。私钥 $s(x)$ 和误差 $e(x)$ 也变成了多项式。
方程如下所示：
&lt;/p>
$$ b(x) = a(x) \cdot s(x) + e(x) \pmod q $$
&lt;p>因为这是多项式的乘法，所以通过使用类似于快速傅里叶变换（FFT）的“数论变换（Number Theoretic Transform: NTT）”，可以将计算量急剧削减到 $\mathcal{O}(n \log n)$。此外，由于公钥的尺寸也从矩阵缩小到了单一的多项式，数据尺寸减少到了 $\mathcal{O}(n)$。这在通信带宽上带来了压倒性的优势。&lt;/p>
&lt;p>从数学上看，Ring-LWE并不是一般的格，而是归结为被称为“理想格（Ideal Lattice）”的具有特殊对称性的格上的问题。&lt;/p>
&lt;h2 id="72-module-lwe-与-nist-的标准化kyber--ml-kem">7.2 Module-LWE 与 NIST 的标准化（Kyber / ML-KEM）
&lt;/h2>&lt;p>Ring-LWE虽然高效，但也存在着一些担忧，即理想格特殊的代数结构未来可能会成为被攻击的突破口。因此，结合了Standard LWE的保守安全性与Ring-LWE的高效性，做到了“两全其美”的，便是“Module-LWE（MLWE）”。&lt;/p>
&lt;p>在Module-LWE中，考虑的是以多项式为元素的小型矩阵和向量。也就是说，处理的是环上的模（Module）。
目前，NIST选定作为PQC密钥封装机制（KEM）标准的“CRYSTALS-Kyber”（标准化名称：ML-KEM），正是基于这个Module-LWE问题的困难性所构建的。&lt;/p>
&lt;h1 id="8-为什么对量子计算机也是安全的">8. 为什么对量子计算机也是安全的？
&lt;/h1>&lt;p>最后，我们来探讨一下核心问题：“为什么人们认为格密码即使使用量子计算机也无法被破解？”。&lt;/p>
&lt;p>量子计算机能够破解RSA密码和椭圆曲线密码的Shor算法，其本质是一个求解“隐藏子群问题（Hidden Subgroup Problem: HSP）”的算法。RSA和ECC背后的数学结构（有限阿贝尔群）具有周期性，通过使用被称为量子傅里叶变换（QFT）的量子算法特有操作，可以一口气提取出这个周期（隐藏的子群）。&lt;/p>
&lt;p>然而，格问题有着根本的不同。尽管格也具有周期性，但在SVP和CVP中需要求解的是“最短距离”或“去除噪声”这种几何上的非线性性质。即使直接套用像Shor算法中那样的“阿贝尔群上的量子傅里叶变换”，也无法高效地提取出成为格问题解答的有用信息。迄今为止，尚未发现能够在多项式时间内求解SVP或LWE的量子算法，人们普遍相信，即便拥有量子计算机的并行计算能力，也只有近乎穷举的搜索（如利用Grover算法带来的平方根加速程度）才是有效的手段。&lt;/p>
&lt;h1 id="9-总结">9. 总结
&lt;/h1>&lt;p>本文从格密码的数学直觉出发，详细讲解了从格的几何学定义，到LWE问题的公式化，再到构建公钥密码的全过程。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>格（Lattice）&lt;/strong> 是由基向量的整数系数线性组合表示的离散空间，在高维空间中，寻找接近正交的“好基”（SVP）是非常困难的。&lt;/li>
&lt;li>&lt;strong>LWE问题（Learning With Errors）&lt;/strong> 是求解带噪声的线性方程组的问题，由于它被归约到格的最坏情况问题的困难性，从而提供了强大的安全性依据。&lt;/li>
&lt;li>利用LWE问题，通过刻意添加或消除噪声的巧妙机制，实现了加解密（&lt;strong>Regev密码&lt;/strong>）。&lt;/li>
&lt;li>在现实的协议中，为了提高通信效率和计算速度，采用了使用多项式环的 &lt;strong>Ring-LWE&lt;/strong> 和 &lt;strong>Module-LWE&lt;/strong>，这成为了NIST标准 &lt;strong>ML-KEM&lt;/strong> 的基础。&lt;/li>
&lt;/ol>
&lt;p>在量子计算机这一前所未有的计算范式转变日益逼近的背景下，脱胎于经典线性代数和数论深渊的“格密码”，将承担起未来互联网安全基石的重任，这着实是一个充满浪漫色彩的故事。作为格密码基础的数学绝非高不可攀，只要具备线性代数和概率的基础知识，就足以理解其美妙的结构。希望本文能为您理解作为PQC核心的格密码提供一些帮助。&lt;/p></description></item><item><title>黎曼猜想与素数分布：与现代密码学的深刻联系</title><link>http://kenji.blog/zh-cn/p/riemann-hypothesis-prime-distribution-cryptography/</link><pubDate>Fri, 11 Sep 2026 16:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/p/riemann-hypothesis-prime-distribution-cryptography/</guid><description>&lt;img src="http://kenji.blog/p/riemann-hypothesis-prime-distribution-cryptography/img/eyecatch.jpg" alt="Featured image of post 黎曼猜想与素数分布：与现代密码学的深刻联系" />&lt;h1 id="1-引言素数这一宇宙奥秘与黎曼猜想">1. 引言：素数这一宇宙奥秘与黎曼猜想
&lt;/h1>&lt;p>“素数（Prime Numbers）”是只能被1和自身整除的自然数，在数学世界中被称为“原子”。2, 3, 5, 7, 11, 13&amp;hellip; 这串数列乍看之下显得杂乱无章、随机出现。自从古希腊数学家欧几里得证明“素数有无限多个”以来，无数数学家试图解开隐藏在素数排列中的规律。&lt;/p>
&lt;p>在探索素数之谜的道路上，最接近真相的是1859年由德国数学家波恩哈德·黎曼（Bernhard Riemann）提出的**“黎曼猜想（Riemann Hypothesis）”**。黎曼猜想是现代数学中最重要且未解决的难题之一，也是克雷数学研究所设立的千禧年大奖难题之一，悬赏100万美元。&lt;/p>
&lt;p>乍看之下，关于素数分布的纯数学难题似乎与我们的日常生活毫不相干。然而，支撑现代社会基础设施的互联网安全，尤其是&lt;strong>RSA加密和椭圆曲线密码学（ECC）等现代密码技术&lt;/strong>，都深深依赖于巨大素数的性质。&lt;/p>
&lt;p>本文将带领读者进行一次数学之旅，从素数分布、素数定理、黎曼zeta函数，一直深入到黎曼猜想的核心，并极其详尽地探讨它如何与现代密码技术产生联系，以及如果黎曼猜想被证明，世界将会发生怎样的改变。&lt;/p>
&lt;hr>
&lt;h1 id="2-素数定理与素数分布高斯的发现">2. 素数定理与素数分布：高斯的发现
&lt;/h1>&lt;p>为了理解素数是如何分布的，数学家们引入了&lt;strong>素数计数函数（Prime-counting function）&lt;/strong> $\pi(x)$，它表示“小于或等于某个数 $x$ 的素数有多少个”。&lt;/p>
&lt;p>例如：&lt;/p>
&lt;ul>
&lt;li>$\pi(10) = 4$ （2, 3, 5, 7）&lt;/li>
&lt;li>$\pi(100) = 25$&lt;/li>
&lt;li>$\pi(1000) = 168$&lt;/li>
&lt;/ul>
&lt;p>15岁的天才数学家卡尔·弗里德里希·高斯（Carl Friedrich Gauss）计算了庞大的素数表，并发现素数出现的频率与自然对数 $\ln x$ 成反比而递减。也就是说，他推测在一个数 $x$ 附近找到素数的概率大约是 $\frac{1}{\ln x}$。&lt;/p>
&lt;p>用积分来表达这个推测，就得到了&lt;strong>对数积分（Logarithmic integral）&lt;/strong> $\text{Li}(x)$：&lt;/p>
$$ \text{Li}(x) = \int_{2}^{x} \frac{dt}{\ln t} $$
&lt;p>高斯的猜想后来在1896年由雅克·阿达马和夏尔-让·德拉瓦莱·普桑分别独立证明，确立为&lt;strong>素数定理（Prime Number Theorem, PNT）&lt;/strong>。&lt;/p>
$$ \lim_{x \to \infty} \frac{\pi(x)}{\text{Li}(x)} = 1 $$
&lt;p>或者近似地表示为：&lt;/p>
$$ \pi(x) \sim \frac{x}{\ln x} $$
&lt;p>通过这个定理可知，从宏观来看，素数具有非常平滑且可预测的分布。然而，从微观来看，$\pi(x)$ 和 $\text{Li}(x)$ 之间始终存在着“误差”或者说“波动”。这种波动的本质，正是黎曼猜想试图解开的最大谜团。&lt;/p>
&lt;hr>
&lt;h1 id="3-黎曼zeta函数与欧拉乘积">3. 黎曼zeta函数与欧拉乘积
&lt;/h1>&lt;p>分析素数分布最强大的武器是&lt;strong>黎曼zeta函数（Riemann Zeta Function）&lt;/strong>。它最初是由莱昂哈德·欧拉（Leonhard Euler）针对实数 $s > 1$ 定义的无穷级数。&lt;/p>
$$ \zeta(s) = \sum_{n=1}^\infty \frac{1}{n^s} = 1 + \frac{1}{2^s} + \frac{1}{3^s} + \frac{1}{4^s} + \dots $$
&lt;p>欧拉最大的功绩之一，是证明了这个无穷级数可以表示为关于所有素数 $p$ 的无穷乘积。这就是&lt;strong>欧拉乘积（Euler Product Formula）&lt;/strong>。&lt;/p>
$$ \zeta(s) = \prod_{p \text{ prime}} \frac{1}{1 - p^{-s}} = \left( \frac{1}{1 - 2^{-s}} \right) \left( \frac{1}{1 - 3^{-s}} \right) \left( \frac{1}{1 - 5^{-s}} \right) \dots $$
&lt;p>对于这个证明的直观理解是：将右边各项作为等比级数展开，然后将它们相乘，根据算术基本定理（所有自然数都可以唯一地表示为素数的乘积），左边自然数倒数和的形式会被完美地重构出来。&lt;/p>
&lt;p>&lt;strong>仅仅这一个数学公式，就搭建起了分析学（无穷级数、连续函数）与数论（素数、离散数）之间的桥梁。&lt;/strong> 研究zeta函数，就等同于研究素数的分布。&lt;/p>
&lt;hr>
&lt;h1 id="4-解析延拓与向复平面的扩展">4. 解析延拓与向复平面的扩展
&lt;/h1>&lt;p>黎曼的天才之处在于，他将欧拉原本只在实数范围内考虑的 $\zeta(s)$ 的变量 $s$，扩展到了&lt;strong>复数 $s = \sigma + it$（$\sigma$ 是实部，$t$ 是虚部）&lt;/strong>。&lt;/p>
&lt;p>最初的无穷级数只在 $\sigma > 1$ 时收敛，但黎曼使用了“解析延拓（Analytic Continuation）”的方法，扩展了 $\zeta(s)$ 的定义，使其在除了极点 $s = 1$ 之外的整个复平面上都有意义。&lt;/p>
&lt;p>他进一步推导出了zeta函数满足的优美函数方程（Functional equation）：&lt;/p>
$$ \zeta(s) = 2^s \pi^{s-1} \sin\left(\frac{\pi s}{2}\right) \Gamma(1-s) \zeta(1-s) $$
&lt;p>这里的 $\Gamma(x)$ 是伽玛函数。通过这个方程，可以从右半平面的性质得知左半平面的性质。&lt;/p>
&lt;h3 id="零点zeros-of-the-zeta-function">零点（Zeros of the Zeta Function）
&lt;/h3>&lt;p>使zeta函数值为0的复数 $s$ 被称为“零点”。
从函数方程可知，当 $s$ 是负偶数（$-2, -4, -6, \dots$）时，由于 $\sin(\pi s / 2)$ 变为0，因此 $\zeta(s) = 0$。这些被称为&lt;strong>平凡零点（Trivial zeros）&lt;/strong>。&lt;/p>
&lt;p>然而，在素数分布中真正重要的是其他零点，也就是存在于 $0 \le \sigma \le 1$ 的“临界带（Critical strip）”中的&lt;strong>非平凡零点（Non-trivial zeros）&lt;/strong>。&lt;/p>
&lt;hr>
&lt;h1 id="5-黎曼猜想的核心与显式公式">5. 黎曼猜想的核心与显式公式
&lt;/h1>&lt;p>黎曼计算了少数几个零点，并提出了一个惊人的猜想。这就是&lt;strong>黎曼猜想&lt;/strong>。&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>黎曼猜想 (Riemann Hypothesis)&lt;/strong>
黎曼zeta函数 $\zeta(s)$ 的所有非平凡零点，其实部都在 $1/2$ 的直线上（$\text{Re}(s) = 1/2$）。&lt;/p>
&lt;/blockquote>
&lt;p>这条实部为1/2的直线被称为“临界线（Critical line）”。&lt;/p>
&lt;div class="mermaid">graph TD
A["黎曼zeta函数 ζ(s)"] --> B["通过解析延拓扩展至复平面"]
B --> C["平凡零点 (s = -2, -4, -6 ...)"]
B --> D["非平凡零点 (0 &lt;= Re(s) &lt;= 1)"]
D --> E["黎曼猜想"]
E --> F["所有非平凡零点都在 Re(s) = 1/2 上"]
F --> G["通向素数分布误差项极限的证明"]&lt;/div>
&lt;p>为什么黎曼猜想如此重要？那是因为zeta函数的零点&lt;strong>完全&lt;/strong>决定了素数的分布。&lt;/p>
&lt;p>黎曼以及后来的数学家冯·曼戈尔特推导出了精确描述素数分布的“显式公式（Explicit formula）”。使用切比雪夫函数 $\psi(x)$ 可以表示为如下形式：&lt;/p>
$$ \psi(x) = x - \sum_{\rho} \frac{x^\rho}{\rho} - \ln(2\pi) - \frac{1}{2}\ln(1 - x^{-2}) $$
&lt;p>这里 $\rho$ 是对zeta函数的所有非平凡零点求和。
主项是 $x$ （这对应于素数定理），由此加上或减去取决于零点 $\rho$ 的波状项，就能还原出素数呈阶梯状的精确分布。可以说，非平凡零点代表了素数分布的“频率（波）”。&lt;/p>
&lt;p>如果黎曼猜想成立，即所有非平凡零点 $\rho$ 的实部恰好都是 $1/2$，那么理论上素数定理的误差项将被限制在可以想象的最小范围内：&lt;/p>
$$ |\pi(x) - \text{Li}(x)| \le \frac{1}{8\pi} \sqrt{x} \ln x \quad \text{for} \quad x \ge 2657 $$
&lt;p>也就是说，&lt;strong>如果黎曼猜想为真，就能证明素数以我们所能想象到的最“规则、美丽”的方式分布着。&lt;/strong>&lt;/p>
&lt;hr>
&lt;h1 id="6-现代密码技术与素数不可分割的关系">6. 现代密码技术与素数不可分割的关系
&lt;/h1>&lt;p>到此为止都是深奥的纯数学世界，但这素数的性质在根基上支撑着现代的数字社会。其代表就是以&lt;strong>RSA加密&lt;/strong>为首的公钥加密方式。&lt;/p>
&lt;p>互联网上的信用卡支付、密码传输、区块链电子签名等，所有通信的安全性都依赖于“素数”。&lt;/p>
&lt;h3 id="rsa加密的原理">RSA加密的原理
&lt;/h3>&lt;p>RSA加密的安全性基于一个数学事实（整数分解问题）：“对大位数的合数进行质因数分解非常困难”。&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>密钥生成&lt;/strong>:
随机选择两个巨大的素数 $p$ 和 $q$（例如各为2048位）。
将它们相乘计算出 $N = p \times q$。这个 $N$ 将成为公钥的一部分。
利用欧拉函数 $\phi(N) = (p-1)(q-1)$，生成私钥 $d$。&lt;/p>
$$ e \times d \equiv 1 \pmod{\phi(N)} $$
&lt;/li>
&lt;li>
&lt;p>&lt;strong>加密与解密&lt;/strong>:
明文 $M$ 会利用公钥 $e, N$ 转换为密文 $C$。
&lt;/p>
$$ C \equiv M^e \pmod{N} $$
&lt;p>
只有拥有私钥 $d$ 的人才能解密。
&lt;/p>
$$ M \equiv C^d \pmod{N} $$
&lt;/li>
&lt;/ol>
&lt;div class="mermaid">graph LR
A["明文 (Plaintext)"] --> B["用公钥(e, N)加密"]
B --> C["密文 (Ciphertext)"]
C --> D["用私钥(d)解密"]
D --> E["原始明文"]
F["攻击者 (Attacker)"] -- "尝试对N进行质因数分解" --> C
F -.-> G["如果不知道p和q，d就无法计算"]&lt;/div>
&lt;p>要破解RSA加密，就必须从巨大的 $N$ 中找出原来的素数 $p$ 和 $q$（进行质因数分解）。即使使用当前主流的算法（如普通数域筛选法：GNFS等），并动用超级计算机来分解几百位数的数字，据说也需要远远超过宇宙年龄的时间。&lt;/p>
&lt;hr>
&lt;h1 id="7-黎曼猜想对密码技术的影响">7. 黎曼猜想对密码技术的影响
&lt;/h1>&lt;p>那么，处于纯数学顶点的“黎曼猜想”与“密码技术”是如何交汇的呢？&lt;/p>
&lt;h3 id="71-素数生成算法素性测试与广义黎曼猜想grh">7.1. 素数生成算法（素性测试）与广义黎曼猜想（GRH）
&lt;/h3>&lt;p>为了运行RSA加密，首先需要生成巨大的素数 $p$ 和 $q$。然而，要可靠且高速地判断“某个数是否为素数”并非易事。&lt;/p>
&lt;p>目前在实际应用中使用的是一种被称为**米勒-拉宾素性测试（Miller-Rabin primality test）**的概率算法。这种算法速度很快，但存在以极低概率将合数误判为素数的“伪素数”风险。&lt;/p>
&lt;p>然而，如果假设将黎曼猜想扩展到狄利克雷L函数上的**“广义黎曼猜想（Generalized Riemann Hypothesis, GRH）”&lt;strong>为真，情况将发生戏剧性的变化。
如果GRH为真，那么在数学上就能保证米勒-拉宾测试的测试次数上限，使其从概率算法&lt;/strong>升华为“确定性多项式时间算法”**（这是在AKS素性测试被发现之前就已知晓的重大事实）。&lt;/p>
&lt;p>换言之，黎曼猜想（及其推广）在“能否带着绝对的自信、高速地生成巨大素数”这一密码基石的生成中，起到了直接背书的作用。&lt;/p>
&lt;h3 id="72-与质因数分解算法的关系">7.2. 与质因数分解算法的关系
&lt;/h3>&lt;p>在评估密码破解方算法（如普通数域筛选法等）的计算复杂度时，关于素数分布的知识也是不可或缺的。许多质因数分解算法都依赖于“光滑数（Smooth numbers：仅含有较小质因数的数字）”的分布。&lt;/p>
&lt;p>为了严格评估光滑数出现的频率，需要对素数分布有深刻的理解，这里也充分利用了直接联系zeta函数和黎曼猜想的解析数论技巧。如果黎曼猜想被证明，并且素数分布的误差被完全确定，那么将有可能更准确地看清质因数分解算法的性能极限。&lt;/p>
&lt;hr>
&lt;h1 id="8-如果黎曼猜想被证明密码会被破解吗">8. 如果黎曼猜想被证明，密码会被破解吗？
&lt;/h1>&lt;p>像都市传说一样，有人说“一旦黎曼猜想被解开，RSA加密就会瞬间崩溃”，&lt;strong>但这在数学上是不准确的。&lt;/strong>&lt;/p>
&lt;p>黎曼猜想的证明本身，并不会直接产生一种能让质因数分解戏剧性加速的魔法算法。因为黎曼猜想终究是关于素数“宏观分布规律性”的定理，并不能直接告诉我们个别的数字 $N$ 能被哪个素数整除（局部性质）。&lt;/p>
&lt;p>然而，影响并非为零。
因为在证明黎曼猜想的过程中，&lt;strong>极有可能发现“新的数学工具”或“未知的分析方法”&lt;/strong>。回顾历史，当费马大定理或庞加莱猜想被证明时，在这个过程中发展出的新理论让整个数学界实现了巨大的飞跃。&lt;/p>
&lt;p>如果确立了某种能够完全操控黎曼zeta函数零点性质的未知代数几何学方法或非交换几何学方法，那么这可能会导致发现具有突破性的质因数分解算法（例如将计算复杂度降低到多项式时间的经典算法），这种可能性是无法否认的。正因如此，密码学家们绝对无法将视线从黎曼猜想的动向上移开。&lt;/p>
&lt;h3 id="量子计算机与shor算法">量子计算机与Shor算法
&lt;/h3>&lt;p>对于密码技术而言，更直接、更现实的威胁不是黎曼猜想的证明，而是&lt;strong>量子计算机&lt;/strong>。彼得·秀尔（Peter Shor）在1994年发表的“Shor算法”证明，如果拥有性能足够强大的量子计算机，就能在多项式时间内解决质因数分解问题。这将导致RSA加密和椭圆曲线加密被从根本上破解。&lt;/p>
&lt;p>目前，全世界都在推进向即使是量子计算机也无法破解的“抗量子密码（Post-Quantum Cryptography, PQC）”（如格密码等）的过渡。依赖于素数的密码技术在某种意义上可能正在迎来其黄金时代的落幕，但素数本身的数学价值将永远不会消失。&lt;/p>
&lt;hr>
&lt;h1 id="9-结语数学的抽象性与现实社会的交汇点">9. 结语：数学的抽象性与现实社会的交汇点
&lt;/h1>&lt;div class="mermaid">graph TD
A["对纯粹数学的探索"] --> B["阐明黎曼猜想"]
B --> C["对素数分布的完全理解"]
C --> D["数论、代数几何学的飞跃发展"]
D -.-> E["新质因数分解算法的可能性"]
E -.-> F["更新密码技术的安全性评估"]
A --> G["应用数学、计算机科学"]
G --> H["提高素数判定、密钥生成的效率"]
H --> F&lt;/div>
&lt;p>自古希腊以来对素数不懈的探索，通过黎曼这位天才，升华为了复平面上优美的交响乐（zeta函数的零点）。令人惊叹的是，那纯粹无暇的数学结晶，历经数个世纪，如今已被应用为保障互联网社会安全的最强盾牌。&lt;/p>
&lt;p>黎曼猜想是一个同时象征着数学所具有的“抽象之美”与“对物理世界、现实社会惊人的适用能力”的存在。&lt;/p>
&lt;p>当这座尚未有人登顶的巍峨数学高山在未来的某一天被征服时，我们不仅将完全理解素数这一宇宙真理，而且会对信息化社会的基础产生全新的视角。学习密码技术，本身也是一趟追溯人类智慧历史的旅程。&lt;/p></description></item><item><title>使用C++实现的高速素数判定算法（Miller-Rabin法等）</title><link>http://kenji.blog/zh-cn/p/cpp-fast-prime-testing-miller-rabin/</link><pubDate>Fri, 11 Sep 2026 14:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/p/cpp-fast-prime-testing-miller-rabin/</guid><description>&lt;img src="http://kenji.blog/p/cpp-fast-prime-testing-miller-rabin/img/eyecatch.jpg" alt="Featured image of post 使用C++实现的高速素数判定算法（Miller-Rabin法等）" />&lt;h1 id="前言为什么需要高速的素数判定">前言：为什么需要高速的素数判定
&lt;/h1>&lt;p>在信息科学、密码学理论以及算法竞赛的世界中，快速且准确地判断“一个数是否为素数”是一个非常重要且基础的课题。例如，支撑现代互联网社会安全的RSA加密等公钥加密体制，其安全性的基础就在于巨大素数的生成及其相乘的困难性（大整数分解的困难性）。因此，能够瞬间辨别出巨大数是否为素数的技术，毫不夸张地说，正是支撑数字社会根基的技术。&lt;/p>
&lt;p>此外，在算法竞赛（如AtCoder、Codeforces等）中，素数判定也是一个高频主题。面对限制条件为 $N \le 10^{18}$ 这样巨大的输入，如果要求在1秒内进行数万次素数判定，传统的朴素算法绝对无法在规定时间内完成（会导致超时：Time Limit Exceeded, TLE）。&lt;/p>
&lt;p>本文将从朴素的素数判定算法开始，介绍概率性素数判定法“费马测试”，以及克服了其弱点、在实际应用中堪称最强级别的高速算法——“米勒-拉宾（Miller-Rabin）素数判定法”。我们将从数学背景到高度优化的C++实现，进行彻底的讲解。特别是针对64位整数（$N &lt; 2^{64}$），我们不仅停留在概率性判定上，还将详细说明“能够100%确定是否为素数（确定性判定）”的方法，并提供可直接应用于实战的C++源代码。&lt;/p>
&lt;hr>
&lt;h1 id="1-素数判定的基础与试除法-trial-division">1. 素数判定的基础与试除法 (Trial Division)
&lt;/h1>&lt;p>素数（Prime number）是指在大于1的自然数中，除了1和它本身以外不再有其他正因数的数。如果严格按照素数的定义，要判断一个整数 $N$ 是否为素数，只需用从 $2$ 到 $N-1$ 的所有整数去尝试除 $N$，如果都无法整除，则是素数；如果哪怕有一次能整除，则是合数（非素数）。&lt;/p>
&lt;p>然而，这种方法的时间复杂度为 $O(N)$。当 $N$ 是像 $10^{18}$ 这样的巨大数字时，即使是现代计算机也会消耗极其漫长的时间来进行计算。&lt;/p>
&lt;h2 id="试除法的优化探索至-sqrtn">试除法的优化：探索至 $\sqrt{N}$
&lt;/h2>&lt;p>当合数 $N$ 可以表示为 $a \times b = N$ （$a \le b$）时，必然有 $a \le \sqrt{N}$。因此，素数判定的循环不需要执行到 $N-1$，只需检查到 $\sqrt{N}$ 就足够了。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 使用试除法进行素数判定 (O(sqrt(N)))
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">bool&lt;/span> &lt;span class="nf">is_prime_trial_division&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 只检查3以上的奇数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>该算法的时间复杂度为 $O(\sqrt{N})$。如果 $N \le 10^{12}$ 左右，可以瞬间计算完成；但如果 $N \approx 10^{18}$，循环次数约为 $10^9$ 次，即使使用C++编译器，也需要数百毫秒到几秒的时间，因此不适合进行多次判定。&lt;/p>
&lt;hr>
&lt;h1 id="2-费马测试概率性素数判定的开端">2. 费马测试：概率性素数判定的开端
&lt;/h1>&lt;p>为了突破试除法的局限性，人们利用数论定理提出了“概率性算法（Probabilistic Algorithm）”。其中最具代表性的就是利用费马小定理的“费马测试（Fermat Primality Test）”。&lt;/p>
&lt;h2 id="费马小定理-fermats-little-theorem">费马小定理 (Fermat&amp;rsquo;s Little Theorem)
&lt;/h2>&lt;p>皮埃尔·德·费马（Pierre de Fermat）发现的这一定理主张如下：&lt;/p>
&lt;blockquote>
&lt;p>对于任意素数 $p$ 和任意与 $p$ 互质（不是 $p$ 的倍数）的整数 $a$，以下同余式成立：
&lt;/p>
$$ a^{p-1} \equiv 1 \pmod p $$
&lt;/blockquote>
&lt;p>取该定理的逆否命题，可以说：“对于某个整数 $N$ 及与 $N$ 互质的整数 $a$，如果 $a^{N-1} \not\equiv 1 \pmod N$ 成立，那么 $N$ 必定是合数。”费马测试正是利用这一性质，针对待判定的数 $N$ 选择一个随机的底数（base）$a$，计算 $a^{N-1} \pmod N$ 并确认其结果是否为 $1$。&lt;/p>
&lt;h2 id="快速幂取模反复平方法">快速幂取模（反复平方法）
&lt;/h2>&lt;p>为了进行费马测试，必须能够高速计算 $a^{N-1} \pmod N$ 这种巨大的幂运算。为此，我们使用“反复平方法（Modular Exponentiation / Binary Exponentiation）”。其时间复杂度为 $O(\log N)$，计算速度非常快。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 使用反复平方法计算 a^b mod m
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="nf">mod_pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">%=&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">__int128_t&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">res&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">__int128_t&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">b&lt;/span> &lt;span class="o">&amp;gt;&amp;gt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">res&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>※ 在此代码中，为了防止溢出，我们使用了GCC/Clang的扩展类型 &lt;code>__int128_t&lt;/code>（128位整数）来保存中间的乘积。&lt;/p>
&lt;h2 id="伪素数与卡迈克尔数-carmichael-numbers">伪素数与卡迈克尔数 (Carmichael Numbers)
&lt;/h2>&lt;p>费马测试虽然非常强大，但存在一个致命的弱点。那就是：存在一些数 $N$，尽管它是合数，但对于所有的 $a$ （与 $N$ 互质的 $a$），$a^{N-1} \equiv 1 \pmod N$ 都能成立。&lt;/p>
&lt;p>这种数被称为“绝对伪素数”或“卡迈克尔数”。最小的卡迈克尔数是 $561 = 3 \times 11 \times 17$。
由于卡迈克尔数的存在，仅靠费马测试无法进行“100%概率”的确定性判定。无论你尝试多少个不同的 $a$，像 $561$ 这样的数始终会伪装成素数骗过测试。&lt;/p>
&lt;hr>
&lt;h1 id="3-米勒-拉宾素数判定法-miller-rabin-primality-test">3. 米勒-拉宾素数判定法 (Miller-Rabin Primality Test)
&lt;/h1>&lt;p>出色地克服了费马测试弱点（卡迈克尔数的存在）的方法，是由加里·米勒（Gary L. Miller）和迈克尔·拉宾（Michael O. Rabin）提出的“米勒-拉宾素数判定法”。
目前，作为实用的高速素数判定算法，它在各种编程语言的内部库以及密码系统的密钥生成中得到了最广泛的应用。&lt;/p>
&lt;h2 id="数学原理">数学原理
&lt;/h2>&lt;p>米勒-拉宾算法除了利用费马小定理外，还利用了这样一个性质：“在以素数为模的剩余系（$\mathbb{Z}/p\mathbb{Z}$）中，方程 $x^2 \equiv 1 \pmod p$ 的解仅限于 $x \equiv 1$ 或 $x \equiv -1$”（当模为合数时，可能存在除此以外的非平凡平方根）。&lt;/p>
&lt;p>待判定的奇数 $N$ 减去 $1$ 得到的 $N-1$ 必定是偶数。因此，我们可以将 $N-1$ 尽可能多地除以 $2$，表示为以下形式：
&lt;/p>
$$ N-1 = d \cdot 2^s $$
&lt;p>
（其中，$d$ 为奇数，$s \ge 1$）&lt;/p>
&lt;p>对于任意底数 $a$ （$1 &lt; a &lt; N-1$），我们会根据费马小定理验证 $a^{N-1} \equiv 1 \pmod N$ 是否成立，但这个计算是分阶段进行的。
具体来说，就是依次连续进行平方计算：$a^d, a^{d \cdot 2}, a^{d \cdot 4}, \ldots, a^{d \cdot 2^s}$。&lt;/p>
&lt;p>米勒-拉宾测试判定 $N$ 为“素数（或者以极高的概率为素数）”的条件是，以下&lt;strong>任意一项&lt;/strong>成立：&lt;/p>
&lt;ol>
&lt;li>$a^d \equiv 1 \pmod N$&lt;/li>
&lt;li>存在某个 $r$ （$0 \le r &lt; s$），使得 $a^{d \cdot 2^r} \equiv -1 \pmod N$ 成立。
※ 在C++的取模运算中，$-1 \pmod N$ 相当于 $N-1$。&lt;/li>
&lt;/ol>
&lt;p>如果 $N$ 是素数，那么对于任意的 $a$，该条件必定满足。反之，如果 $N$ 是合数，在随机选择 $a$ 时，满足该条件（即被骗过）的概率在数学上被证明为不超过 $\frac{1}{4}$。
只要进行 $k$ 次独立的测试，误判的概率就会小于 $\left(\frac{1}{4}\right)^k$，在实际应用中可以视作零。不存在像卡迈克尔数那样“绝对能蒙混过关”的数。&lt;/p>
&lt;h2 id="米勒-拉宾法的算法流程mermaid流程图">米勒-拉宾法的算法流程（Mermaid流程图）
&lt;/h2>&lt;p>下图展示了米勒-拉宾素数判定法中一次测试（针对单一底数 $a$ 的测试）的逻辑流程。&lt;/p>
&lt;div class="mermaid">graph TD
Start["测试开始 (输入: N, a)"] --> CalcDS["计算满足 N-1 = d * 2^s 的 d (奇数) 和 s"]
CalcDS --> CalcX["计算 x = a^d mod N"]
CalcX --> CheckX1{"x == 1 或 x == N-1 ?"}
CheckX1 -- "Yes" --> ReturnTrue["可能是素数 (Probably Prime)"]
CheckX1 -- "No" --> LoopStart["从 r = 1 到 s-1 开始循环"]
LoopStart --> LoopCondition{"r &lt; s ?"}
LoopCondition -- "No" --> ReturnFalse["确认为合数 (Composite)"]
LoopCondition -- "Yes" --> SquareX["计算 x = (x * x) mod N"]
SquareX --> CheckXMinus1{"x == N - 1 ?"}
CheckXMinus1 -- "Yes" --> ReturnTrue
CheckXMinus1 -- "No" --> CheckXOne{"x == 1 ?"}
CheckXOne -- "Yes" --> ReturnFalse
CheckXOne -- "No" --> LoopNext["r 增加 1 并继续"]
LoopNext --> LoopCondition&lt;/div>
&lt;hr>
&lt;h1 id="4-针对64位整数的确定性判定">4. 针对64位整数的确定性判定
&lt;/h1>&lt;p>米勒-拉宾素数判定法本质上是“概率性”算法，但如果 $N$ 的上限是固定的，通过测试特定的一组 $a$ （底数），就能够“100%确切地”进行素数判定。
这被称为 &lt;strong>确定性米勒-拉宾测试（Deterministic Miller-Rabin Test）&lt;/strong>。&lt;/p>
&lt;p>根据Jim Sinclair等人的研究，对于 $N &lt; 2^{64}$ （约 $1.8 \times 10^{19}$）的所有整数，只要选取以下 $7$ 个素数作为底数 $a$ 进行测试，就足以实现完全确定性的判定。&lt;/p>
&lt;p>&lt;strong>需要测试的底数 $a$ 列表:&lt;/strong>
&lt;code>{2, 325, 9375, 28178, 450775, 9780504, 1795265022}&lt;/code>&lt;/p>
&lt;p>或者，使用另一组广为人知的 $12$ 个素数，同样能够在 $N &lt; 2^{64}$ 的范围内进行完美判定：
&lt;code>{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}&lt;/code>&lt;/p>
&lt;p>在本次实现中，为了兼顾算法的简洁性与高可靠性，我们采用基于后者 $12$ 个素数（或更为优化的 $7$ 个基底）的方法。在C++的实现中，通过条件分支划分数值范围，进行优化以将测试次数降至最低。&lt;/p>
&lt;hr>
&lt;h1 id="5-使用c的高级实现-highly-optimized-c-implementation">5. 使用C++的高级实现 (Highly Optimized C++ Implementation)
&lt;/h1>&lt;p>接下来，我们将结合迄今为止的数学理论与算法设计，展示现代C++中最强级别的米勒-拉宾素数判定函数的实现代码。&lt;/p>
&lt;h2 id="实现要点">实现要点
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>&lt;strong>避免64位整数乘法的溢出:&lt;/strong>
当 $N \approx 10^{18}$ 时，取模乘法中的 $x \times x$ 最大可达 $10^{36}$，会轻松超过普通64位整数（&lt;code>uint64_t&lt;/code> 或 &lt;code>long long&lt;/code>）的最大值 $1.8 \times 10^{19}$ 导致溢出。
为了解决这个问题，我们使用GCC和Clang的扩展类型 &lt;code>__int128_t&lt;/code>（或 &lt;code>unsigned __int128&lt;/code>），在进行128位精度的计算后再取模。这样一来，无需复杂的算法也能高速完成取模乘法。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>确定性基底的选择:&lt;/strong>
当 $N$ 的值较小时，优化代码使得只需测试少量的底数（base）。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="完整的c源代码">完整的C++源代码
&lt;/h2>&lt;p>下面给出可用于实战的最终版源代码。您可以直接将此代码复制到算法竞赛等环境中使用。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;span class="lnt">84
&lt;/span>&lt;span class="lnt">85
&lt;/span>&lt;span class="lnt">86
&lt;/span>&lt;span class="lnt">87
&lt;/span>&lt;span class="lnt">88
&lt;/span>&lt;span class="lnt">89
&lt;/span>&lt;span class="lnt">90
&lt;/span>&lt;span class="lnt">91
&lt;/span>&lt;span class="lnt">92
&lt;/span>&lt;span class="lnt">93
&lt;/span>&lt;span class="lnt">94
&lt;/span>&lt;span class="lnt">95
&lt;/span>&lt;span class="lnt">96
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;vector&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cstdint&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;initializer_list&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 使用 128 位整数进行高速的 (a * b) mod m 计算
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">inline&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="nf">mod_mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span>&lt;span class="p">)((&lt;/span>&lt;span class="kt">unsigned&lt;/span> &lt;span class="n">__int128&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 使用反复平方法计算 (base^exp) mod m
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="nf">mod_pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">exp&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base&lt;/span> &lt;span class="o">%=&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">exp&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">exp&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">exp&lt;/span> &lt;span class="o">&amp;gt;&amp;gt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">res&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 基于米勒-拉宾素数判定法的 64 位整数确定性判定
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">bool&lt;/span> &lt;span class="nf">is_prime_miller_rabin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 边界值与小素数的提前判定
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">3&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">5&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">7&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">3&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">5&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">7&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 分解为 n-1 = d * 2^s 的形式
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">d&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">s&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="n">d&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">d&lt;/span> &lt;span class="o">&amp;gt;&amp;gt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 判定所用的底数 (bases) 列表
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// 根据 N 的大小，将需要测试的基底数量优化到最低限度
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">uint64_t&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">bases&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">4759123141ULL&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bases&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">7&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">61&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">1122004669633ULL&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bases&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">13&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">23&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1662803&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 对于所有 N &amp;lt; 2^64 的数，都能提供确定性判定的 7 个底数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">bases&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">325&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">9375&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">28178&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">450775&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">9780504&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1795265022&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 对每个底数执行测试
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="nl">a&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">bases&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">%=&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">continue&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 当 a 为 n 的倍数时无法判定，但能说明 n 不是素数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">continue&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 满足第一条件，进入下一个底数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">bool&lt;/span> &lt;span class="n">composite&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 循环 s-1 次 (x = x^2 mod n)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">r&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">r&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">s&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">r&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">composite&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 满足第二条件，可能是素数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 如果所有条件都不满足，则确认为合数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">composite&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 所有基底都满足条件，则确认为素数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 测试用例
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">uint64_t&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">test_cases&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="mi">1000000007&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// 著名的素数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="mi">998244353&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// 著名的素数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="mi">1000000000000000003&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// 10^18 附近的素数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="mi">1000000000000000007&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// 合数 (10^18 + 7)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="mi">561&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// 卡迈克尔数 (合数)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="mi">18446744073709551557ULL&lt;/span> &lt;span class="c1">// 2^64 附近最大的素数之一
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="nl">n&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">test_cases&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34; is &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">is_prime_miller_rabin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="s">&amp;#34;Prime&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s">&amp;#34;Composite&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h1 id="6-算法的时间复杂度与性能评估">6. 算法的时间复杂度与性能评估
&lt;/h1>&lt;p>接下来探讨所实现算法的性能。&lt;/p>
&lt;h2 id="时间复杂度-time-complexity">时间复杂度 (Time Complexity)
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>试除法:&lt;/strong> $O(\sqrt{N})$&lt;/li>
&lt;li>&lt;strong>费马测试:&lt;/strong> 幂运算 $O(\log N) \times k$ （$k$ 为尝试次数）&lt;/li>
&lt;li>&lt;strong>米勒-拉宾法:&lt;/strong> 幂运算及内部循环 $O(\log N) \times k$&lt;/li>
&lt;/ul>
&lt;p>在64位环境（$N \le 2^{64}$）下，上述确定性米勒-拉宾法最多只验证 $7$ 个基底。因此，可将 $k \le 7$ 视为常数，整体的时间复杂度严格来说就是 $O(\log N)$。
即使在最坏情况下（$N \approx 10^{19}$），执行步骤也最多只有 $7 \times 64 = 448$ 步基本运算，运行时间在数微秒（$10^{-6}$ 秒）以内。与试除法的 $O(\sqrt{N})$（循环次数 $\approx 4 \times 10^9$ 次）相比，实现了 &lt;strong>数百万倍的性能提升&lt;/strong>。&lt;/p>
&lt;h2 id="进一步优化蒙哥马利乘法-montgomery-multiplication">进一步优化：蒙哥马利乘法 (Montgomery Multiplication)
&lt;/h2>&lt;p>在本文的实现中，我们使用了128位整数扩展类型 &lt;code>__int128_t&lt;/code> 来进行除法（取模运算 &lt;code>%&lt;/code>）。即使在现代CPU上，整数除法（DIV指令）也是比加法和乘法花费更多周期（数十个周期）的高成本指令。&lt;/p>
&lt;p>追求极限优化的代码库作者或竞赛选手，有时会采用被称为 &lt;strong>蒙哥马利乘法（Montgomery Multiplication）&lt;/strong> 的技术。蒙哥马利乘法通过将数字映射到特殊的“蒙哥马利空间”，是一种能将高成本的取模运算（除法）替换为“仅需位移和乘法”的惊人算法。
将此技术引入到米勒-拉宾判定的取模乘法中，可将执行速度进一步提升2到3倍左右。由于这是一个极其深奥的主题，我希望能在另一篇文章中对其进行详细讲解。&lt;/p>
&lt;hr>
&lt;h1 id="7-总结">7. 总结
&lt;/h1>&lt;p>本文从素数判定的基础一直讲解到了进阶内容。
让我们回顾一下要点：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>试除法&lt;/strong> 确切可靠，但由于时间复杂度为 $O(\sqrt{N})$，当 $N$ 超过 $10^{12}$ 时缺乏实用性。&lt;/li>
&lt;li>&lt;strong>费马测试&lt;/strong> 在 $O(\log N)$ 内极其快速，但有着被卡迈克尔数等绝对伪素数欺骗的致命弱点。&lt;/li>
&lt;li>&lt;strong>米勒-拉宾素数判定法&lt;/strong> 解决了费马测试的弱点，是最具实用性且最强的算法。&lt;/li>
&lt;li>在使用C++实现时，通过运用 &lt;code>__int128_t&lt;/code> 可以安全地处理64位整数的乘法溢出。&lt;/li>
&lt;li>在64位整数范围（$N &lt; 2^{64}$）内，通过选取 $7$ 个或 $12$ 个特定的素数作为底数，可以进行&lt;strong>确定性（100%准确）而非概率性的素数判定&lt;/strong>。&lt;/li>
&lt;/ol>
&lt;p>在涉及大数字计算的领域，高速的素数判定是一项不可或缺的技术。本文提供的C++米勒-拉宾源代码健壮可靠，可直接应用于实战。请务必在您的项目或算法竞赛中尝试使用它。&lt;/p>
&lt;div class="mermaid">graph LR
TrialDivision["试除法 (O(√N))"] --> Fermat["费马测试 (O(log N), 存在弱点)"]
Fermat --> MillerRabin["米勒-拉宾法 (O(log N), 可转化为确定性)"]
MillerRabin --> Montgomery["+ 蒙哥马利乘法 (常数倍加速)"]
style MillerRabin fill:#f9f,stroke:#333,stroke-width:2px&lt;/div>
&lt;p>编程与数学交织的算法世界是如此美丽且深奥。希望能为您的学习带来帮助。&lt;/p>
&lt;hr>
&lt;p>&lt;em>参考资料 (Reference):&lt;/em>&lt;/p>
&lt;ul>
&lt;li>&lt;em>Pomerance, C., Selfridge, J. L., &amp;amp; Wagstaff, S. S. (1980). The pseudoprimes to 25.10^9. Mathematics of Computation.&lt;/em>&lt;/li>
&lt;li>&lt;em>Sinclair, J. (2011). Deterministic Miller-Rabin primality testing.&lt;/em>&lt;/li>
&lt;/ul></description></item><item><title>【数学解说】让高中生也能看懂的RSA加密原理</title><link>http://kenji.blog/zh-cn/p/rsa-encryption-math-explained-for-beginners/</link><pubDate>Fri, 11 Sep 2026 13:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/p/rsa-encryption-math-explained-for-beginners/</guid><description>&lt;img src="http://kenji.blog/p/rsa-encryption-math-explained-for-beginners/img/eyecatch.jpg" alt="Featured image of post 【数学解说】让高中生也能看懂的RSA加密原理" />&lt;p>互联网社会安全的基础技术之一是“RSA加密”。在线购物的信用卡支付、与朋友的SNS交流、公司机密信息的收发等，我们每天都在不经意间使用的很多通信，都是由RSA加密或其后继技术保护着的。&lt;/p>
&lt;p>然而，一听到“密码（加密）”，你可能会想象出间谍电影中出现的复杂密码机，或者只有少数天才才能理解的超高级数学。确实，现代密码学基于高级数学，但&lt;strong>RSA加密的根本原理，只要有高中学过的数学（整数的性质、素数、同余式等）知识就足以理解&lt;/strong>。&lt;/p>
&lt;p>本文将以高中数学知识为起点，逐步彻底解说RSA加密是基于怎样的数学原理运行的，以及为什么破解它如此困难。为了让稍微不擅长数学的人也能理解，我们将结合具体例子进行详细说明。&lt;/p>
&lt;hr>
&lt;h2 id="1-对称加密与非对称加密公开密钥加密">1. 对称加密与非对称加密（公开密钥加密）
&lt;/h2>&lt;p>在进入RSA加密的数学原理之前，我们先来整理一下加密的基本概念。加密方式大致分为“对称加密（共享密钥加密）”和“非对称加密（公开密钥加密）”两种。&lt;/p>
&lt;h3 id="11-对称加密方式的局限性">1.1 对称加密方式的局限性
&lt;/h3>&lt;p>自古以来使用的密码大多被称为“对称加密方式”。这是一种&lt;strong>在“加密（将消息转换为秘密的密文）”和“解密（将密文还原为原始消息）”时使用相同密钥&lt;/strong>的方式。&lt;/p>
&lt;p>例如，爱丽丝想给鲍勃发送一封秘密信件。爱丽丝用挂锁（对称密钥）把信锁在盒子里。鲍勃要打开那个盒子，就必须拥有与爱丽丝使用的相同的钥匙。&lt;/p>
&lt;p>这种方式存在一个大问题，那就是“密钥分发问题”。相隔遥远的爱丽丝和鲍勃在第一次通信时，该如何共享密钥而不被窃听呢？如果在邮寄密钥的途中被第三方窃取，之后的加密通信将全部泄露。&lt;/p>
&lt;h3 id="12-划时代的发明公开密钥加密方式">1.2 划时代的发明“公开密钥加密方式”
&lt;/h3>&lt;p>为了解决这个密钥分发问题而发明的就是“公开密钥加密方式”。RSA加密也是其中一种。&lt;/p>
&lt;p>在公开密钥加密方式中，使用**“用于加密的密钥（公钥）”和“用于解密的密钥（私钥）”这两个不同的密钥**。&lt;/p>
&lt;ol>
&lt;li>接收者鲍勃创建一对“公钥”和“私钥”。&lt;/li>
&lt;li>鲍勃将“公钥”向全世界公开（任何人获取都可以）。&lt;/li>
&lt;li>发送者爱丽丝使用鲍勃的“公钥”对消息进行加密并发送。&lt;/li>
&lt;li>加密后的消息，只有拥有鲍勃“私钥”的人（即鲍勃自己）才能解密。&lt;/li>
&lt;/ol>
&lt;p>如果用挂锁来比喻，鲍勃制作了许多“处于打开状态的挂锁（公钥）”并散布到全世界。爱丽丝把要寄给鲍勃的信放进盒子里，捡起鲍勃的挂锁“咔哒”一声锁上。挂锁一旦锁上，就只能用鲍勃拥有的“万能钥匙（私钥）”才能打开。即使有人中途偷了盒子，因为没有万能钥匙也无法打开。&lt;/p>
&lt;div class="mermaid">graph TD
A["爱丽丝 (发送者)"] --> B["明文 (消息)"]
B --> C["加密处理"]
D["鲍勃的公钥 (任何人皆可获取)"] --> C
C --> E["通过互联网发送: 密文"]
E --> F["解密处理"]
G["鲍勃的私钥 (仅鲍勃拥有)"] --> F
F --> H["还原的明文 (消息)"]
H --> I["鲍勃 (接收者)"]&lt;/div>
&lt;p>为了实现这个划时代的系统，需要一种**“单向函数（单向的数学谜题）”**，即“用公钥很容易加密，但如果没有私钥绝对无法解密”。作为这个谜题的部件而被看中的，就是我们熟知的“素数（质数）”。&lt;/p>
&lt;hr>
&lt;h2 id="2-支撑rsa加密的数学基础1素数与素因数分解">2. 支撑RSA加密的数学基础1：素数与素因数分解
&lt;/h2>&lt;p>RSA加密的安全性基于**“对巨大数字进行素因数分解非常困难”**这一数学事实。&lt;/p>
&lt;h3 id="21-什么是素数">2.1 什么是素数
&lt;/h3>&lt;p>素数是指“只能被1和自身整除的、大于1的自然数”。
例如：$2, 3, 5, 7, 11, 13, 17, 19, 23...$&lt;/p>
&lt;p>素数就像是所有整数的“原子”。任何自然数都可以分解成素数相乘的形式。这被称为&lt;strong>素因数分解&lt;/strong>。例如，$60 = 2^2 \times 3 \times 5$，如果不考虑顺序，素因数分解的方式只有一种，这就是著名的“算术基本定理”。&lt;/p>
&lt;h3 id="22-素因数分解的困难性单向函数">2.2 素因数分解的困难性（单向函数）
&lt;/h3>&lt;p>这里重要的是**“乘法很容易，但素因数分解很难”**这种非对称性。&lt;/p>
&lt;p>例如，请心算下面两个素数的乘法：
$11 \times 13 = ?$
这很简单吧。答案是 $143$。&lt;/p>
&lt;p>那么，下面这个数呢？
请对 $323$ 进行素因数分解。
怎么样？应该会花点时间吧。（答案是 $17 \times 19$）。&lt;/p>
&lt;p>如果数字很小，人类还能勉强计算出来，但当数字变大时，即使使用计算机，计算也会呈爆炸式增长。目前主流的RSA加密中，使用了2048位（十进制约600位）这样极其巨大的素数 $p$ 和 $q$ 相乘得到的数 $N = p \times q$。&lt;/p>
&lt;p>当给出两个巨大的素数 $p$ 和 $q$ 时，计算机计算 $N$ 只需要一瞬间（不到一毫秒）。然而，反过来如果只给出 $N$，要找出原来的 $p$ 和 $q$，即使让目前最快的超级计算机运行数万亿年也解不出来。&lt;/p>
&lt;p>这种**“计算的非对称性（去向容易，逆向困难）”**，构成了公钥和私钥之间关系的基础。&lt;/p>
&lt;hr>
&lt;h2 id="3-支撑rsa加密的数学基础2同余式模运算">3. 支撑RSA加密的数学基础2：同余式（模运算）
&lt;/h2>&lt;p>RSA加密的计算，并不是我们在日常生活中使用的无限变大的加法或乘法，而是在除以某个数后的“余数”世界里进行的。这被称为&lt;strong>同余式（模运算）&lt;/strong>。&lt;/p>
&lt;h3 id="31-钟表数学">3.1 钟表数学
&lt;/h3>&lt;p>模运算常被比喻为“钟表数学”。假设现在是10点，那么5小时后是几点？虽然 $10 + 5 = 15$ 点，但在普通的12小时制钟表上，我们会回答“3点”。这是因为15除以12的余数是3。&lt;/p>
&lt;p>在数学世界中，这被写成如下形式：
&lt;/p>
$$ 15 \equiv 3 \pmod{12} $$
&lt;p>
读作“15和3在模12下同余（除以12的余数相等）”。&lt;/p>
&lt;h3 id="32-同余式的基本性质">3.2 同余式的基本性质
&lt;/h3>&lt;p>同余式有着与等式（$=$）非常相似且非常方便的性质。设模数（除数）为 $N$。
当 $a \equiv b \pmod N$ 且 $c \equiv d \pmod N$ 时，以下性质成立：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>加法:&lt;/strong> $a + c \equiv b + d \pmod N$&lt;/li>
&lt;li>&lt;strong>减法:&lt;/strong> $a - c \equiv b - d \pmod N$&lt;/li>
&lt;li>&lt;strong>乘法:&lt;/strong> $a \times c \equiv b \times d \pmod N$&lt;/li>
&lt;li>&lt;strong>乘方:&lt;/strong> $a^k \equiv b^k \pmod N$ （$k$ 为自然数）&lt;/li>
&lt;/ol>
&lt;p>特别重要的是“乘方”的性质。这意味着**“余数的乘方，等于乘方的余数”**。
例如，假设我们想求 $7^{100}$ 除以 $5$ 的余数。如果要老老实实地把 $7$ 乘100次再除以 $5$，那简直是场灾难。但如果使用同余式的性质，因为 $7 \equiv 2 \pmod 5$，所以 $7^{100} \equiv 2^{100} \pmod 5$，计算就会变得非常简单。在密码学的世界里，因为要处理非常大的数字的乘方，所以这个性质是必不可少的。&lt;/p>
&lt;hr>
&lt;h2 id="4-支撑rsa加密的数学基础3欧拉函数与欧拉定理">4. 支撑RSA加密的数学基础3：欧拉函数与欧拉定理
&lt;/h2>&lt;p>接下来的部分是RSA加密核心的魔法数学。作为“费马小定理”的推广，“欧拉定理”即将登场。&lt;/p>
&lt;h3 id="41-欧拉函数-phin">4.1 欧拉函数 $\phi(N)$
&lt;/h3>&lt;p>欧拉函数（$\phi$ 函数）是指，对于某个自然数 $N$，**“返回从1到 $N$ 的自然数中，与 $N$ 互质（最大公约数为1）的数的个数”**的函数。&lt;/p>
&lt;p>我们来看几个例子：&lt;/p>
&lt;ul>
&lt;li>$\phi(5)$: 在 1, 2, 3, 4, 5 中，与 5 互质的数有 1, 2, 3, 4，共4个。因此 $\phi(5) = 4$。&lt;/li>
&lt;li>$\phi(6)$: 在 1, 2, 3, 4, 5, 6 中，与 6 互质的数有 1, 5，共2个。因此 $\phi(6) = 2$。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>【素数时的特殊性质】&lt;/strong>
当 $p$ 是素数时，从1到 $p-1$ 的所有数都与 $p$ 互质。因此，
&lt;/p>
$$ \phi(p) = p - 1 $$
&lt;p>&lt;strong>【素数乘积时的特殊性质】&lt;/strong>
对于两个不同的素数 $p$ 和 $q$，如果 $N = p \times q$，那么 $\phi(N)$ 可以通过以下计算轻松求出：
&lt;/p>
$$ \phi(N) = \phi(p) \times \phi(q) = (p - 1)(q - 1) $$
&lt;p>
这个性质起到了RSA加密中“秘密后门（陷门）”的作用。知道 $p$ 和 $q$ 的人（密钥创建者）可以瞬间计算出 $\phi(N)$，而只知道 $N$ 的第三方，除非对 $N$ 进行素因数分解，否则无法求出 $\phi(N)$。&lt;/p>
&lt;h3 id="42-欧拉定理">4.2 欧拉定理
&lt;/h3>&lt;p>莱昂哈德·欧拉利用这个 $\phi(N)$，证明了以下这个优美的定理。&lt;/p>
&lt;p>&lt;strong>欧拉定理：&lt;/strong>
当整数 $a$ 和 $N$ 互质时，以下同余式成立。
&lt;/p>
$$ a^{\phi(N)} \equiv 1 \pmod N $$
&lt;p>这是一个惊人的性质，意思是“将某个数 $a$ 连续相乘 $\phi(N)$ 次后除以 $N$，余数必定是 $1$”。（当 $N$ 为素数 $p$ 时，变为 $a^{p-1} \equiv 1 \pmod p$，这被称为费马小定理）。&lt;/p>
&lt;p>让我们对这个欧拉定理进行变形。在等式两边再乘一次 $a$。
&lt;/p>
$$ a^{\phi(N) + 1} \equiv a \pmod N $$
&lt;p>进一步地，对于任意整数 $k$，$a^{k \cdot \phi(N)}$ 也等于 $1^k = 1$，因此以下等式成立：
&lt;/p>
$$ a^{k \cdot \phi(N) + 1} \equiv a \pmod N $$
&lt;p>这个公式正是让RSA加密实现**“加密后再解密就能恢复原状”**这一魔法的根本原理。&lt;/p>
&lt;hr>
&lt;h2 id="5-rsa加密的算法密钥生成加密解密的步骤">5. RSA加密的算法：密钥生成、加密、解密的步骤
&lt;/h2>&lt;p>基础知识准备齐全后，终于可以来看RSA加密的具体步骤了。RSA加密大致分为“1. 密钥生成”、“2. 加密”和“3. 解密”三个阶段。&lt;/p>
&lt;div class="mermaid">flowchart TD
A1["1. 选择素数 p, q"] --> A2["计算 N = p × q"]
A1 --> A3["计算 φ(N) = (p-1)(q-1)"]
A3 --> A4["选择与 φ(N) 互质的 e"]
A3 --> A5["计算 d 使 e × d ≡ 1 (mod φ(N))"]
A2 --> A6["公钥 (N, e)"]
A4 --> A6
A5 --> A7["私钥 d"]
B1["2. 明文消息 M"] --> B2["计算 C ≡ M^e (mod N)"]
A6 -.-> B2
B2 --> B3["发送密文 C"]
B3 --> C1["3. 收到密文 C"]
C1 --> C2["计算 M ≡ C^d (mod N)"]
A7 -.-> C2
C2 --> C3["获取原始明文消息 M"]&lt;/div>
&lt;h3 id="51-密钥生成key-generation">5.1 密钥生成（Key Generation）
&lt;/h3>&lt;p>接收者鲍勃将生成属于自己的“公钥”和“私钥”。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>选择素数:&lt;/strong> 随机选择两个大素数 $p$ 和 $q$。&lt;/li>
&lt;li>&lt;strong>计算模数 $N$:&lt;/strong> 计算 $N = p \times q$。这个 $N$ 是公开的。&lt;/li>
&lt;li>&lt;strong>计算 $\phi(N)$:&lt;/strong> 计算欧拉函数 $\phi(N) = (p - 1)(q - 1)$。这是只有鲍勃知道的秘密数字。&lt;/li>
&lt;li>&lt;strong>选择公钥 $e$:&lt;/strong> 选择一个满足 $1 &lt; e &lt; \phi(N)$，且与 $\phi(N)$ 互质的整数 $e$。&lt;/li>
&lt;li>&lt;strong>计算私钥 $d$:&lt;/strong> 找到一个满足以下条件的整数 $d$。
$$ e \times d \equiv 1 \pmod{\phi(N)} $$
换句话说，就是寻找“一个数 $d$，使得 $e \times d$ 除以 $\phi(N)$ 的余数是 $1$”。&lt;/li>
&lt;/ol>
&lt;p>到这里，密钥的准备就完成了。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>公钥:&lt;/strong> $(N, e)$ 的组合。向全世界公开。&lt;/li>
&lt;li>&lt;strong>私钥:&lt;/strong> $d$。绝对不能告诉任何人。&lt;/li>
&lt;/ul>
&lt;h3 id="52-加密encryption">5.2 加密（Encryption）
&lt;/h3>&lt;p>假设爱丽丝想给鲍勃发送秘密消息 $M$。（$M$ 是将文字数字化的结果，且 $0 \le M &lt; N$）。爱丽丝使用鲍勃的公钥 $(N, e)$ 进行如下计算：&lt;/p>
$$ C \equiv M^e \pmod N $$
&lt;p>计算“消息 $M$ 的 $e$ 次方，除以 $N$ 的余数 $C$”。这个 $C$ 就是密文。&lt;/p>
&lt;h3 id="53-解密decryption">5.3 解密（Decryption）
&lt;/h3>&lt;p>鲍勃收到了密文 $C$。鲍勃使用私钥 $d$ 进行如下计算：&lt;/p>
$$ M \equiv C^d \pmod N $$
&lt;p>计算“密文 $C$ 的 $d$ 次方，除以 $N$ 的余数”，结果竟然奇迹般地恢复成了原始消息 $M$！&lt;/p>
&lt;hr>
&lt;h2 id="6-为什么解密能恢复原状数学证明">6. 为什么解密能恢复原状？（数学证明）
&lt;/h2>&lt;p>你可能会感到疑惑：“为什么只要对 $C$ 求 $d$ 次方，就能恢复成原来的 $M$ 呢？”这时，刚才提到的“欧拉定理”就要大显身手了。&lt;/p>
&lt;p>我们将加密公式 $C = M^e$ 代入解密的计算公式 $C^d \pmod N$ 中看看。
&lt;/p>
$$ C^d \equiv (M^e)^d \equiv M^{ed} \pmod N $$
&lt;p>这里，请回想一下密钥生成步骤的第5步。鲍勃在生成 $d$ 时，是按照满足 $e \times d \equiv 1 \pmod{\phi(N)}$ 来选择的。这意味着“$ed$ 是 $\phi(N)$ 的倍数加上 $1$”。使用整数 $k$ 可以写成如下形式：
&lt;/p>
$$ ed = k \cdot \phi(N) + 1 $$
&lt;p>将其代入指数部分，并利用指数法则进行分解：
&lt;/p>
$$ M^{ed} = M^{k \cdot \phi(N) + 1} = M^{k \cdot \phi(N)} \times M^1 = (M^{\phi(N)})^k \times M $$
&lt;p>假设消息 $M$ 和 $N$ 互质，根据&lt;strong>欧拉定理&lt;/strong>，会有 $M^{\phi(N)} \equiv 1 \pmod N$。
&lt;/p>
$$ (M^{\phi(N)})^k \times M \equiv 1^k \times M \equiv M \pmod N $$
&lt;p>因此，下面这个式子完美成立了：
&lt;/p>
$$ C^d \equiv M \pmod N $$
&lt;p>爱丽丝不知道 $d$，窃听者也不知道 $d$，所以只有拥有 $d$ 的鲍勃，才能从 $C$ 中提取出 $M$。&lt;/p>
&lt;hr>
&lt;h2 id="7-具体例子用小素数手工计算体验rsa">7. 具体例子：用小素数手工计算体验RSA
&lt;/h2>&lt;p>让我们实际使用小数字（素数），来模拟一次爱丽丝向鲍勃发送加密通信的过程吧。&lt;/p>
&lt;p>&lt;strong>【鲍勃的密钥生成阶段】&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>选择两个素数 $p=11$, $q=13$。&lt;/li>
&lt;li>计算 $N = 11 \times 13 = 143$。&lt;/li>
&lt;li>计算 $\phi(N) = (11 - 1) \times (13 - 1) = 10 \times 12 = 120$。&lt;/li>
&lt;li>选择与 $\phi(N)=120$ 互质的公钥 $e$。这里我们选 $e=7$。&lt;/li>
&lt;li>求解私钥 $d$。寻找满足 $7 \times d \equiv 1 \pmod{120}$ 的 $d$。
在方程 $7d = 120k + 1$ 中，当 $k=6$ 时右边为 $721$，$721 \div 7 = 103$。
因此，$d = 103$。&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>公钥：$(N=143, e=7)$&lt;/li>
&lt;li>私钥：$d=103$&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>【爱丽丝的加密阶段】&lt;/strong>
假设要发送消息 $M = 9$。
公式：$C \equiv 9^7 \pmod{143}$
$9^7 = 4,782,969$。除以 143 后商是 $33447$，余数为 $48$。
所以密文 $C = 48$。&lt;/p>
&lt;p>&lt;strong>【鲍勃的解密阶段】&lt;/strong>
鲍勃收到密文 $C = 48$，使用私钥 $d = 103$ 进行解密。
公式：$M \equiv 48^{103} \pmod{143}$
如果在计算器上执行 &lt;code>(48 ** 103) % 143&lt;/code>，结果精彩地显示为“&lt;strong>9&lt;/strong>”！我们成功收到了原来的消息。&lt;/p>
&lt;hr>
&lt;h2 id="8-求私钥-d-的方法扩展欧几里得算法">8. 求私钥 $d$ 的方法：扩展欧几里得算法
&lt;/h2>&lt;p>在手工计算的例子中，我们是靠直觉寻找 $k$ 来得出 $d=103$ 的，但当数字达到几百位时，这种方法就行不通了。实际的程序中会使用名为**“扩展欧几里得算法”**的算法。&lt;/p>
&lt;p>求解 $7d \equiv 1 \pmod{120}$，就等同于寻找满足 $7d + 120y = 1$ 的整数 $d$ 和 $y$。通过逆向推导欧几里得算法，就可以机械地求出这个解。&lt;/p>
&lt;ol>
&lt;li>$120 \div 7 = 17$ 余 $1$&lt;/li>
&lt;li>变形后得到，$1 = 120 - 17 \times 7$&lt;/li>
&lt;li>也就是说，$-17 \times 7 \equiv 1 \pmod{120}$&lt;/li>
&lt;/ol>
&lt;p>$-17$ 在模 $120$ 的世界里，与 $120 - 17 = 103$ 意义相同。因此，瞬间就能求出 $d = 103$。这种方法无论数字多大，都能非常快速地进行计算。&lt;/p>
&lt;hr>
&lt;h2 id="9-rsa加密的另一面数字签名">9. RSA加密的另一面：数字签名
&lt;/h2>&lt;p>RSA加密非常棒的一点是，通过反转公钥和私钥的作用，它还可以用作**“数字签名”**。&lt;/p>
&lt;p>在加密时，是“用公钥加密 $\Rightarrow$ 用私钥解密”，
而在数字签名中，步骤变成了“用私钥加密 $\Rightarrow$ 用公钥解密”。&lt;/p>
&lt;div class="mermaid">flowchart TD
A1["1. 爱丽丝用私钥生成签名"] --> A2["S ≡ M^d (mod N)"]
A2 --> A3["发送消息 M 和签名 S"]
A3 --> B1["2. 鲍勃用公钥验证签名"]
B1 --> B2["计算 M' ≡ S^e (mod N)"]
B2 --> B3["确认 M' 和 M 是否一致"]&lt;/div>
&lt;p>爱丽丝使用自己的私钥 $d$ 对消息进行转换（这就是签名 $S$），然后发送给鲍勃。鲍勃使用爱丽丝的公钥 $e$ 进行验证计算。如果计算结果与原始消息一致，就能同时证明“这是只有爱丽丝的私钥才能生成的数据”以及“消息在传输过程中没有被篡改”。&lt;/p>
&lt;hr>
&lt;h2 id="10-通过程序感受rsa加密">10. 通过程序感受RSA加密
&lt;/h2>&lt;p>手工计算非常困难的乘方计算，如果使用Python就能非常轻松地实现。以下是可以体验RSA加密核心逻辑的Python代码。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;求最大公约数&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">b&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">a&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">mod_inverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;求私钥 d（利用 Python 3.8 及以上版本的内置功能）&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 密钥生成&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">p&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">q&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">13&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">N&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">q&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">phi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">e&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">7&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">d&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_inverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;公钥: (N=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">, e=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">), 私钥: d=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. 加密&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">9&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ciphertext&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;密文: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">ciphertext&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. 解密&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">decrypted_message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ciphertext&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;解密后的消息: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">decrypted_message&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Python 的 &lt;code>pow(base, exp, mod)&lt;/code> 函数在内部使用了称为“快速幂（反复平方法）”的高效算法，所以即使是几百位的数字，也能在一瞬间完成计算。&lt;/p>
&lt;hr>
&lt;h2 id="11-总结与未来的密码技术">11. 总结与未来的密码技术
&lt;/h2>&lt;p>我们以高中数学知识为基础，揭开了RSA加密原理的面纱。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>素因数分解的困难性:&lt;/strong> $p \times q = N$ 很容易计算，但从 $N$ 找出 $p, q$ 却极其困难。&lt;/li>
&lt;li>&lt;strong>同余式与欧拉定理:&lt;/strong> 根据 $a^{\phi(N)} \equiv 1 \pmod N$ 这一法则，完成了“乘方某次后恢复原状”的魔法陷门。&lt;/li>
&lt;li>&lt;strong>公钥与私钥:&lt;/strong> 任何人都可以加密，但只有合法的接收者才能解密。&lt;/li>
&lt;/ol>
&lt;p>目前使用的RSA加密中，$N$ 超过600位，即使动员全世界的超级计算机，进行素因数分解也需要花费超过宇宙年龄的时间。然而，随着近年来“量子计算机”研究的推进，如果在未来投入实用，通过“秀尔算法”，这种素因数分解可能会被瞬间破解。因此，目前世界各地正在加紧开发连量子计算机都无法破解的“抗量子计算密码（后量子密码学）”。&lt;/p>
&lt;p>那些常被认为“没有用处”的高级数学，实际上正在底层默默保护着我们的日常生活。RSA加密正是向我们展示这种数学深度与美感的最佳教材。希望通过这篇文章，能让您哪怕多感受到一点密码学与数学的乐趣。&lt;/p></description></item><item><title>什么是完全同态加密（FHE）？解密下一代安全的核心</title><link>http://kenji.blog/zh-cn/p/fully-homomorphic-encryption-fhe-explained/</link><pubDate>Fri, 11 Sep 2026 11:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/p/fully-homomorphic-encryption-fhe-explained/</guid><description>&lt;img src="http://kenji.blog/p/fully-homomorphic-encryption-fhe-explained/img/eyecatch.jpg" alt="Featured image of post 什么是完全同态加密（FHE）？解密下一代安全的核心" />&lt;p>随着云计算和AI技术作为社会基础设施的普及，“数据隐私”与“数据利用”之间的权衡已成为最重要的问题之一。虽然医疗数据、金融信息和个人生物特征信息等高度机密数据在云端交由AI进行分析的需求日益增长，但出于安全考虑，不少企业仍对向外部发送数据犹豫不决。&lt;/p>
&lt;p>传统的加密技术（如AES和RSA等）擅长保护存储中的数据（Data at Rest）和网络传输中的数据（Data in Transit）。然而，&lt;strong>当服务器端对数据进行搜索或机器学习等处理（计算）时（Data in Use），必须先解密恢复为明文&lt;/strong>。如果在解密的这一时刻服务器遭到黑客攻击，或者内部的恶意管理员窥探数据，将直接导致信息泄露。&lt;/p>
&lt;p>克服“处理时解密”这一根本弱点的梦幻技术，便是&lt;strong>完全同态加密（Fully Homomorphic Encryption: FHE）&lt;/strong>。利用FHE，可以在保持数据加密且完全不解密的情况下进行计算处理，并仅将结果的密文返回给客户端。&lt;/p>
&lt;p>本文将从FHE的概念、历史、Craig Gentry的突破性进展、数学基础（如Ring-LWE），到其面临的最大挑战“噪声”及其解决方案（自举，Bootstrapping），以及最新的实现库，全面深入地解析作为下一代安全核心的FHE。&lt;/p>
&lt;hr>
&lt;h2 id="1-什么是同态加密基本概念">1. 什么是同态加密？基本概念
&lt;/h2>&lt;p>“同态（Homomorphic）”是代数术语，指在具有某种结构的集合之间，能够保持运算结构不变地进行映射的性质。在密码学理论中，“同态性”指的是&lt;strong>明文空间中的运算与密文空间中的运算相对应&lt;/strong>的性质。&lt;/p>
&lt;p>用简单的数学公式表示，设明文 $m_1$ 和 $m_2$ 的加密函数为 $E(\cdot)$，解密函数为 $D(\cdot)$。当明文上的运算（如加法或乘法等）记为 $\circ$，密文上的运算记为 $\diamond$ 时，成立以下关系：&lt;/p>
$$ D(E(m_1) \diamond E(m_2)) = m_1 \circ m_2 $$
&lt;p>也就是说，对密文 $E(m_1)$ 和 $E(m_2)$ 施加某种运算 $\diamond$ 后，将其结果解密，会与原始明文之间进行 $\circ$ 运算的结果一致。&lt;/p>
&lt;h3 id="云计算中的数据流">云计算中的数据流
&lt;/h3>&lt;p>使用FHE的云处理架构与传统的完全不同。下图展示了利用FHE进行安全数据处理的流程。&lt;/p>
&lt;div class="mermaid">graph TD
A["客户端 (持有私钥)"] -->|1. 加密明文 x: E(x)| B["云服务器 (仅加密数据)"]
B -->|2. 在密文状态下应用函数 f: E(f(x))| B
B -->|3. 计算结果的密文 E(y)| A
A -->|4. 使用私钥解密: y = f(x)| A
style A fill:#d4edda,stroke:#28a745
style B fill:#f8d7da,stroke:#dc3545&lt;/div>
&lt;p>服务器虽然接收到加密后的数据 $E(x)$，但由于没有私钥，绝对无法得知数据的内容。然而，通过利用FHE的性质，可以对密文应用函数 $f$（例如机器学习的推理模型）并生成 $E(f(x))$。客户端接收到该结果后，通过自己的私钥解密，便可获得目标结果 $y = f(x)$。&lt;/p>
&lt;hr>
&lt;h2 id="2-同态加密的发展历史phe-she-fhe">2. 同态加密的发展历史：PHE, SHE, FHE
&lt;/h2>&lt;p>同态加密并不是一蹴而就达到现在的“完全”形态的。根据能够实现的运算类型和次数，它大致可分为三个阶段。&lt;/p>
&lt;h3 id="partially-homomorphic-encryption-phe-部分同态加密">Partially Homomorphic Encryption (PHE: 部分同态加密)
&lt;/h3>&lt;p>PHE是&lt;strong>仅能&lt;/strong>无限制地进行加法或乘法其中&lt;strong>一种运算&lt;/strong>的加密方式。实际上，具有这种性质的加密算法自古就已存在。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>RSA加密（对乘法的同态性）&lt;/strong>
RSA加密无意中具有乘法同态性。设明文为 $m_1, m_2$，公钥为 $(e, N)$，则：
$$ E(m_1) = m_1^e \pmod N $$
$$ E(m_2) = m_2^e \pmod N $$
将它们相乘：
$$ E(m_1) \times E(m_2) = (m_1 \cdot m_2)^e \pmod N = E(m_1 \times m_2) $$
这样，密文之间的乘法就对应于明文的乘法。&lt;/li>
&lt;li>&lt;strong>Paillier加密（对加法的同态性）&lt;/strong>
1999年发明的Paillier加密具有加法同态性。它已被实际应用于电子投票（汇总加密选票，仅解密最终结果）等场景中。&lt;/li>
&lt;/ul>
&lt;h3 id="somewhat-homomorphic-encryption-she-有限同态加密">Somewhat Homomorphic Encryption (SHE: 有限同态加密)
&lt;/h3>&lt;p>这是一种可以同时执行加法和乘法的加密方式，但可运算的&lt;strong>次数（电路深度）有限制&lt;/strong>。由于后文将提到的“噪声”积累，当进行超过一定次数的乘法运算后，就会变得无法解密。2005年的BGN（Boneh-Goh-Nissim）加密等就属于此类，但在进行实际复杂的计算（如深度学习等）时存在局限性。&lt;/p>
&lt;h3 id="fully-homomorphic-encryption-fhe-完全同态加密">Fully Homomorphic Encryption (FHE: 完全同态加密)
&lt;/h3>&lt;p>这是一种可以&lt;strong>无限制次数&lt;/strong>地同时执行加法和乘法运算的加密方式。正如信息论中的图灵完备性一样，只要能够无限地组合加法（相当于XOR）和乘法（相当于AND），在原理上就意味着可以在保持加密状态下执行任何可计算的函数和算法。&lt;/p>
&lt;p>FHE长期以来被称为“密码学界的圣杯”，甚至有人认为它是不可能实现的。然而，在2009年，当时正在斯坦福大学攻读博士学位的 &lt;strong>Craig Gentry&lt;/strong> 提出了基于理想格（Ideal Lattices）的首个FHE方案，震惊了世界。&lt;/p>
&lt;hr>
&lt;h2 id="3-fhe的数学基础lwe问题和ring-lwe">3. FHE的数学基础：LWE问题和Ring-LWE
&lt;/h2>&lt;p>目前主流的许多FHE方案都基于&lt;strong>LWE（Learning With Errors，容错学习）问题&lt;/strong>，这是“格密码（Lattice-based Cryptography）”——也以抗量子计算机密码（Post-Quantum Cryptography）而闻名——中的一个数学难题。&lt;/p>
&lt;h3 id="lwe问题的直观理解">LWE问题的直观理解
&lt;/h3>&lt;p>使用高斯消元法等方法，求解线性方程组是很容易的。&lt;/p>
$$ \begin{cases} 3s_1 + 4s_2 + 2s_3 \equiv 12 \pmod{17} \\ 1s_1 + 9s_2 + 5s_3 \equiv 8 \pmod{17} \\ \vdots \end{cases} $$
&lt;p>然而，如果在该方程组的结果中加入极微小的“随机误差（噪声）” $e$，会发生什么呢？&lt;/p>
$$ \begin{cases} 3s_1 + 4s_2 + 2s_3 + e_1 \equiv 13 \pmod{17} \\ 1s_1 + 9s_2 + 5s_3 + e_2 \equiv 7 \pmod{17} \\ \vdots \end{cases} $$
&lt;p>仅仅加入这个误差 $e$，寻找秘密变量向量 $\vec{s}$ 的问题就会转变为一个即使使用现代超级计算机或量子计算机也难以破解的NP困难问题。这就是LWE问题。&lt;/p>
&lt;h3 id="ring-lwe问题rlwe">Ring-LWE问题（RLWE）
&lt;/h3>&lt;p>标准的LWE问题由于包含矩阵运算，存在密钥尺寸非常大（甚至可能达到千兆字节级别）且计算效率低下的问题。为了解决这一问题，引入了使用多项式环上运算的&lt;strong>Ring-LWE（RLWE）问题&lt;/strong>。&lt;/p>
&lt;p>在RLWE中，元素属于多项式环 $R_q = \mathbb{Z}_q[x] / (x^N + 1)$（其中 $N$ 是2的幂，$q$ 是作为模的素数）。
设私钥为多项式 $s(x)$，一个随机多项式为 $a(x)$，小噪声多项式为 $e(x)$，那么公钥就是以下对：&lt;/p>
$$ (a(x), b(x)) \quad \text{where} \quad b(x) = -a(x) \cdot s(x) + e(x) \pmod q $$
&lt;p>在加密时，将利用这种多项式的性质对明文 $m(x)$ 进行编码，并生成密文。&lt;/p>
&lt;hr>
&lt;h2 id="4-最大障碍噪声与gentry的自举bootstrapping">4. 最大障碍“噪声”与Gentry的自举（Bootstrapping）
&lt;/h2>&lt;p>在理解FHE时，最重要的概念就是**“噪声管理”**。&lt;/p>
&lt;p>在基于LWE/RLWE的密码中，为了保证安全性，会有意加入较小的“噪声（误差）”。
将明文 $m$ 的密文 $c$ 解密的过程，粗略地说，可以用以下公式表示：&lt;/p>
$$ D(c) = (c \cdot s) \pmod q = m + \text{noise} $$
&lt;p>在解密时，通过舍入等操作去除这个 &lt;code>noise&lt;/code> 即可得到正确的明文 $m$。然而，一旦在密文之间进行同态运算（尤其是乘法），这种噪声就会急剧放大。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>同态加法&lt;/strong>: 噪声以加法的方式增加（$e_1 + e_2$）。这种增加相对较慢。&lt;/li>
&lt;li>&lt;strong>同态加法的同态性数学表示&lt;/strong>:
$$ E(m_1) \oplus E(m_2) = E(m_1 + m_2) $$&lt;/li>
&lt;li>&lt;strong>同态乘法&lt;/strong>: 噪声呈爆炸性倍乘（因为它包含了 $e_1 \times e_2$ 等项）。仅仅经过几次乘法运算，噪声就会超过阈值 $q/2$，导致无法进行正确的舍入处理，从而解密失败。&lt;/li>
&lt;li>&lt;strong>同态乘法的同态性数学表示&lt;/strong>:
$$ E(m_1) \otimes E(m_2) = E(m_1 \times m_2) $$&lt;/li>
&lt;/ul>
&lt;p>这就是FHE长期无法实现，只能停留在SHE（次数受限）阶段的原因。&lt;/p>
&lt;h3 id="自举bootstrapping的魔力">自举（Bootstrapping）的魔力
&lt;/h3>&lt;p>Craig Gentry的天才贡献在于他发明了一种被称为**“自举（Bootstrapping）”**的降噪技术。这是密码学领域的一次范式转变。&lt;/p>
&lt;p>直观上，这个操作就是“在密文充满噪声而崩溃之前，在保持加密的状态下对其进行‘解密’清理，然后重新放入一个新密文中”。&lt;/p>
&lt;ol>
&lt;li>假设有一个噪声变得很大的密文 $C_{noisy}$。&lt;/li>
&lt;li>客户端预先将使用公钥“加密后的私钥 $sk$” $E_{pk}(sk)$（称之为自举密钥）发送给服务器。&lt;/li>
&lt;li>服务器对 $C_{noisy}$ 执行同态的&lt;strong>解密电路（Decryption Circuit）&lt;/strong>。&lt;/li>
&lt;li>具体来说，就是利用 $E_{pk}(sk)$ 对 $E_{pk}(C_{noisy})$ 进行“在加密空间内的解密”。&lt;/li>
&lt;li>尽管这个解密电路本身也是同态运算，会产生新的噪声，但其输出的新密文 $C_{fresh}$ 的噪声会被重置为一个特定的“固定水平”。&lt;/li>
&lt;/ol>
&lt;div class="mermaid">graph LR
A["高噪声密文 C_noisy"] --> B["同态解密电路 (Eval_Dec)"]
C["加密后的私钥 E(sk)"] --> B
B --> D["低噪声密文 C_fresh"]
style B fill:#ffeeba,stroke:#ffc107&lt;/div>
&lt;p>通过在计算过程中定期执行这种自举操作，理论上就可以计算无限深度的电路（实现了FHE）。然而，在Gentry最初的方案中，这种自举处理一次需要数十分钟甚至数小时，计算成本高得令人绝望。&lt;/p>
&lt;hr>
&lt;h2 id="5-fhe的世代与主要方案的演进">5. FHE的世代与主要方案的演进
&lt;/h2>&lt;p>为了使FHE走向实用化，全世界的密码学家们竞相改进算法。如今，FHE主要可分为四个世代或家族。&lt;/p>
&lt;h3 id="第二代整数的精确运算-bgv-bfv">第二代：整数的精确运算 (BGV, BFV)
&lt;/h3>&lt;p>这是在2011年至2012年间出现的 &lt;strong>BGV（Brakerski-Gentry-Vaikuntanathan）&lt;/strong> 和 &lt;strong>BFV（Brakerski/Fan-Vercauteren）&lt;/strong> 方案。它们以RLWE为基础，适合进行整数的模运算（精确计算）。
其特点是支持类似SIMD（单指令多数据）的批处理（Batching）技术，可以将数千个数据槽塞入一个巨大的多项式密文中，从而一次性进行并行计算。&lt;/p>
&lt;h3 id="第三代加速自举-gsw-fhew-tfhe">第三代：加速自举 (GSW, FHEW, TFHE)
&lt;/h3>&lt;p>2013年的 &lt;strong>GSW（Gentry-Sahai-Waters）&lt;/strong> 方案使FHE的结构变得更加简单。在其基础上发展而来的，便是当今的主流方案之一：&lt;strong>TFHE（Fast Fully Homomorphic Encryption over the Torus）&lt;/strong>。
TFHE的特点在于其自举速度极快（毫秒级）。它擅长在门级（如AND、XOR等逻辑电路）进行运算，且密文尺寸相对较小，非常适合快速评估任意逻辑电路。&lt;/p>
&lt;h3 id="第四代专注于近似计算与机器学习-ckks">第四代：专注于近似计算与机器学习 (CKKS)
&lt;/h3>&lt;p>2017年由Cheon等人提出的 &lt;strong>CKKS（Cheon-Kim-Kim-Song）&lt;/strong> 方案，可以说是当前AI与机器学习隐私保护领域的终极技术。
相较于以往的FHE固执于“精确的整数计算”，CKKS支持在加密状态下进行**“浮点数的近似计算”**。在神经网络训练或推理等允许微小误差的实数计算中，它展现出压倒性的性能优势。&lt;/p>
&lt;p>下表总结了根据不同目的选择方案的方法。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:left">方案名称&lt;/th>
&lt;th style="text-align:left">擅长的数据类型&lt;/th>
&lt;th style="text-align:left">推荐的使用用例&lt;/th>
&lt;th style="text-align:left">特点&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>BFV / BGV&lt;/strong>&lt;/td>
&lt;td style="text-align:left">整数 (Integer)&lt;/td>
&lt;td style="text-align:left">精确的统计计算、金融数据汇总、数据库搜索&lt;/td>
&lt;td style="text-align:left">通过SIMD批处理实现高吞吐量&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>CKKS&lt;/strong>&lt;/td>
&lt;td style="text-align:left">实数 (Real/Complex)&lt;/td>
&lt;td style="text-align:left">机器学习 (DNN, 逻辑回归)、信号处理&lt;/td>
&lt;td style="text-align:left">利用近似计算实现加速、重缩放&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>TFHE&lt;/strong>&lt;/td>
&lt;td style="text-align:left">布尔值 (Boolean)&lt;/td>
&lt;td style="text-align:left">任意逻辑电路、字符串搜索、非线性函数评估&lt;/td>
&lt;td style="text-align:left">超高速自举（毫秒级）&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="6-实践fhe库及概念性代码">6. 实践：FHE库及概念性代码
&lt;/h2>&lt;p>如今，有许多开源库可供使用，即使没有深厚的密码学知识，也可以轻松利用FHE。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Microsoft SEAL (Simple Encrypted Arithmetic Library)&lt;/strong>：支持BFV、BGV和CKKS的C++库，是行业标准之一。其Python绑定版本 &lt;strong>TenSEAL&lt;/strong> 在AI工程师中非常受欢迎。&lt;/li>
&lt;li>&lt;strong>Zama (Concrete)&lt;/strong>：基于TFHE的框架。可以使用Rust/Python编写，并提供了将现有的PyTorch模型编译并运行在FHE上的功能（Concrete ML）。&lt;/li>
&lt;li>&lt;strong>OpenFHE&lt;/strong>：PALISADE的继任者，支持所有主要方案的综合性C++库。&lt;/li>
&lt;/ul>
&lt;h3 id="使用pythontenseal进行fhe编程的示例">使用Python（TenSEAL）进行FHE编程的示例
&lt;/h3>&lt;p>在这里，我们展示一段使用CKKS方案，在加密状态下对实数向量进行加法和乘法运算的概念性Python代码示例。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">tenseal&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">ts&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 上下文设置 (包含密钥生成)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 使用CKKS方案，将多项式的次数设置为8192&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">context&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">SCHEME_TYPE&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">CKKS&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">poly_modulus_degree&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">8192&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">coeff_mod_bit_sizes&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">60&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">40&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">40&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">60&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">context&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">generate_galois_keys&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">context&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">global_scale&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="o">**&lt;/span>&lt;span class="mi">40&lt;/span> &lt;span class="c1"># 实数的缩放因子&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. 客户端：数据加密&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">vector1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mf">1.5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">2.5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">3.5&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">vector2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mf">2.0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">3.0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">4.0&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 将明文向量转换为密文 (原本应该在客户端执行)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">enc_v1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ckks_vector&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vector1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">enc_v2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ckks_vector&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vector2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. 服务器端：在加密状态下进行运算 (保护Data in Use)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 服务器不知道明文，但可以执行加法和乘法&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">enc_add&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">enc_v1&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">enc_v2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">enc_mul&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">enc_v1&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">enc_v2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 4. 客户端：结果解密&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 只有拥有私钥的客户端才能看到结果&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">res_add&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">enc_add&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">decrypt&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">res_mul&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">enc_mul&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">decrypt&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;解密后的加法结果: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">res_add&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 输出示例: [3.5000001, 5.5000001, 7.5000002] (因为是近似计算，会包含微小误差)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;解密后的乘法结果: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">res_mul&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 输出示例: [3.0000002, 7.5000005, 14.0000003]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>正如上述代码所示，可以通过像 &lt;code>enc_v1 + enc_v2&lt;/code> 这样重载常规Python运算符，直观地编写密文之间的计算。在服务器端，它可以在完全不知道向量内容的情况下完成向量运算。&lt;/p>
&lt;hr>
&lt;h2 id="7-fhe的挑战性能与硬件加速">7. FHE的挑战：性能与硬件加速
&lt;/h2>&lt;p>FHE在理论上提供了完美的安全性，但其实用化面临的最大挑战是**“性能开销”**。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>计算开销&lt;/strong>：与明文计算相比，密文计算在CPU上的速度要慢几千倍甚至几万倍。多项式的乘法以及自举过程都需要进行大量的FFT（快速傅里叶变换）或NTT（数论变换）计算。&lt;/li>
&lt;li>&lt;strong>数据大小膨胀 (Ciphertext Expansion)&lt;/strong>：只需几个字节的明文，加密后就可能变成数兆字节。这会严重挤占内存带宽和网络带宽。&lt;/li>
&lt;/ol>
&lt;h3 id="硬件解决方案的探索">硬件解决方案的探索
&lt;/h3>&lt;p>为了克服这一开销，世界各地正在研发FHE专用的硬件加速器（支持ASIC、FPGA、GPU等）。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>GPU加速&lt;/strong>：正在利用NVIDIA等强大的GPU，对NTT运算和自举进行并行化的尝试。据报道，其速度相比纯软件实现提升了数十倍（例如：100x.ai，Zama的TFHE-rs CUDA backend）。&lt;/li>
&lt;li>&lt;strong>DARPA DPRIVE 项目&lt;/strong>：美国国防高级研究计划局（DARPA）正在推进旨在将FHE的计算速度提升至与明文处理执行速度同等水平（开销控制在10倍以内）的专用硬件开发项目“DPRIVE (Data Protection in Virtual Environments)”，Intel、Microsoft、Intellectual Ventures等公司均有参与。&lt;/li>
&lt;li>&lt;strong>FPU (FHE Processing Unit) 的出现&lt;/strong>：Cornami、Optalysys等初创公司正着手研发使用光计算或特殊硅架构的FHE专用芯片。&lt;/li>
&lt;/ul>
&lt;p>在不久的将来，也许会迎来如同AI领域中NPU（Neural Processing Unit）一般，服务器或云基础设施标配“FPU”的时代。&lt;/p>
&lt;hr>
&lt;h2 id="8-期待的应用用例">8. 期待的应用用例
&lt;/h2>&lt;p>随着FHE逐渐达到实用的速度，它有望在以下领域带来颠覆性的创新。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>医疗与基因组分析的隐私保护&lt;/strong>：
通过对多家医院拥有的患者病历数据和DNA数据使用FHE加密，并在云端交由AI进行训练，可以在不违反隐私法规（如HIPAA和GDPR）的前提下，开展高精度的癌症诊断模型或新药研发。&lt;/li>
&lt;li>&lt;strong>金融机构的欺诈检测与反洗钱 (AML)&lt;/strong>：
相互竞争的银行之间无需透露客户的账户信息或交易记录，即可在加密状态下比对彼此的数据，从而实现跨行分析，以检测庞大的非法汇款网络。&lt;/li>
&lt;li>&lt;strong>安全的AI推理API (MaaS: Model as a Service)&lt;/strong>：
用户可以将自己的声音、面部图像或提示词加密后发送给AI服务（如ChatGPT等LLM）。AI提供商在完全不知道用户输入内容的情况下生成回答，并以密文形式返回。这将完全消除“AI会学习/窥探个人信息”的担忧。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="9-结论密码学的未来走向不可见计算">9. 结论：密码学的未来走向“不可见计算”
&lt;/h2>&lt;p>正如20世纪70年代公钥加密（RSA）的发明使得互联网上的安全通信（如HTTPS）成为可能一样，Craig Gentry发明的FHE是密码学历史上最重要的里程碑之一。&lt;/p>
&lt;p>如今，完全同态加密（FHE）已经走出实验室理论的范畴，进入了由Microsoft、IBM、Intel、Google以及众多初创企业为了实用化而展开激烈竞争的阶段。尽管仍面临计算成本和数据大小等挑战，但得益于算法的精进和硬件加速器的演进，其性能正以超越摩尔定律的速度持续提升。&lt;/p>
&lt;p>在几年后，“对加密数据进行计算”将不再是一件特别的事情，而将成为云服务中标准数据保护的最佳实践。在数据驱动型社会中，FHE能够实现&lt;strong>极致隐私保护与数据充分利用的完美结合&lt;/strong>，它无疑是下一代安全的核心。&lt;/p></description></item><item><title>椭圆曲线密码学（ECC）的数学基础与C++实现</title><link>http://kenji.blog/zh-cn/p/elliptic-curve-cryptography-math-cpp/</link><pubDate>Fri, 11 Sep 2026 10:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/p/elliptic-curve-cryptography-math-cpp/</guid><description>&lt;img src="http://kenji.blog/p/elliptic-curve-cryptography-math-cpp/img/eyecatch.jpg" alt="Featured image of post 椭圆曲线密码学（ECC）的数学基础与C++实现" />&lt;h1 id="椭圆曲线密码学ecc的数学基础与c实现">椭圆曲线密码学（ECC）的数学基础与C++实现
&lt;/h1>&lt;p>在现代密码技术中，**椭圆曲线密码学（Elliptic Curve Cryptography: ECC）**发挥着极其重要的作用。从我们日常的互联网通信（HTTPS/TLS）、智能手机的安全隔离区（Secure Enclave）、通过SSH进行的服务器身份验证、FIDO等无密码认证，甚至到比特币和以太坊等加密资产，可以说现代数字社会的信任基础正是由ECC支撑的。&lt;/p>
&lt;p>本文将从其背后的优美且深奥的数学理论（有限体上的代数几何学）出发，深入探讨椭圆曲线密码学是如何运作的，并结合C++的实际实现方法，乃至防止侧信道攻击（计时攻击）的安全编码技巧，以压倒性的篇幅为您进行彻底的讲解。&lt;/p>
&lt;hr>
&lt;h2 id="1-为什么选择椭圆曲线密码学与rsa的比较">1. 为什么选择椭圆曲线密码学？（与RSA的比较）
&lt;/h2>&lt;p>说到公钥密码体制的代名词，长期以来一直非&lt;strong>RSA密码&lt;/strong>莫属。RSA密码的安全性基于“大分解合成数的质因数分解的困难性”。然而，随着计算机计算能力的提高，为了维持安全性，必须不断增加RSA的密钥长度（模数的位数）。目前，建议的密钥长度至少为2048位，如果要求更安全，则推荐使用3072位或4096位。&lt;/p>
&lt;p>相比之下，椭圆曲线密码学（ECC）的安全基础是另一种数学困难性，即**“椭圆曲线上的离散对数问题（ECDLP）”**。直到现在，尚未发现能够有效解决ECDLP的算法（如亚指数时间算法），即使是已知最有效的攻击方法也需要指数级的时间。&lt;/p>
&lt;p>正是因为这一特性，ECC具有一个决定性的优势：&lt;strong>能够在极短的密钥长度下实现与RSA同等的安全强度&lt;/strong>。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:center">安全强度（位）&lt;/th>
&lt;th style="text-align:center">RSA密码的密钥长度（位）&lt;/th>
&lt;th style="text-align:center">椭圆曲线密码的密钥长度（位）&lt;/th>
&lt;th style="text-align:center">密钥长度比率&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:center">80&lt;/td>
&lt;td style="text-align:center">1024&lt;/td>
&lt;td style="text-align:center">160&lt;/td>
&lt;td style="text-align:center">1:6&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:center">112&lt;/td>
&lt;td style="text-align:center">2048&lt;/td>
&lt;td style="text-align:center">224&lt;/td>
&lt;td style="text-align:center">1:9&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:center">128&lt;/td>
&lt;td style="text-align:center">3072&lt;/td>
&lt;td style="text-align:center">256&lt;/td>
&lt;td style="text-align:center">1:12&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:center">192&lt;/td>
&lt;td style="text-align:center">7680&lt;/td>
&lt;td style="text-align:center">384&lt;/td>
&lt;td style="text-align:center">1:20&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:center">256&lt;/td>
&lt;td style="text-align:center">15360&lt;/td>
&lt;td style="text-align:center">512&lt;/td>
&lt;td style="text-align:center">1:30&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>如上表所示，为了获得128位的安全强度（目前的标准强度），RSA需要3072位的密钥，而ECC仅需256位。这使得减少计算量、降低内存使用量和节约网络带宽成为可能，特别是在资源有限的物联网设备和智能卡环境中，ECC拥有压倒性的优势。&lt;/p>
&lt;hr>
&lt;h2 id="2-数学准备群论与有限体世界">2. 数学准备：群论与有限体世界
&lt;/h2>&lt;p>为了真正理解椭圆曲线密码学，必须掌握抽象代数（群论和域论）的基本概念。在此，我们简要总结构建ECC所需的前提知识。&lt;/p>
&lt;h3 id="21-群group与阿贝尔群">2.1. 群（Group）与阿贝尔群
&lt;/h3>&lt;p>**群（Group）**是指由一个集合 $G$ 及其上的二元运算（此处假设为加法 $+$）组成的集合 $(G, +)$，且满足以下四个公理：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>封闭性（Closure）&lt;/strong>: 对于任意的 $a, b \in G$，$a + b \in G$ 成立。&lt;/li>
&lt;li>&lt;strong>结合律（Associativity）&lt;/strong>: 对于任意的 $a, b, c \in G$，$(a + b) + c = a + (b + c)$ 成立。&lt;/li>
&lt;li>&lt;strong>单位元的存在性（Identity element）&lt;/strong>: 存在元素 $e \in G$，使得对于任意的 $a \in G$，$a + e = e + a = a$ 成立。在加法群中，该单位元通常记为 $0$ 或 $\mathcal{O}$。&lt;/li>
&lt;li>&lt;strong>逆元的存在性（Inverse element）&lt;/strong>: 对于任意的 $a \in G$，存在元素 $b \in G$，使得 $a + b = b + a = e$。该 $b$ 记作 $-a$。&lt;/li>
&lt;/ol>
&lt;p>此外，如果改变运算顺序结果不变，即满足以下条件的群被称为&lt;strong>阿贝尔群（交换群）&lt;/strong>：&lt;/p>
&lt;ol start="5">
&lt;li>&lt;strong>交换律（Commutativity）&lt;/strong>: 对于任意的 $a, b \in G$，$a + b = b + a$ 成立。&lt;/li>
&lt;/ol>
&lt;p>椭圆曲线上的点集，通过定义特定的加法规则，构成了这种&lt;strong>阿贝尔群&lt;/strong>。&lt;/p>
&lt;h3 id="22-有限体finite-field">2.2. 有限体（Finite Field）
&lt;/h3>&lt;p>在密码学理论中，我们使用的不是像实数或复数那样具有连续且无限元素的域，而是元素数量有限的**有限体（Finite Field）**或称伽罗瓦域（Galois Field）。&lt;/p>
&lt;p>最基本的有限体是使用素数 $p$ 构成的&lt;strong>素体 $\mathbb{F}_p$&lt;/strong>。它是在整数集合 $\{0, 1, 2, \dots, p-1\}$ 上，定义了模 $p$（除以 $p$ 的余数）的四则运算（加法、减法、乘法、除法）。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>加法&lt;/strong>: $(a + b) \pmod p$&lt;/li>
&lt;li>&lt;strong>减法&lt;/strong>: $(a - b) \pmod p$&lt;/li>
&lt;li>&lt;strong>乘法&lt;/strong>: $(a \times b) \pmod p$&lt;/li>
&lt;li>&lt;strong>除法&lt;/strong>: $a \times b^{-1} \pmod p$ （这里 $b^{-1}$ 是模 $p$ 下 $b$ 的乘法逆元）&lt;/li>
&lt;/ul>
&lt;p>**乘法逆元（Modular Multiplicative Inverse）**的计算在密码学实现中极为重要。为了求出满足 $b \times b^{-1} \equiv 1 \pmod p$ 的 $b^{-1}$，主要使用以下两种算法：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>扩展欧几里得算法（Extended Euclidean Algorithm）&lt;/strong>: 速度快，但根据实现的不同，处理时间可能依赖于输入值，从而存在计时攻击的风险。&lt;/li>
&lt;li>&lt;strong>费马小定理（Fermat&amp;rsquo;s Little Theorem）&lt;/strong>: 当 $p$ 为素数且 $b \neq 0$ 时，$b^{p-1} \equiv 1 \pmod p$ 成立。两边同除以 $b$，可得 $b^{p-2} \equiv b^{-1} \pmod p$。也就是说，通过计算 $b$ 的 $p-2$ 次方即可求得逆元。因为幂运算容易实现为常数时间操作，所以密码学实现中通常更倾向于使用这种方法。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="3-椭圆曲线的方程与几何学">3. 椭圆曲线的方程与几何学
&lt;/h2>&lt;h3 id="31-魏尔斯特拉斯标准型">3.1. 魏尔斯特拉斯标准型
&lt;/h3>&lt;p>**椭圆曲线（Elliptic Curve）&lt;strong>通常由以下被称为&lt;/strong>魏尔斯特拉斯标准型（Weierstrass normal form）**的方程所定义的平面曲线：&lt;/p>
$$ y^2 = x^3 + ax + b $$
&lt;p>在这里，$a$ 和 $b$ 是常数。为了确保曲线没有奇异点（自交点或尖点）（即这是一条平滑曲线），要求以下&lt;strong>判别式（Discriminant） $\Delta$&lt;/strong> 不为零：&lt;/p>
$$ \Delta = -16(4a^3 + 27b^2) \neq 0 $$
&lt;p>带有奇异点的曲线会破坏密码学的安全性，因此必须选择满足该条件的系数 $a, b$。&lt;/p>
&lt;h3 id="32-无穷远点point-at-infinity">3.2. 无穷远点（Point at Infinity）
&lt;/h3>&lt;p>为了使椭圆曲线在数学上构成一个完备的群，除了平面上的点之外，我们引入了一个称为**“无穷远点（Point at Infinity）”**的虚拟点。记作 $\mathcal{O}$（字母 O）。&lt;/p>
&lt;p>无穷远点 $\mathcal{O}$ 被定义为所有垂直线在无穷远处的交点。在群论中，这个无穷远点 $\mathcal{O}$ 作为&lt;strong>加法中的单位元&lt;/strong>（零）发挥作用。&lt;/p>
&lt;p>也就是说，对于曲线上的任意点 $P$，以下等式成立：
&lt;/p>
$$ P + \mathcal{O} = \mathcal{O} + P = P $$
&lt;p>另外，点 $P = (x, y)$ 的逆元 $-P$，定义为关于 x 轴对称的点 $(x, -y)$。因此：
&lt;/p>
$$ P + (-P) = \mathcal{O} $$
&lt;p>
成立。&lt;/p>
&lt;hr>
&lt;h2 id="4-椭圆曲线上的群运算点的加法与二倍算">4. 椭圆曲线上的群运算（点的加法与二倍算）
&lt;/h2>&lt;p>椭圆曲线密码学的核心在于曲线上点与点之间的**“加法（Addition）”**运算。这不同于普通整数的加法，它是基于几何操作来定义的。&lt;/p>
&lt;h3 id="41-几何加法切线与割线法--tangent-and-chord-method">4.1. 几何加法（切线与割线法 / Tangent and Chord Method）
&lt;/h3>&lt;p>将曲线上不同的两点 $P$ 和 $Q$ 相加，求得新点 $R$ ($R = P + Q$) 的步骤如下：&lt;/p>
&lt;ol>
&lt;li>画一条穿过点 $P$ 和点 $Q$ 的直线（割线）。&lt;/li>
&lt;li>根据代数几何学的定理，这条直线必然与椭圆曲线相交于第三个点（我们称之为 $-R$）。&lt;/li>
&lt;li>将交点 $-R$ 关于 x 轴对称翻转（即 y 坐标的符号反转）得到的点，就是我们所求的点 $R$。&lt;/li>
&lt;/ol>
&lt;div class="mermaid">graph TD
Step1["画一条连接 P(x1, y1) 和 Q(x2, y2) 的直线"] --> Step2["找到与曲线的第三个交点 -R"]
Step2 --> Step3["将 -R 沿 x 轴翻转，得到 R(x3, y3)"]
Step3 -.-> Result["这就是 R = P + Q"]&lt;/div>
&lt;h3 id="42-点的二倍算point-doubling">4.2. 点的二倍算（Point Doubling）
&lt;/h3>&lt;p>如果要在点 $P$ 上加上相同的点 $P$（$P + P = 2P$），我们无法画出穿过两点的直线。在这种情况下，我们要画出&lt;strong>曲线在点 $P$ 处的切线（Tangent）&lt;/strong>。&lt;/p>
&lt;ol>
&lt;li>画出曲线在点 $P$ 处的切线。&lt;/li>
&lt;li>该切线将与曲线相交于另一个点 $-R$。&lt;/li>
&lt;li>将交点关于 x 轴对称翻转得到的点，就是所求的点 $R = 2P$。&lt;/li>
&lt;/ol>
&lt;h3 id="43-代数计算公式">4.3. 代数计算公式
&lt;/h3>&lt;p>我们需要将几何操作转化为代数公式，以便计算机可以进行计算。
所有的运算都在**有限体 $\mathbb{F}_p$ 上（模 $p$）**进行。&lt;/p>
&lt;p>设点 $P = (x_1, y_1)$，点 $Q = (x_2, y_2)$。
并且设计算结果的点为 $R = P + Q = (x_3, y_3)$。&lt;/p>
&lt;p>设直线的斜率为 $\lambda$（Lambda）。&lt;/p>
&lt;p>&lt;strong>【情况1：当 $P \neq Q$ 时（点的加法）】&lt;/strong>
斜率 $\lambda$ 是两点间的变化率。
&lt;/p>
$$ \lambda \equiv \frac{y_2 - y_1}{x_2 - x_1} \pmod p $$
$$ \lambda \equiv (y_2 - y_1) \cdot (x_2 - x_1)^{-1} \pmod p $$
&lt;p>利用这个 $\lambda$，$x_3, y_3$ 可以如下求出：
&lt;/p>
$$ x_3 \equiv \lambda^2 - x_1 - x_2 \pmod p $$
$$ y_3 \equiv \lambda(x_1 - x_3) - y_1 \pmod p $$
&lt;p>&lt;strong>【情况2：当 $P = Q$ 时（点的二倍算）】&lt;/strong>
斜率 $\lambda$ 即通过求导得到的切线斜率。（隐式对 $y^2 = x^3 + ax + b$ 求导）
&lt;/p>
$$ 2y \cdot y' = 3x^2 + a \implies y' = \frac{3x^2 + a}{2y} $$
&lt;p>
因此，
&lt;/p>
$$ \lambda \equiv (3x_1^2 + a) \cdot (2y_1)^{-1} \pmod p $$
&lt;p>$x_3, y_3$ 的公式形式与加法相同，但由于 $x_2 = x_1$，结果如下：
&lt;/p>
$$ x_3 \equiv \lambda^2 - 2x_1 \pmod p $$
$$ y_3 \equiv \lambda(x_1 - x_3) - y_1 \pmod p $$
&lt;blockquote>
&lt;p>[!IMPORTANT]
这些公式包含了形如 $(x_2 - x_1)^{-1}$ 或 $(2y_1)^{-1}$ 的&lt;strong>除法（模逆元的计算）&lt;/strong>。模逆元的计算成本非常高，因此在实际实现中，通常会使用诸如**“雅可比坐标系（Jacobian Coordinates）”**等射影坐标系来延迟除法运算。&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="5-标量乘法与椭圆曲线离散对数问题ecdlp">5. 标量乘法与椭圆曲线离散对数问题（ECDLP）
&lt;/h2>&lt;p>在椭圆曲线密码学中，计算量最大且构成安全核心的运算是&lt;strong>标量乘法（Scalar Multiplication）&lt;/strong>。&lt;/p>
&lt;h3 id="51-什么是标量乘法">5.1. 什么是标量乘法
&lt;/h3>&lt;p>将某一点 $P$ 相加 $k$ 次的操作称为标量乘法，记作 $kP$。
&lt;/p>
$$ kP = \underbrace{P + P + \dots + P}_{k\text{次}} $$
&lt;p>在这里，$k$ 是一个非常大的整数（例如 256 位的整数）。&lt;/p>
&lt;h3 id="52-椭圆曲线离散对数问题ecdlp">5.2. 椭圆曲线离散对数问题（ECDLP）
&lt;/h3>&lt;p>椭圆曲线密码学的安全性取决于以下问题的困难性：&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>椭圆曲线离散对数问题 (Elliptic Curve Discrete Logarithm Problem: ECDLP)&lt;/strong>
给定已知点 $P$（基点）和计算结果点 $Q$，求满足 $Q = kP$ 的标量 $k$。&lt;/p>
&lt;/blockquote>
&lt;p>利用后文所述的算法，由 $k$ 和 $P$ 计算 $Q$（正向）是很简单的（多项式时间），但由 $P$ 和 $Q$ 逆推 $k$（反向）除了暴力穷举外没有有效的解法，实际上是不可能的（单向函数）。
在密码协议中，&lt;strong>$k$ 对应“私钥”，$Q$ 对应“公钥”&lt;/strong>。&lt;/p>
&lt;h3 id="53-double-and-add-算法">5.3. Double-and-Add 算法
&lt;/h3>&lt;p>如果 $k$ 是一个巨大的数（例如 $2^{256}$），那么就算宇宙寿命耗尽，也无法完成将 $P$ 愚直地相加 $k$ 次的操作。因此，为了快速执行标量乘法，我们使用了&lt;strong>Double-and-Add 方法（二进制法）&lt;/strong>。&lt;/p>
&lt;p>这是计算整数快速幂的“重复平方求幂法”的椭圆曲线版本。将标量 $k$ 表示为二进制数，然后从高位到低位依次处理。&lt;/p>
&lt;ol>
&lt;li>将保存结果的点 $R$ 初始化为 $\mathcal{O}$。&lt;/li>
&lt;li>从 $k$ 的最高位到最低位重复以下操作：
&lt;ul>
&lt;li>将 $R$ 翻倍（Point Doubling: $R = 2R$）&lt;/li>
&lt;li>如果当前位为 &lt;code>1&lt;/code>，则在 $R$ 上加上 $P$（Point Addition: $R = R + P$）&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>通过该算法，计算量从 $O(k)$ 剧烈减少到 $O(\log_2 k)$，从而使得在现实时间（毫秒级）内完成计算成为可能。&lt;/p>
&lt;hr>
&lt;h2 id="6-椭圆曲线迪菲-赫尔曼ecdh密钥交换">6. 椭圆曲线迪菲-赫尔曼（ECDH）密钥交换
&lt;/h2>&lt;p>在此，我们将讲解 ECC 最典型的应用案例——&lt;strong>ECDH（Elliptic Curve Diffie-Hellman）密钥交换协议&lt;/strong>的机制。ECDH 是爱丽丝和鲍勃在可能被窃听的通信信道上，安全地生成并共享共同私钥（会话密钥）的机制（这也是 TLS 握手的核心）。&lt;/p>
&lt;p>&lt;strong>【前提参数（域参数）】&lt;/strong>
双方预先共享了所使用的椭圆曲线 $E$、素数 $p$ 以及基点 $G$。（例如 NIST P-256 或 secp256k1 等）&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant Alice as "Alice (爱丽丝)"
participant Bob as "Bob (鲍勃)"
Note over Alice,Bob: "公开参数: 曲线 E, 基点 G, 素数 p"
Alice->>Alice: "生成私钥 d_A (随机整数)"
Alice->>Alice: "计算公钥 Q_A = d_A * G"
Bob->>Bob: "生成私钥 d_B (随机整数)"
Bob->>Bob: "计算公钥 Q_B = d_B * G"
Alice->>Bob: "发送公钥 Q_A (明文)"
Bob->>Alice: "发送公钥 Q_B (明文)"
Alice->>Alice: "计算共享密钥 S = d_A * Q_B"
Bob->>Bob: "计算共享密钥 S = d_B * Q_A"
Note over Alice,Bob: "S = d_A * (d_B * G) = d_B * (d_A * G) = (d_A * d_B) * G"
Note over Alice,Bob: "双方的计算结果 S 完全一致！"&lt;/div>
&lt;p>窃听者（伊芙）可以截获通信路径上流动的 $G$、$Q_A$、$Q_B$，但由于 ECDLP 的困难性，无法从 $Q_A = d_A \cdot G$ 推导得出爱丽丝的私钥 $d_A$。此外，即使将 $Q_A$ 和 $Q_B$ 相乘也不会得到共享密钥 $S$，因此窃听者无法计算出 $S$。&lt;/p>
&lt;hr>
&lt;h2 id="7-实现中的陷阱侧信道攻击及其对策">7. 实现中的陷阱：侧信道攻击及其对策
&lt;/h2>&lt;p>即使在理论上是完美的密码算法，在将其实现为程序的过程中，也有可能产生漏洞。这就是**“侧信道攻击（Side-Channel Attack）”**。&lt;/p>
&lt;h3 id="71-计时攻击timing-attack">7.1. 计时攻击（Timing Attack）
&lt;/h3>&lt;p>让我们回顾一下前面提到的 Double-and-Add 算法。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 脆弱的 Double-and-Add 伪代码
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">Point&lt;/span> &lt;span class="n">R&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Infinity&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">255&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">R&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PointDoubling&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// 总是执行
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">bit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">R&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PointAddition&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// 只有当位为 1 时才执行！
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这个实现存在一个致命的缺陷。由于只有当位为 &lt;code>1&lt;/code> 时才会执行 Point Addition，所以当位为 &lt;code>1&lt;/code> 时所需的&lt;strong>计算时间会比位为 &lt;code>0&lt;/code> 时稍微长一些&lt;/strong>。此外，处理器的分支预测和缓存内存的行为也会发生变化。
攻击者通过对这微小的计算时间（或功耗）差异进行数千次的统计观测，就可以&lt;strong>逐位完全还原出私钥 $k$ 的二进制序列&lt;/strong>。这就是计时攻击。&lt;/p>
&lt;h3 id="72-constant-time恒定时间实现montgomery-ladder">7.2. Constant-Time（恒定时间）实现：Montgomery Ladder
&lt;/h3>&lt;p>为了防止计时攻击，我们需要采用一种无论**私钥的位值如何，执行的指令序列和计算时间始终保持不变（Constant-Time）**的算法。&lt;/p>
&lt;p>其代表性的例子就是&lt;strong>蒙哥马利阶梯（Montgomery Ladder）&lt;/strong>。&lt;/p>
&lt;div class="mermaid">graph TD
Start["初始化: R0 = O, R1 = P"] --> LoopStart["对于每个位 i (从最高位开始)"]
LoopStart --> Cond{"k_i 的值是？"}
Cond -->|0| Branch0["R1 = R0 + R1&lt;br>R0 = 2 * R0"]
Cond -->|1| Branch1["R0 = R0 + R1&lt;br>R1 = 2 * R1"]
Branch0 --> LoopEnd["进入下一位"]
Branch1 --> LoopEnd
LoopEnd --> LoopStart
LoopStart -.->|"全部位完成"| End["结束: R0 即为结果 (kP)"]&lt;/div>
&lt;p>Montgomery Ladder 的优美之处在于，无论位是 &lt;code>0&lt;/code> 还是 &lt;code>1&lt;/code>，都会**“始终执行一次 Point Addition 和一次 Point Doubling”**。这样就完全排除了计算时间对数据的依赖性。&lt;/p>
&lt;p>然而，如果代码中存在分支（&lt;code>if (k_i == 0)&lt;/code>）本身，那么由于编译器的优化或CPU的分支预测，执行时间仍然有波动的风险。因此，在实际的恒定时间实现中，我们会消除条件分支（&lt;code>if&lt;/code>语句），转而使用&lt;strong>基于位运算的条件交换（Conditional Swap）&lt;/strong>。&lt;/p>
&lt;hr>
&lt;h2 id="8-使用-c-实现椭圆曲线密码学">8. 使用 C++ 实现椭圆曲线密码学
&lt;/h2>&lt;p>从这里开始，我们将把理论转化为 C++ 代码。虽然实际实用的密码学库（如 OpenSSL 或 libsodium 等）使用了高度的汇编优化和雅可比坐标系，但在这里为了加深对数学的理解，我们将展示一个&lt;strong>使用仿射坐标系且易于理解的 Constant-Time 实现&lt;/strong>的基础框架。&lt;/p>
&lt;p>假设大整数运算使用 &lt;code>boost::multiprecision::cpp_int&lt;/code>。&lt;/p>
&lt;h3 id="81-模运算与逆元">8.1. 模运算与逆元
&lt;/h3>&lt;p>首先，定义有限体上的运算辅助函数。实现基于费马小定理的逆元计算。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;vector&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stdexcept&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;boost/multiprecision/cpp_int.hpp&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">boost&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">multiprecision&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 以 secp256k1 的素数 p 和参数为例
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="nf">p&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">const&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">const&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">7&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 返回正余数的模运算
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">cpp_int&lt;/span> &lt;span class="nf">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cpp_int&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">r&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">r&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="n">r&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nl">m&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">r&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 模幂运算 (x^y mod m)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">cpp_int&lt;/span> &lt;span class="nf">powerMod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cpp_int&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">exp&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">exp&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">exp&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">res&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">base&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">exp&lt;/span> &lt;span class="o">/=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">res&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 使用费马小定理的模逆元
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">cpp_int&lt;/span> &lt;span class="nf">modInverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cpp_int&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 前提 m 是素数: n^(m-2) ≡ n^(-1) mod m
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="n">powerMod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="82-点的表示与群运算加法二倍算">8.2. 点的表示与群运算（加法・二倍算）
&lt;/h3>&lt;p>通过标志位管理无穷远点的 &lt;code>Point&lt;/code> 结构体，并实现加法公式。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">struct&lt;/span> &lt;span class="nc">Point&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">bool&lt;/span> &lt;span class="n">isInfinity&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 生成无穷远点
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">isInfinity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">true&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 生成普通点
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cpp_int&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">isInfinity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">false&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 椭圆曲线上点的加法 (R = P + Q)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">Point&lt;/span> &lt;span class="nf">pointAdd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">Q&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isInfinity&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="n">Q&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isInfinity&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="c1">// P + (-P) = 无穷远点
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">lambda&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Point Doubling (当 P = Q 时)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// lambda = (3x^2 + a) / 2y
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">num&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">den&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">modInverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lambda&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">num&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">den&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Point Addition (当 P != Q 时)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// lambda = (y2 - y1) / (x2 - x1)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">num&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">den&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">modInverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lambda&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">num&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">den&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">x3&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">lambda&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">lambda&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">y3&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">lambda&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">x3&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y3&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="83-constant-time-条件交换conditional-swap的实现">8.3. Constant-Time 条件交换（Conditional Swap）的实现
&lt;/h3>&lt;p>当根据私钥的位值交换变量内容时，不使用 &lt;code>if&lt;/code> 语句，而是仅使用位运算（掩码）来进行交换。这样执行路径就会完全保持一致。&lt;/p>
&lt;blockquote>
&lt;p>[!TIP]
在实际实现中，像 &lt;code>cpp_int&lt;/code> 这样动态分配的多精度整数类并不适合 Constant-Time 处理。因为内存分配和数组大小的变化会泄露计时信息。在实用库中，使用固定长度（例如 uint64_t × 4 个元素的数组）来表示，并实现位级别的掩码处理。以下是概念性示例。&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 概念性的 Constant-Time Swap (假设使用定长整数)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 如果 bit 为 1，则交换 P1 和 P2；如果为 0，则不交换
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">void&lt;/span> &lt;span class="nf">cswap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Point&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">P1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">P2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint8_t&lt;/span> &lt;span class="n">bit&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// bit 为 0 或 1。如果 bit=1，则掩码全为 1 (0xFF..)；为 0，则全为 0。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// (为了便于说明，这里假设定长 BigInt 类的每个字为 w)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="cm">/*
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> uint64_t mask = 0 - (uint64_t)bit;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> for (int i = 0; i &amp;lt; NUM_WORDS; i++) {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> uint64_t dummy = mask &amp;amp; (P1.x.words[i] ^ P2.x.words[i]);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> P1.x.words[i] ^= dummy;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> P2.x.words[i] ^= dummy;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> // y 坐标和 isInfinity 标志位也进行相同处理
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> }
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ※ 尽管使用 boost::multiprecision 实现完全的恒定时间交换很困难，
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// 但在这里，为了理解逻辑，我们仅通过分支语句进行模拟。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">bit&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">P1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">P2&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="84-使用-montgomery-ladder-的标量乘法">8.4. 使用 Montgomery Ladder 的标量乘法
&lt;/h3>&lt;p>结合前文所述的 &lt;code>pointAdd&lt;/code> 和 &lt;code>cswap&lt;/code>，实现安全的标量乘法。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 标量乘法 k * P (Montgomery Ladder 方式)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">Point&lt;/span> &lt;span class="nf">scalarMultiply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Point&lt;/span> &lt;span class="n">R0&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="c1">// 无穷远点
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">Point&lt;/span> &lt;span class="n">R1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 获取 k 的位长 (若是 secp256k1 则为 256 位)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">numBits&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">256&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">numBits&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 获取第 i 位的值 (0 或 1)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">uint8_t&lt;/span> &lt;span class="n">bit&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">static_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">uint8_t&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bit_test&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 如果 bit == 1，则交换 R0 和 R1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">cswap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">R1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">bit&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 总是执行相同的运算 (Point Addition 和 Point Doubling)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">R1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">pointAdd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">R1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">R0&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">pointAdd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">R0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 如果 bit == 1，为了恢复状态，再次进行交换
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">cswap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">R1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">bit&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">R0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>通过这一实现逻辑，无论标量 $k$ 的每一位是 &lt;code>0&lt;/code> 还是 &lt;code>1&lt;/code>，在每次循环迭代内执行的运算（&lt;code>cswap&lt;/code> $\to$ &lt;code>pointAdd&lt;/code> $\to$ &lt;code>pointAdd&lt;/code> $\to$ &lt;code>cswap&lt;/code>）流程完全相同，可以有效防止通过计时和缓存访问模式差异而导致的机密信息泄露。&lt;/p>
&lt;hr>
&lt;h2 id="9-总结">9. 总结
&lt;/h2>&lt;p>初看椭圆曲线密码学（ECC），可能会让人感到奇怪：“为什么画条直线并翻转交点这种几何操作能成为密码呢？”然而，将其映射到有限体这一离散世界中，就可以构建出极佳的单向函数（离散对数问题），这正是数学与密码学奇迹般融合的产物。&lt;/p>
&lt;p>本文讲解了以下几个要点：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>相对 RSA 的优势&lt;/strong>: 能在非常短的密钥长度下提供强大的安全性，是现代移动互联网和物联网时代的最佳选择。&lt;/li>
&lt;li>&lt;strong>群论与有限体基础&lt;/strong>: 构成 ECC 基础的数学结构。&lt;/li>
&lt;li>&lt;strong>加法和二倍算公式&lt;/strong>: 使用魏尔斯特拉斯方程实现代数群运算的方法。&lt;/li>
&lt;li>&lt;strong>侧信道攻击的威胁&lt;/strong>: 依赖于私钥位值的条件分支会导致致命漏洞。&lt;/li>
&lt;li>&lt;strong>Constant-Time（恒定时间）实现&lt;/strong>: 使用 Montgomery Ladder 和 Conditional Swap 来均衡硬件级别的行为，从而防范攻击的 C++ 编码技术。&lt;/li>
&lt;/ol>
&lt;p>实际上，因为极高的安全风险，业界不建议（&amp;ldquo;Don&amp;rsquo;t roll your own crypto&amp;rdquo;）自行编写用于生产环境的密码学库。然而，深入理解其内部运行的算法和数学背景，对于设计和运维更安全、高性能系统的工程师来说，必定是一件不可或缺的强大武器。&lt;/p>
&lt;p>在下一篇文章中，我们将进一步深入探讨使用这种椭圆曲线的数字签名算法——&lt;strong>ECDSA（Elliptic Curve Digital Signature Algorithm）&lt;/strong> 的机制，以及比特币中所采用的 &lt;strong>Schnorr 签名&lt;/strong>。&lt;/p></description></item><item><title>是否存在超越GNFS（普通数域筛选法）的算法？</title><link>http://kenji.blog/zh-cn/p/beyond-gnfs-integer-factorization-algorithms/</link><pubDate>Fri, 11 Sep 2026 09:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/p/beyond-gnfs-integer-factorization-algorithms/</guid><description>&lt;img src="http://kenji.blog/p/beyond-gnfs-integer-factorization-algorithms/img/eyecatch.jpg" alt="Featured image of post 是否存在超越GNFS（普通数域筛选法）的算法？" />&lt;h2 id="1-引言整数分解与现代密码学的根基">1. 引言：整数分解与现代密码学的根基
&lt;/h2>&lt;p>现代社会中互联网通信的安全性在很大程度上依赖于公开密钥密码体制RSA的安全性。而RSA密码的安全性是基于“大合数分解难度”这一数学假设。如果发现了极其高效的整数分解算法，全球的通信基础设施将从根本上崩溃。&lt;/p>
&lt;p>目前，在使用经典计算机分解大整数时，**普通数域筛选法（GNFS: General Number Field Sieve）**作为最快、最强的算法占据着统治地位。GNFS是在1980年代后期提出的特殊数域筛选法（SNFS）的扩展基础上诞生的，直到今天，它依然保持着分解RSA-768和RSA-250等巨大合数的记录。&lt;/p>
&lt;p>然而，密码学家和数学家们始终抱有如下疑问：“是否存在超越GNFS的经典算法？”“经典计算机的极限在哪里？”以及，“量子计算机将如何打破这一僵局？”&lt;/p>
&lt;p>本文将深入剖析GNFS背后的深邃数学结构，并对多项式选择、筛选处理、以及利用Block Wiedemann方法的线性代数步骤等进行详细的技术分析。此外，还将探讨通过Coppersmith改进等方法对GNFS进行扩展的技术，并从数学角度比较和解释经典次指数时间（Sub-exponential time）算法与量子多项式时间算法之间的决定性差异。&lt;/p>
&lt;hr>
&lt;h2 id="2-渐进复杂度与l记号l-notation">2. 渐进复杂度与L记号（L-notation）
&lt;/h2>&lt;p>在评估整数分解算法的计算复杂度时，标准的多项式时间表示（如 $O(n^k)$ 等）不再适用，为了表示相对于输入 $n$ 的位数的次指数时间，我们使用&lt;strong>L记号（L-notation）&lt;/strong>。L记号的定义如下：&lt;/p>
$$
L_n[\alpha, c] = \exp \left( (c + o(1)) (\ln n)^\alpha (\ln \ln n)^{1-\alpha} \right)
$$
&lt;p>在这里，$n$ 是待分解的整数，$\ln n$ 是自然对数，与 $n$ 的位长成正比。&lt;/p>
&lt;ul>
&lt;li>当 $\alpha = 0$ 时：$L_n[0, c] = \exp(c \ln \ln n) = (\ln n)^c$，表示相对于位长的&lt;strong>多项式时间（Polynomial time）&lt;/strong>。&lt;/li>
&lt;li>当 $\alpha = 1$ 时：$L_n[1, c] = \exp(c \ln n) = n^c$，表示相对于位长的&lt;strong>指数时间（Exponential time）&lt;/strong>。&lt;/li>
&lt;li>当 $0 &lt; \alpha &lt; 1$ 时：介于多项式时间和指数时间之间，称为&lt;strong>次指数时间（Sub-exponential time）&lt;/strong>。&lt;/li>
&lt;/ul>
&lt;p>过去整数分解算法的进化史，同时也是这个 $\alpha$ 值逐渐减小的历史。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>连分数法（CFRAC）与多重多项式二次筛选法（MPQS）&lt;/strong>：属于 $\alpha = 1/2$ 的类别，计算复杂度约为 $L_n[1/2, 1]$。&lt;/li>
&lt;li>&lt;strong>普通数域筛选法（GNFS）&lt;/strong>：实现了 $\alpha = 1/3$，在目前已知的经典算法中，它拥有最快的 $L_n[1/3, (64/9)^{1/3}]$ 计算复杂度。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="3-gnfs普通数域筛选法算法全貌与数学结构">3. GNFS（普通数域筛选法）算法全貌与数学结构
&lt;/h2>&lt;p>GNFS拥有非常复杂且高级的数学基础。其基本思想是费马小定理和二次筛选法（QS）的延伸，即寻找满足同余式 $X^2 \equiv Y^2 \pmod n$ 且 $X \not\equiv \pm Y \pmod n$ 的非平凡组合 $(X, Y)$，从而推导出 $n$ 的因子 $\gcd(X-Y, n)$。&lt;/p>
&lt;p>然而，GNFS的精髓在于，它不仅仅在有理数域 $\mathbb{Q}$ 中进行计算，而是同时在称为代数数域（Algebraic Number Field）的扩域 $\mathbb{Q}(\alpha)$ 和有理数域中寻找“光滑数（Smooth numbers）”，并通过同态映射构建同余关系。&lt;/p>
&lt;p>GNFS的过程大致分为5个阶段。&lt;/p>
&lt;div class="mermaid">graph TD
A["素数分解问题 (输入 n)"] --> B["1. 多项式选择 (Polynomial Selection)"]
B --> C["2. 筛选处理 (Sieving Phase)"]
C --> D["3. 过滤处理 (Filtering Phase)"]
D --> E["4. 线性代数 (Linear Algebra Phase)"]
E --> F["5. 平方根 (Square Root Phase)"]
F --> G["输出素因子 p, q"]&lt;/div>
&lt;h3 id="31-阶段1多项式选择-polynomial-selection">3.1 阶段1：多项式选择 (Polynomial Selection)
&lt;/h3>&lt;p>GNFS的成功在很大程度上取决于如何选择合适的多项式。目标是找到具有公共根 $m$ 的两个不可约多项式 $f_1(x)$（有理侧）和 $f_2(x)$（代数侧）。也就是说，满足：
$f_1(m) \equiv f_2(m) \equiv 0 \pmod n$&lt;/p>
&lt;p>通常，有理侧的多项式选择一次方程 $f_1(x) = x - m$，而代数侧的多项式 $f_2(x)$ 选择一个次数为 $d$（通常为5或6）的首一多项式。最经典的方法是&lt;strong>Base-$m$ 方法&lt;/strong>。
选择一个接近 $n$ 的 $1/(d+1)$ 次方的整数 $m = \lfloor n^{1/(d+1)} \rfloor$，并将 $n$ 展开为 $m$ 进制形式：
$n = c_d m^d + c_{d-1} m^{d-1} + \dots + c_1 m + c_0$
由此可以得到多项式 $f_2(x) = c_d x^d + c_{d-1} x^{d-1} + \dots + c_0$。显然 $f_2(m) = n \equiv 0 \pmod n$。&lt;/p>
&lt;p>但是在现代实现中，使用的是 &lt;strong>Kleinjung算法&lt;/strong>。这是一种在避免多项式系数过大（优化偏度，Skewness）的同时，优化代数性质（Murphy&amp;rsquo;s $E$ value 或 $\alpha$-value），寻找能在筛选阶段更容易生成光滑数的多项式的算法。仅仅是这一步骤就会投入大量的计算资源。&lt;/p>
&lt;h3 id="32-阶段2筛选处理-sieving-phase">3.2 阶段2：筛选处理 (Sieving Phase)
&lt;/h3>&lt;p>多项式确定后，算法进入计算负载最高的“筛选（Sieving）”阶段。在这里，我们需要寻找数据对 $(a, b)$。这个数据对必须是互素的，并且要求以下两个值同时为“光滑（Smooth）”：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>有理侧的范数&lt;/strong>: $F_1(a, b) = b \cdot f_1(a/b) = a - bm$&lt;/li>
&lt;li>&lt;strong>代数侧的范数&lt;/strong>: $F_2(a, b) = b^d \cdot f_2(a/b)$&lt;/li>
&lt;/ol>
&lt;p>“光滑”意味着该数只能被指定的上限（Sieve bound）以下的素数分解。我们会准备一个有理侧的素数基（Factor base）和一个代数侧的素数基，并在巨大的搜索空间内，像埃拉托斯特尼筛法一样高效地找出光滑数。
目前的主流方法被称为&lt;strong>格筛选（Lattice Sieving）&lt;/strong>。通过固定某个特定的素数 $q$，仅对使有理侧和代数侧同时为 $q$ 的倍数的子格上的 $(a, b)$ 对进行筛选，从而实现了极高的效率。&lt;/p>
&lt;h3 id="33-阶段3过滤处理-filtering-phase">3.3 阶段3：过滤处理 (Filtering Phase)
&lt;/h3>&lt;p>在筛选阶段找到的光滑关系式（Relations）数量将高达数亿甚至数十亿。但其中也包含了大量无用的信息。
过滤的目的是在构建巨大的稀疏矩阵（Sparse Matrix）的同时，尽可能地缩小其维度。&lt;/p>
&lt;p>具体进行如下操作：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Singleton removal（单例移除）&lt;/strong>: 删除包含只出现过一次的素因子的关系式。&lt;/li>
&lt;li>&lt;strong>Clique removal / Merging（团移除/合并）&lt;/strong>: 将包含出现两次以上素因子的关系式相互相乘，消去变量，将其压缩为密度更高但维度更小的方程组。&lt;/li>
&lt;/ul>
&lt;p>通过这一过程，数十亿行的矩阵被压缩为数千万行级别的巨大稀疏矩阵 $\mathbf{A}$（元素为域 $\mathbb{F}_2$ 上的0和1）。&lt;/p>
&lt;h3 id="34-阶段4线性代数-linear-algebra-phase">3.4 阶段4：线性代数 (Linear Algebra Phase)
&lt;/h3>&lt;p>在这一步中，我们需要找到方程 $\mathbf{A} \mathbf{x} \equiv \mathbf{0} \pmod 2$ 的非平凡解向量 $\mathbf{x}$。也就是求解该巨大稀疏矩阵的左零空间（Left Nullspace）的问题。&lt;/p>
&lt;p>由于矩阵规模过大，使用常规的高斯消元法（$O(N^3)$）根本无法进行计算。因此，这里使用了属于克雷洛夫子空间法（Krylov subspace method）的一种迭代法。历史上曾使用&lt;strong>Block Lanczos方法（块兰佐斯法）&lt;/strong>，而在现代的分布式计算环境中，能够大幅减少通信开销的**Block Wiedemann方法（块维德曼法）**成为了主流。&lt;/p>
&lt;p>Block Wiedemann方法通过计算矩阵 $\mathbf{A}$ 与向量序列的最小多项式，并使用Berlekamp-Massey算法构造零空间的基。这一步极难实现并行化，需要超级计算机或大规模集群的紧耦合通信网络，是GNFS中最大的瓶颈之一。&lt;/p>
&lt;h3 id="35-阶段5平方根-square-root-phase">3.5 阶段5：平方根 (Square Root Phase)
&lt;/h3>&lt;p>利用线性代数求得的解，我们可以在有理侧和代数侧分别构造出“完全平方”的乘积。
在有理侧，$\prod (a-bm)$ 会成为某个整数 $X$ 的平方 $X^2$；在代数侧，对应的理想（Ideal）的乘积在代数数域上成为完全平方 $\gamma^2$。
在代数数域上计算出这个 $\gamma$，并通过映射到有理整数环的同态映射 $\phi: \alpha \mapsto m \pmod n$，我们可以得到同余式：
$X^2 \equiv \phi(\gamma)^2 \equiv Y^2 \pmod n$&lt;/p>
&lt;p>计算代数数域上的平方根时，使用了**Montgomery方法（Montgomery&amp;rsquo;s Method）**等复杂算法，这需要代数数论的深厚知识。最终，通过计算 $\gcd(X-Y, n)$，如果能得到非平凡的因子，因数分解即告完成。&lt;/p>
&lt;hr>
&lt;h2 id="4-是否存在超越gnfs的经典算法">4. 是否存在超越GNFS的经典算法？
&lt;/h2>&lt;p>迄今为止，对于一般整数的因数分解，尚未发现渐进复杂度低于 $L_n[1/3, c]$ 的经典算法。不过，为了突破理论与实践的局限，存在一些尝试与衍生算法。&lt;/p>
&lt;h3 id="41-多重数域筛选法-mnfs-multiple-number-field-sieve">4.1 多重数域筛选法 (MNFS: Multiple Number Field Sieve)
&lt;/h3>&lt;p>作为对GNFS的扩展，D. Coppersmith提出了&lt;strong>多重数域筛选法（MNFS）&lt;/strong>。GNFS使用了两个多项式（有理侧和代数侧），而MNFS则对一个有理侧多项式同时使用多个不同的代数侧多项式。&lt;/p>
$$ f_1(x), f_{2,1}(x), f_{2,2}(x), \dots, f_{2,V}(x) $$
&lt;p>通过利用多个代数数域，可以在每次筛选步骤中极大地提高“在任一代数数域中变光滑”的概率。Coppersmith通过这种方法成功地稍微减小了计算复杂度 $L_n[1/3, c]$ 中的常数 $c$。
具体而言，GNFS的常数为 $c = (64/9)^{1/3} \approx 1.923$，而通过优化MNFS，理论上可以将其计算复杂度缩减至 $c \approx 1.902$ 左右。
然而，在实际应用中，管理多个数域带来的开销很大，因此尚未在实际规模的RSA模数上取得决定性的突破。&lt;/p>
&lt;h3 id="42-l_n14-级别的算法可能存在吗">4.2 $L_n[1/4]$ 级别的算法可能存在吗？
&lt;/h3>&lt;p>关于整数分解经典算法的极限，数学家们长年探讨的一个课题是：“是否存在指数 $\alpha = 1/4$ 的算法？”
目前的GNFS及其衍生算法强烈受限于基于筛选“寻找光滑数”的框架，在这个范式内，人们普遍认为 $\alpha = 1/3$ 就是极限。从使用Dickman函数（Dickman function）分析光滑整数分布概率的结果来看，结合目前的代数数域构造方法与筛选，无论怎么优化都不可能突破 $O(L_n[1/3])$ 的壁垒。&lt;/p>
&lt;p>如果真的存在 $L_n[1/4]$ 或经典的纯多项式时间算法，那它必然依赖于与GNFS这种“基于光滑数”的方法截然不同、且目前人类难以想象的全新数学结构（例如，类似于针对椭圆曲线密码学的Schoof算法那样更高阶的代数几何方法等）。然而，目前还没有出现这种迹象。&lt;/p>
&lt;hr>
&lt;h2 id="5-量子计算机的突破shor算法">5. 量子计算机的突破：Shor算法
&lt;/h2>&lt;p>虽然经典计算机面临着 $L_n[1/3]$ 的壁垒，但通过彻底改变计算模型本身而粉碎这一壁垒的，正是Peter Shor于1994年发表的&lt;strong>Shor算法（Shor&amp;rsquo;s Algorithm）&lt;/strong>。&lt;/p>
&lt;h3 id="51-量子多项式时间的冲击">5.1 量子多项式时间的冲击
&lt;/h3>&lt;p>Shor算法将素数分解问题归约为“求阶问题（Order Finding Problem）”。即对于某个整数 $a$，寻找函数 $f(x) = a^x \pmod n$ 的周期（阶） $r$ 的问题。
在经典计算机上找到这个周期需要指数级的时间，但在量子计算机上使用&lt;strong>量子相位估计（QPE: Quantum Phase Estimation）&lt;strong>和&lt;/strong>量子傅里叶变换（QFT: Quantum Fourier Transform）&lt;/strong>，可以对所有状态的叠加态（Superposition）进行并行评估，从而以很高的概率提取出周期 $r$。&lt;/p>
&lt;p>从计算复杂度的角度来看，Shor算法的执行时间属于&lt;strong>量子多项式时间&lt;/strong>，具体如下：
&lt;/p>
$$ O((\log n)^3) $$
&lt;p>
考虑到近年来优化的电路实现，据说可以将其缩减至 $O((\log n)^2 \log \log n)$。&lt;/p>
&lt;div class="mermaid">graph LR
A["经典算法 (GNFS)"] -->|极限| B["次指数时间 L_n[1/3]"]
C["量子算法 (Shor)"] -->|突破| D["多项式时间 O((log n)^3)"]
B --> E["RSA密码的继续使用 (增加密钥长度)"]
D --> F["RSA密码的彻底崩溃"]&lt;/div>
&lt;h3 id="52-经典次指数时间-vs-量子多项式时间">5.2 经典次指数时间 vs 量子多项式时间
&lt;/h3>&lt;p>这两个复杂度类别之间的差距，在现实世界的密码安全中具有决定性的意义。&lt;/p>
&lt;p>例如，考虑分解RSA-2048（2048位的合数）的情况。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>GNFS (经典)&lt;/strong>: 将 $n \approx 2^{2048}$ 代入 $L_n[1/3, 1.923]$，大约需要 $2^{112}$ 次运算。这是一个即使集合目前地球上所有计算资源，也需要超过宇宙寿命时间的天文学计算量。&lt;/li>
&lt;li>&lt;strong>Shor&amp;rsquo;s Algorithm (量子)&lt;/strong>: 对于 $O((\log n)^3)$ 的算法，仅需大约 $2048^3 \approx 8.5 \times 10^9$ 次逻辑门操作。这意味着如果存在合适的硬件（具备数百万物理量子比特和纠错能力的通用量子计算机），只需数小时到数天即可完成计算。&lt;/li>
&lt;/ul>
&lt;p>从“指数 $\alpha=1/3$”的次指数时间向“多项式时间”的范式转移，使得传统密码学中通过延长密钥长度来保证安全性的策略完全失效。&lt;/p>
&lt;hr>
&lt;h2 id="6-总结下一代展望">6. 总结：下一代展望
&lt;/h2>&lt;p>关于“是否存在超越GNFS的经典算法？”这一问题，目前科学界的共识如下：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>实际的改进仍在继续，但不会有渐进意义上的飞跃&lt;/strong>: 虽然对优化多项式选择、并行化Block Wiedemann方法以及MNFS等尝试仍在继续（试图改善GNFS的常数项 $c$），但人们认为发现 $\alpha = 1/3$ 以下的经典算法的可能性微乎其微。&lt;/li>
&lt;li>&lt;strong>在经典计算机上，RSA的安全性依然非常坚固&lt;/strong>: GNFS的计算量依然是极其庞大的，RSA-2048和RSA-4096在面对经典计算机的攻击时，未来几十年内仍将保持安全。&lt;/li>
&lt;li>&lt;strong>真正的威胁是量子算法&lt;/strong>: 跨越计算复杂度壁垒的是基于量子力学原理的Shor算法。这也迫使世界开始向抗量子计算机密码学（PQC: Post-Quantum Cryptography）过渡。转向即使是量子计算机也难以破解（无法在多项式时间内求解）的全新数学问题，如格密码（Lattice-based cryptography）和基于哈希的密码（Hash-based cryptography），已成为当前密码学的最前沿。&lt;/li>
&lt;/ol>
&lt;p>普通数域筛选法（GNFS）是人类挑战经典数学和算法设计极限所达到的“最高境界”之一。理解GNFS深邃的数学结构，不仅是为了学习密码破译的历史，更是一段触及计算复杂性理论和代数数论之美的知识探索之旅。在量子计算机真正实现实用化之前，GNFS无疑将继续捍卫其作为最强整数分解算法的王座。&lt;/p></description></item><item><title>在Python中模拟秀尔算法</title><link>http://kenji.blog/zh-cn/p/shors-algorithm-simulation-python/</link><pubDate>Fri, 11 Sep 2026 08:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/p/shors-algorithm-simulation-python/</guid><description>&lt;img src="http://kenji.blog/p/shors-algorithm-simulation-python/img/eyecatch.jpg" alt="Featured image of post 在Python中模拟秀尔算法" />&lt;h1 id="1-引言量子计算机带来的密码危机">1. 引言：量子计算机带来的密码危机
&lt;/h1>&lt;p>现代互联网社会的大部分安全性都依赖于&lt;strong>公钥密码体制&lt;/strong>（尤其是RSA加密）。当我们在网上购物发送信用卡信息，或交换高度机密的数据时，这些通信内容都受到RSA加密的坚固保护。&lt;/p>
&lt;p>RSA加密安全性的基础依赖于一个数学事实：“&lt;strong>使用经典计算机（我们日常使用的PC或超级计算机）对巨大的整数进行质因数分解是极其困难的&lt;/strong>”。然而，彼得·秀尔（Peter Shor）在1994年发表的“&lt;strong>秀尔算法（Shor&amp;rsquo;s Algorithm）&lt;/strong>”从根本上颠覆了这一前提。在数学上已经证明，如果在大型量子计算机上运行秀尔算法，可以在短短几分钟到几个小时内解开经典计算机需要花费超过宇宙年龄的时间才能完成的质因数分解。&lt;/p>
&lt;p>在本文中，我们将极其详细地讲解秀尔算法如何高速进行质因数分解，从其数学原理到使用Python和量子计算框架&lt;strong>Qiskit&lt;/strong>的具体模拟实现。&lt;/p>
&lt;hr>
&lt;h1 id="2-计算复杂度的剧烈变化从指数级到多项式时间">2. 计算复杂度的剧烈变化：从指数级到多项式时间
&lt;/h1>&lt;p>为什么质因数分解如此困难？即使使用被称为经典计算机中最好的质因数分解算法的“普通数域筛选法（General Number Field Sieve, GNFS）”，其时间复杂度也是次指数级的。&lt;/p>
&lt;p>在经典方法中，对位数为 $N$ 的合数进行质因数分解所需的时间复杂度如下：&lt;/p>
$$ O\left(\exp\left( c (\log N)^{1/3} (\log \log N)^{2/3} \right)\right) $$
&lt;p>因此，只要增加密钥长度（例如增加到2048位或4096位），在经典计算机上的破解就会耗费数千年、数万年这种不切实际的时间。&lt;/p>
&lt;p>但是，如果在量子计算机上使用&lt;strong>秀尔算法&lt;/strong>，计算复杂度将针对输入的位数 $\log N$ 剧烈降低到多项式时间。&lt;/p>
$$ O((\log N)^3) $$
&lt;p>这意味着，如果将位数翻倍，经典计算机的计算时间会呈天文数字般增长，而在量子计算机上，计算时间最多只会增加约8倍。这种&lt;strong>从指数级时间到多项式时间的计算复杂度类的降低（被包含在BQP类中）&lt;/strong>，正是秀尔算法的真正厉害之处。&lt;/p>
&lt;div class="mermaid">graph TD
A["输入大小(位数) N增加"] --> B{"算法选择"}
B -->|经典: 普通数域筛选法| C["次指数级增长 O(exp(...))"]
B -->|量子: 秀尔算法| D["多项式时间 O((log N)^3)"]
C --> E["数千年〜数十亿年 (无法破解)"]
D --> F["几分钟〜几小时 (现实时间内破解)"]&lt;/div>
&lt;hr>
&lt;h1 id="3-算法全貌与数学背景">3. 算法全貌与数学背景
&lt;/h1>&lt;p>秀尔算法实际上并非完全在量子计算机上运行。它是通过经典计算机的前处理与后处理，以及量子计算机的核心部分（周期寻找算法）协同工作而构成的。&lt;/p>
&lt;p>算法的整体流程如下：&lt;/p>
&lt;div class="mermaid">graph TD
A["输入: 想要进行质因数分解的合数 N"] --> B["选择随机数 a 使得 a &lt; N"]
B --> C{"gcd(a, N) > 1 ?"}
C -- "Yes" --> D["输出平凡因子 gcd(a, N) 并结束"]
C -- "No" --> E["使用量子算法寻找 f(x) = a^x mod N 的周期 r"]
E --> F{"r 是偶数 且 a^(r/2) ≢ -1 mod N ?"}
F -- "No" --> B
F -- "Yes" --> G["计算因子 p = gcd(a^(r/2) - 1, N), q = gcd(a^(r/2) + 1, N)"]
G --> H["输出: p, q"]&lt;/div>
&lt;h2 id="从质因数分解归约到周期寻找问题">从质因数分解归约到周期寻找问题
&lt;/h2>&lt;p>秀尔的天才之处在于将“&lt;strong>质因数分解问题&lt;/strong>”转换为了“&lt;strong>周期寻找问题（Order Finding Problem）&lt;/strong>”。&lt;/p>
&lt;p>考虑整数 $N$（想要进行质因数分解的数）和一个互质的整数 $a$（$1 &lt; a &lt; N$）。我们定义如下的模指数函数：&lt;/p>
$$ f(x) = a^x \bmod N $$
&lt;p>这个函数具有某个周期 $r$。也就是说，对于任意的 $x$，都满足 $f(x+r) = f(x)$。特别是在 $x=0$ 时：&lt;/p>
$$ a^r \equiv 1 \pmod N $$
&lt;p>满足这个条件的最小正整数 $r$ 被称为“$a$ 模 $N$ 的阶（Order）”。如果能找到这个周期 $r$，就可以通过以下方式导出质因数。&lt;/p>
&lt;p>对公式进行变形：
&lt;/p>
$$ a^r - 1 \equiv 0 \pmod N $$
&lt;p>
如果 $r$ 是偶数，就可以使用平方差公式进行因式分解：
&lt;/p>
$$ (a^{r/2} - 1)(a^{r/2} + 1) \equiv 0 \pmod N $$
&lt;p>这意味着 $N$ 与 $(a^{r/2} - 1)$ 或 $(a^{r/2} + 1)$ 之一有公约数（但必须满足条件 $a^{r/2} \not\equiv -1 \pmod N$）。因此，使用欧几里得算法计算：&lt;/p>
$$ p = \gcd(a^{r/2} - 1, N) $$
$$ q = \gcd(a^{r/2} + 1, N) $$
&lt;p>就可以找到 $N$ 的非平凡质因数 $p, q$。这个计算（计算最大公约数或生成随机数）在经典计算机上可以非常快速地完成。问题的焦点就集中在&lt;strong>如何高速寻找周期 $r$&lt;/strong> 上。在经典计算机上，寻找这个周期 $r$ 本身就需要指数级的时间。这里就轮到量子计算机出场了。&lt;/p>
&lt;hr>
&lt;h1 id="4-量子算法部分周期寻找的机制">4. 量子算法部分：周期寻找的机制
&lt;/h1>&lt;p>使用量子计算机寻找周期 $r$ 的子程序由以下4个步骤组成：&lt;/p>
&lt;div class="mermaid">graph LR
subgraph "量子状态演化"
S1["|0⟩|0⟩ (初始化)"] --> S2["H门: 叠加态 Σ|x⟩|0⟩"]
S2 --> S3["神谕 U: Σ|x⟩|a^x mod N⟩"]
S3 --> S4["QFT: 通过干涉提取周期"]
S4 --> S5["测量: 获取近似值 y"]
end&lt;/div>
&lt;h2 id="步骤1量子寄存器的初始化与叠加">步骤1：量子寄存器的初始化与叠加
&lt;/h2>&lt;p>首先，准备两个量子寄存器。第1个寄存器用于输入状态，第2个寄存器用于存储函数的计算结果。
初始状态全为 $|0\rangle$。&lt;/p>
$$ |\psi_0\rangle = |0\rangle_1 |0\rangle_2 $$
&lt;p>对第1个寄存器的所有量子比特应用阿达马门（Hadamard Gate），创造出所有可能输入 $x$（从 $0$ 到 $Q-1$，$Q=2^n$）的等概率叠加态。&lt;/p>
$$ |\psi_1\rangle = \frac{1}{\sqrt{Q}} \sum_{x=0}^{Q-1} |x\rangle_1 |0\rangle_2 $$
&lt;p>由此，量子计算机将在一次运算中同时保持所有 $Q$ 个输入的状态。这是&lt;strong>量子并行性&lt;/strong>的强大源泉。&lt;/p>
&lt;h2 id="步骤2应用神谕函数模幂运算">步骤2：应用神谕函数（模幂运算）
&lt;/h2>&lt;p>接下来，使用量子运算电路 $U_f$ 计算函数 $f(x) = a^x \bmod N$，并将其结果存储到第2寄存器中。&lt;/p>
$$ |\psi_2\rangle = \frac{1}{\sqrt{Q}} \sum_{x=0}^{Q-1} |x\rangle_1 |a^x \bmod N\rangle_2 $$
&lt;p>此时，第1寄存器和第2寄存器处于**量子纠缠（Entanglement）**状态。如果（假设）观测第2寄存器并得到了一个特定的值 $k = a^{x_0} \bmod N$，那么第1寄存器的状态将坍缩为给出该值 $k$ 的 $x$ 的叠加态。由于函数的周期是 $r$，剩余的状态将是以 $r$ 为间隔的值，即 $x_0, x_0+r, x_0+2r, \dots$。&lt;/p>
$$ |\psi_3\rangle = \sqrt{\frac{r}{Q}} \sum_{j=0}^{M-1} |x_0 + j r\rangle_1 |k\rangle_2 $$
&lt;p>但是，我们想知道的并不是 $x_0$，而是周期 $r$ 本身。从这个状态直接观测 $r$ 是不可能的。因此，我们使用量子傅里叶变换。&lt;/p>
&lt;h2 id="步骤3通过量子傅里叶变换qft产生相位干涉">步骤3：通过量子傅里叶变换（QFT）产生相位干涉
&lt;/h2>&lt;p>对第1寄存器应用&lt;strong>量子傅里叶变换（Quantum Fourier Transform, QFT）&lt;/strong>。QFT是经典离散傅里叶变换的量子版本，它转换状态向量的振幅。QFT对基态 $|x\rangle$ 的作用定义如下：&lt;/p>
$$ QFT |x\rangle = \frac{1}{\sqrt{Q}} \sum_{y=0}^{Q-1} \omega^{xy} |y\rangle $$
&lt;p>这里，$\omega = e^{2\pi i / Q}$。&lt;/p>
&lt;p>应用QFT后，状态的振幅会产生干涉。省略数学细节，对于具有周期 $r$ 的状态应用QFT时，只有当 $y$ 非常接近 $Q/r$ 的整数倍时，波才会发生&lt;strong>相长干涉（Constructive Interference）&lt;/strong>。对于其他状态，由于&lt;strong>相消干涉（Destructive Interference）&lt;/strong>，概率振幅会相互抵消并趋向于零。&lt;/p>
&lt;h2 id="步骤4测量与连分数展开">步骤4：测量与连分数展开
&lt;/h2>&lt;p>最后测量第1寄存器。通过测量得到的值 $y$，以很高的概率满足以下条件：&lt;/p>
$$ y \approx c \frac{Q}{r} \implies \frac{y}{Q} \approx \frac{c}{r} $$
&lt;p>（$c$ 是 $0 \le c &lt; r$ 的未知整数）&lt;/p>
&lt;p>对得到的有理数 $y/Q$ 应用经典算法&lt;strong>连分数展开（Continued Fraction Expansion）&lt;/strong>，计算出近似分数 $c/r$，然后从分母中提取出周期 $r$。&lt;/p>
&lt;hr>
&lt;h1 id="5-使用python和qiskit的模拟实现">5. 使用Python和Qiskit的模拟实现
&lt;/h1>&lt;p>仅凭理论很难有切实的感受，让我们实际使用Python和IBM的量子计算框架&lt;strong>Qiskit&lt;/strong>来模拟秀尔算法吧。&lt;/p>
&lt;p>在这里，我们将实现最经典著名的例子：&lt;strong>“使用 $a=7$ 对 $N=15$ 进行质因数分解”&lt;/strong> 的场景。&lt;/p>
&lt;h2 id="运行环境准备">运行环境准备
&lt;/h2>&lt;p>请预先安装Qiskit。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">pip install qiskit qiskit-aer numpy
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="python实现代码全貌">Python实现代码全貌
&lt;/h2>&lt;p>以下代码是专为 $N=15, a=7$ 编写的秀尔算法实现示例。由于在当前的模拟器中构建通用的模幂电路计算成本过高，因此我们将特定 $a=7$ 情况下的门操作硬编码了。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt"> 10
&lt;/span>&lt;span class="lnt"> 11
&lt;/span>&lt;span class="lnt"> 12
&lt;/span>&lt;span class="lnt"> 13
&lt;/span>&lt;span class="lnt"> 14
&lt;/span>&lt;span class="lnt"> 15
&lt;/span>&lt;span class="lnt"> 16
&lt;/span>&lt;span class="lnt"> 17
&lt;/span>&lt;span class="lnt"> 18
&lt;/span>&lt;span class="lnt"> 19
&lt;/span>&lt;span class="lnt"> 20
&lt;/span>&lt;span class="lnt"> 21
&lt;/span>&lt;span class="lnt"> 22
&lt;/span>&lt;span class="lnt"> 23
&lt;/span>&lt;span class="lnt"> 24
&lt;/span>&lt;span class="lnt"> 25
&lt;/span>&lt;span class="lnt"> 26
&lt;/span>&lt;span class="lnt"> 27
&lt;/span>&lt;span class="lnt"> 28
&lt;/span>&lt;span class="lnt"> 29
&lt;/span>&lt;span class="lnt"> 30
&lt;/span>&lt;span class="lnt"> 31
&lt;/span>&lt;span class="lnt"> 32
&lt;/span>&lt;span class="lnt"> 33
&lt;/span>&lt;span class="lnt"> 34
&lt;/span>&lt;span class="lnt"> 35
&lt;/span>&lt;span class="lnt"> 36
&lt;/span>&lt;span class="lnt"> 37
&lt;/span>&lt;span class="lnt"> 38
&lt;/span>&lt;span class="lnt"> 39
&lt;/span>&lt;span class="lnt"> 40
&lt;/span>&lt;span class="lnt"> 41
&lt;/span>&lt;span class="lnt"> 42
&lt;/span>&lt;span class="lnt"> 43
&lt;/span>&lt;span class="lnt"> 44
&lt;/span>&lt;span class="lnt"> 45
&lt;/span>&lt;span class="lnt"> 46
&lt;/span>&lt;span class="lnt"> 47
&lt;/span>&lt;span class="lnt"> 48
&lt;/span>&lt;span class="lnt"> 49
&lt;/span>&lt;span class="lnt"> 50
&lt;/span>&lt;span class="lnt"> 51
&lt;/span>&lt;span class="lnt"> 52
&lt;/span>&lt;span class="lnt"> 53
&lt;/span>&lt;span class="lnt"> 54
&lt;/span>&lt;span class="lnt"> 55
&lt;/span>&lt;span class="lnt"> 56
&lt;/span>&lt;span class="lnt"> 57
&lt;/span>&lt;span class="lnt"> 58
&lt;/span>&lt;span class="lnt"> 59
&lt;/span>&lt;span class="lnt"> 60
&lt;/span>&lt;span class="lnt"> 61
&lt;/span>&lt;span class="lnt"> 62
&lt;/span>&lt;span class="lnt"> 63
&lt;/span>&lt;span class="lnt"> 64
&lt;/span>&lt;span class="lnt"> 65
&lt;/span>&lt;span class="lnt"> 66
&lt;/span>&lt;span class="lnt"> 67
&lt;/span>&lt;span class="lnt"> 68
&lt;/span>&lt;span class="lnt"> 69
&lt;/span>&lt;span class="lnt"> 70
&lt;/span>&lt;span class="lnt"> 71
&lt;/span>&lt;span class="lnt"> 72
&lt;/span>&lt;span class="lnt"> 73
&lt;/span>&lt;span class="lnt"> 74
&lt;/span>&lt;span class="lnt"> 75
&lt;/span>&lt;span class="lnt"> 76
&lt;/span>&lt;span class="lnt"> 77
&lt;/span>&lt;span class="lnt"> 78
&lt;/span>&lt;span class="lnt"> 79
&lt;/span>&lt;span class="lnt"> 80
&lt;/span>&lt;span class="lnt"> 81
&lt;/span>&lt;span class="lnt"> 82
&lt;/span>&lt;span class="lnt"> 83
&lt;/span>&lt;span class="lnt"> 84
&lt;/span>&lt;span class="lnt"> 85
&lt;/span>&lt;span class="lnt"> 86
&lt;/span>&lt;span class="lnt"> 87
&lt;/span>&lt;span class="lnt"> 88
&lt;/span>&lt;span class="lnt"> 89
&lt;/span>&lt;span class="lnt"> 90
&lt;/span>&lt;span class="lnt"> 91
&lt;/span>&lt;span class="lnt"> 92
&lt;/span>&lt;span class="lnt"> 93
&lt;/span>&lt;span class="lnt"> 94
&lt;/span>&lt;span class="lnt"> 95
&lt;/span>&lt;span class="lnt"> 96
&lt;/span>&lt;span class="lnt"> 97
&lt;/span>&lt;span class="lnt"> 98
&lt;/span>&lt;span class="lnt"> 99
&lt;/span>&lt;span class="lnt">100
&lt;/span>&lt;span class="lnt">101
&lt;/span>&lt;span class="lnt">102
&lt;/span>&lt;span class="lnt">103
&lt;/span>&lt;span class="lnt">104
&lt;/span>&lt;span class="lnt">105
&lt;/span>&lt;span class="lnt">106
&lt;/span>&lt;span class="lnt">107
&lt;/span>&lt;span class="lnt">108
&lt;/span>&lt;span class="lnt">109
&lt;/span>&lt;span class="lnt">110
&lt;/span>&lt;span class="lnt">111
&lt;/span>&lt;span class="lnt">112
&lt;/span>&lt;span class="lnt">113
&lt;/span>&lt;span class="lnt">114
&lt;/span>&lt;span class="lnt">115
&lt;/span>&lt;span class="lnt">116
&lt;/span>&lt;span class="lnt">117
&lt;/span>&lt;span class="lnt">118
&lt;/span>&lt;span class="lnt">119
&lt;/span>&lt;span class="lnt">120
&lt;/span>&lt;span class="lnt">121
&lt;/span>&lt;span class="lnt">122
&lt;/span>&lt;span class="lnt">123
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">numpy&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">np&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">qiskit&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">QuantumCircuit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">qiskit_aer&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AerSimulator&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">qiskit.visualization&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">plot_histogram&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">fractions&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Fraction&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">math&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 构建逆量子傅里叶变换 (QFT†) 的函数&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">qft_dagger&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;生成n量子比特的逆量子傅里叶变换电路&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">QuantumCircuit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 用于反转顺序的SWAP门&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">qubit&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="o">//&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">qubit&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">qubit&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 应用受控相位门和H门&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pi&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nb">float&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="o">**&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">m&lt;/span>&lt;span class="p">)),&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">j&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">h&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;QFT_dagger&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">qc&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. 构建 7^x mod 15 的受控模幂运算的函数&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">c_amod15&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">power&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;为特定的a和幂生成受控U门 (N=15专用)&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">QuantumCircuit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">_&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">power&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># a=7 时 7^x mod 15 的硬编码逻辑&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">7&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">8&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">7&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">11&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">q&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">to_gate&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">^&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">power&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> mod 15&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">c_U&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">control&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">c_U&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. 主量子电路构建&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">shor_circuit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n_count&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># n_count: 控制寄存器的比特数&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 目标寄存器为了表示 0〜15，需要 4比特&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">QuantumCircuit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_count&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n_count&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 初始化第1寄存器（控制寄存器）（生成叠加态）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">q&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_count&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">h&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 将第2寄存器（目标寄存器）初始化为 |1&amp;gt; (0001)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">n_count&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 应用受控模幂运算（神谕）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">q&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_count&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 应用 2^q 次方的运算&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c_amod15&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="o">**&lt;/span>&lt;span class="n">q&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="n">q&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="n">n_count&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">)])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 对第1寄存器应用逆量子傅里叶变换&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">qft_dagger&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_count&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_count&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 测量第1寄存器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">measure&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_count&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_count&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">qc&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># --- 执行部分 ---&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">N&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">15&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">7&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">n_count&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">8&lt;/span> &lt;span class="c1"># 控制寄存器使用8个量子比特 (Q=256)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;搜索设置: N=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">, a=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">, 控制量子比特数=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">n_count&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 生成电路&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">shor_circuit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n_count&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 在模拟器上运行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sim&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AerSimulator&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 在最新的Qiskit中推荐使用transpile&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">from&lt;/span> &lt;span class="nn">qiskit&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">transpile&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">compiled_circuit&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">transpile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">qc&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sim&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">job&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sim&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">compiled_circuit&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">shots&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1024&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">job&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">counts&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_counts&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">测量结果(比特串: 观测次数):&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">bitstring&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">count&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">counts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">items&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34; &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">bitstring&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">count&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">次&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 经典后处理: 通过连分数展开确定周期r&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">--- 周期计算与质因数分解 ---&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">phases&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">output&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">counts&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 将比特串转换为十进制数&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">decimal&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">output&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 相位 = 测量值 / 2^n_count&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">phase&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">decimal&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="o">**&lt;/span>&lt;span class="n">n_count&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">phases&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">phase&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 通过连分数展开获取近似分数。分母上限为 N=15&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">frac&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Fraction&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">phase&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">limit_denominator&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">15&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">r&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">frac&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">denominator&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;观测值: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">decimal&lt;/span>&lt;span class="si">:&lt;/span>&lt;span class="s2">3d&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> | 相位: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">phase&lt;/span>&lt;span class="si">:&lt;/span>&lt;span class="s2">.4f&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> | 连分数: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">frac&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> | 估计周期 r = &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">r&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 确认周期 r 是否为偶数并能带来有效结果&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">r&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">guess1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">math&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="o">**&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">r&lt;/span>&lt;span class="o">//&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">guess2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">math&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="o">**&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">r&lt;/span>&lt;span class="o">//&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">guess1&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="n">guess2&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34; =&amp;gt; 成功！ &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> 的质因数是 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">guess1&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> 和 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">guess2&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">。&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34; =&amp;gt; 仅得到平凡因子。重试。&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34; =&amp;gt; 周期为奇数，失败。&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="代码解析与执行结果分析">代码解析与执行结果分析
&lt;/h2>&lt;p>运行上述代码，作为控制寄存器的测量结果，将以极高的概率获得特定的峰值（观测值）。在 &lt;code>n_count=8&lt;/code>（$Q=256$）的情况下，如果是理想的量子计算机（或模拟器），观测值如 &lt;code>0&lt;/code>, &lt;code>64&lt;/code>, &lt;code>128&lt;/code>, &lt;code>192&lt;/code> 等出现的概率会非常大。&lt;/p>
&lt;p>将它们除以 $Q=256$，相位 $y/Q$ 分别为 $0.0$, $0.25$, $0.5$, $0.75$。
对这些相位进行连分数展开：&lt;/p>
&lt;ul>
&lt;li>$0.25 \to 1/4$ （估计周期 $r=4$）&lt;/li>
&lt;li>$0.50 \to 1/2$ （估计周期 $r=2$）&lt;/li>
&lt;li>$0.75 \to 3/4$ （估计周期 $r=4$）&lt;/li>
&lt;/ul>
&lt;p>使用这里得到的周期 $r=4$，计算质因数。
因为 $a=7, r=4$，
$p = \gcd(7^2 - 1, 15) = \gcd(48, 15) = 3$
$q = \gcd(7^2 + 1, 15) = \gcd(50, 15) = 5$&lt;/p>
&lt;p>精彩地，成功完成了 $15 = 3 \times 5$ 的质因数分解。&lt;/p>
&lt;blockquote>
&lt;p>[!TIP]
如果观测值为 $y=128$（相位 $0.5$），分母将变为 $2$，从而得到其约数而不是真实周期 $r=4$。在这种情况下，可以通过多次运行算法，或者检查所得到的 $r$ 的倍数来找到真实周期。&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h1 id="6-走向实用化的课题与-nisq-时代的局限">6. 走向实用化的课题与 NISQ 时代的局限
&lt;/h1>&lt;p>虽然在模拟器上可以轻松地对 $N=15$ 进行质因数分解，但要对现实社会中使用的 RSA-2048（617位的十进制数）进行质因数分解，现实的量子计算机还面临许多障碍。&lt;/p>
&lt;p>我们现在所处的时代被称为&lt;strong>NISQ（Noisy Intermediate-Scale Quantum：含噪声中等规模量子）时代&lt;/strong>。量子比特对外部环境的噪声极其敏感，在计算过程中会发生“退相干”导致状态破坏。&lt;/p>
&lt;p>为了准确运行像秀尔算法这样深（门数量多）的电路，必须使用纠正噪声的&lt;strong>量子纠错（Quantum Error Correction）&lt;/strong>。为了创造一个没有噪声的“逻辑量子比特”，需要使用表面码（Surface Code）等编码数千个“物理量子比特”。&lt;/p>
&lt;p>据估计，为了破解2048位的RSA加密，需要数千个完美的逻辑量子比特，而要实现这一点，需要搭载&lt;strong>数百万到数千万个物理量子比特&lt;/strong>的容错量子计算机。即使是当前最先进的量子处理器，也只有几百到几千个物理量子比特的规模，因此全世界的密码不会立即被破解。&lt;/p>
&lt;blockquote>
&lt;p>[!WARNING]
然而，存在一种称为“Store Now, Decrypt Later（现在存储，以后解密）”的威胁模型。攻击者可能会将现在加密的机密通信以加密数据的形式大量保存下来，并在10到20年后强大的量子计算机完成的瞬间，采取将它们全部解密的策略。&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h1 id="7-向抗量子计算密码pqc过渡">7. 向抗量子计算密码（PQC）过渡
&lt;/h1>&lt;p>为了应对这样的“Q-Day（量子计算机破解密码之日）”的到来，以美国国家标准与技术研究院（NIST）为首，全世界的密码学家都在推动**抗量子计算密码（Post-Quantum Cryptography, PQC）**的制定。&lt;/p>
&lt;p>PQC基于新的数学问题（如格问题、多变量多项式问题、基于哈希函数等），在数学上被认为即使使用秀尔算法（或使用格罗弗算法）也无法高效求解。已经有诸如“CRYSTALS-Kyber”和“CRYSTALS-Dilithium”等算法被选为标准规范，并正逐步引入Apple的iMessage和各种Web浏览器的通信协议中。&lt;/p>
&lt;p>对于管理IT基础设施的工程师来说，将系统从现有的RSA或椭圆曲线加密向PQC过渡，并建立“密码敏捷性（Crypto-Agility：能够快速切换加密方式的设计）”，将是未来的重要使命。&lt;/p>
&lt;hr>
&lt;h1 id="8-结语">8. 结语
&lt;/h1>&lt;p>在本文中，我们从秀尔算法的理论数学背景开始，讲解了使用量子傅里叶变换提取周期的机制，以及使用Python和Qiskit的具体模拟代码，以1万字规模的篇幅进行了极其详细的解说。&lt;/p>
&lt;p>量子力学这个微观世界的物理定律，竟然能从根本上颠覆作为宏观信息科学基础的计算复杂性理论和密码学理论，这一事实是科学史上最激动人心的范式转换之一。我们不能将目光从不断发展演进的量子计算技术与应对它的新密码技术的攻防战中移开。&lt;/p>
&lt;p>请务必在您自己的环境中运行这次介绍的Python代码，亲身体验量子态的叠加与干涉所产生的“计算魔法”。&lt;/p>
&lt;hr>
&lt;p>&lt;strong>参考文献&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Shor, P. W. (1994). &amp;ldquo;Algorithms for quantum computation: discrete logarithms and factoring&amp;rdquo;. Proceedings 35th Annual Symposium on Foundations of Computer Science.&lt;/li>
&lt;li>Nielsen, M. A., &amp;amp; Chuang, I. L. (2010). &amp;ldquo;Quantum Computation and Quantum Information&amp;rdquo;. Cambridge University Press.&lt;/li>
&lt;li>Qiskit Documentation: &lt;a class="link" href="https://qiskit.org/documentation/" target="_blank" rel="noopener"
>https://qiskit.org/documentation/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>【图解PQC】抗量子密码学主要算法比较</title><link>http://kenji.blog/zh-cn/p/post-quantum-cryptography-algorithms-comparison/</link><pubDate>Fri, 11 Sep 2026 07:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/p/post-quantum-cryptography-algorithms-comparison/</guid><description>&lt;img src="http://kenji.blog/p/post-quantum-cryptography-algorithms-comparison/img/eyecatch.jpg" alt="Featured image of post 【图解PQC】抗量子密码学主要算法比较" />&lt;h2 id="1-引言量子计算机带来的密码危机">1. 引言：量子计算机带来的“密码危机”
&lt;/h2>&lt;p>在现代互联网社会中，为了保护通信的机密性和数据的完整性，公钥密码技术作为基础设施是不可或缺的。目前广泛使用的RSA加密和椭圆曲线加密（ECC），分别依赖于“大整数素数分解的困难性”和“椭圆曲线上离散对数问题的困难性”等数学壁垒来保证安全性。在经典计算机（包括超级计算机在内的我们目前使用的计算机）上，解开这些数学问题被证明需要比宇宙年龄还要长的时间，这一直是其安全性的依据。&lt;/p>
&lt;p>然而，这一坚固的前提正随着&lt;strong>量子计算机&lt;/strong>的理论与实用化进展而面临被彻底颠覆的危险。1994年，密码学家彼得·秀尔（Peter Shor）发表了“&lt;strong>Shor算法&lt;/strong>”，理论上证明了在具备足够性能的容错通用量子计算机（CRQC: Cryptographically Relevant Quantum Computer）上运行该算法，能够在“多项式时间”内破解素数分解问题和离散对数问题。这意味着目前使用的所有公钥密码都将被使其无效化。&lt;/p>
&lt;div class="mermaid">graph TD
A["大规模量子计算机 (CRQC)"] -->|执行| B["Shor算法"]
B -->|多项式时间内破解| C["素数分解问题 (RSA)"]
B -->|多项式时间内破解| D["离散对数问题 (ECC / ECDSA)"]
C --> E["加密通信的窃听、数据篡改、身份伪造"]
D --> E
F["Store Now, Decrypt Later (SNDL)"] --> E&lt;/div>
&lt;p>认为“真正的量子计算机还需要数十年才能完成，所以没有问题”是非常危险的。因为被称为**Store Now, Decrypt Later (SNDL：现在存储，稍后解密)**的攻击手法已经成为现实的威胁。这是一种恶意国家或黑客组织将当前加密的通信数据（如TLS流量等）大量存储起来，等到将来强大的量子计算机可用时，瞬间将其全部解密的攻击。国家机密、基础设施信息、需要长期保护的医疗数据等，已经暴露在这种威胁之下。&lt;/p>
&lt;p>此外，针对对称密钥加密（如AES）和哈希函数（如SHA-256），1996年发现的&lt;strong>Grover算法&lt;/strong>也同样存在。它能够将暴力破解（穷举攻击）的计算量减少到原来的平方根。也就是说，AES-128的安全级别实际上减半到了2的64次方，因此在量子时代，建议使用AES-256和SHA-384等更长的密钥和哈希长度。&lt;/p>
&lt;p>为了应对这场前所未有的密码危机，基于即便使用量子计算机也难以破解的新数学问题的**抗量子密码学（Post-Quantum Cryptography: PQC）**应运而生。本文将基于美国国家标准与技术研究院（NIST）主导推进的PQC标准化进程的结果，从数学背景、机制到架构比较，极其详尽地解说主要的PQC算法。&lt;/p>
&lt;hr>
&lt;h2 id="2-nist的pqc标准化项目全貌与历史">2. NIST的PQC标准化项目全貌与历史
&lt;/h2>&lt;p>密码技术的迁移，包括协议的重新设计、系统的更新、硬件的更换等，需要数年至数十年的时间。因此，全世界的密码学家们很早就开始了PQC的研究。其中发挥核心作用的是美国的NIST（国家标准与技术研究院）。NIST在2016年公开招募了PQC的标准化流程，接受来自全世界密码学社区的全新密码算法提案。&lt;/p>
&lt;p>标准化的对象主要分为以下两个类别：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>公钥密码 / 密钥封装机制 (KEM: Key Encapsulation Mechanism)&lt;/strong>: 在TLS连接等场景中，为了加密通信路径，安全共享（分发）对称密钥的机制。&lt;/li>
&lt;li>&lt;strong>数字签名 (Digital Signatures)&lt;/strong>: 在软件更新或电子证书中，证明数据未被篡改且发送者身份真实（真实性）的机制。&lt;/li>
&lt;/ol>
&lt;p>经过大约6年非常激烈的评估、分析与密码破解竞争（Round 1〜Round 3），部分算法还进行了Round 4的追加评估。最终结果是，2024年以下算法作为正式的联邦信息处理标准（FIPS）发布，并确立为未来世界的标准。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>FIPS 203 (ML-KEM)&lt;/strong>: 基于CRYSTALS-Kyber的KEM&lt;/li>
&lt;li>&lt;strong>FIPS 204 (ML-DSA)&lt;/strong>: 基于CRYSTALS-Dilithium的数字签名&lt;/li>
&lt;li>&lt;strong>FIPS 205 (SLH-DSA)&lt;/strong>: 基于SPHINCS+的无状态基于哈希的签名&lt;/li>
&lt;li>&lt;strong>（未来计划制定）FN-DSA&lt;/strong>: 基于FALCON的数字签名&lt;/li>
&lt;/ul>
&lt;p>这些被选定的算法所依赖的数学“困难性问题”各不相同，确保了即使某个算法将来被发现存在致命漏洞，整个系统也不会崩溃的多样性（Crypto Agility）。在标准化流程中，基于性能方面的考虑，格密码学（Lattice-based cryptography）成为了主力，而基于哈希的密码和基于编码的密码则作为强大的备份被采用。&lt;/p>
&lt;hr>
&lt;h2 id="3-pqc主要数学方法的分类">3. PQC主要数学方法的分类
&lt;/h2>&lt;p>PQC算法根据其安全性依据的数学问题，主要分为以下五大类。本文将特别深入探讨前三类。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>格密码学 (Lattice-based Cryptography)&lt;/strong>:
基于多维格空间中的最短向量问题（SVP）、最近向量问题（CVP）及其衍生出的LWE问题。它是NIST标准化的核心，Kyber、Dilithium、FALCON均属于此类。处理速度、公钥大小和密文大小的平衡最出色，适合通用场景。&lt;/li>
&lt;li>&lt;strong>基于哈希的密码学 (Hash-based Cryptography)&lt;/strong>:
仅依赖于密码学哈希函数（如SHA-2、SHAKE等）的“抗碰撞性”和“单向性”作为安全性依据。只能应用于数字签名（如SPHINCS+），但其安全性证明最为坚固，对未知的数学攻击具有极高的抵抗力。&lt;/li>
&lt;li>&lt;strong>基于编码的密码学 (Code-based Cryptography)&lt;/strong>:
基于纠错码理论，依赖于伴随式解码问题（Syndrome Decoding Problem）的困难性。代表是20世纪70年代提出的Classic McEliece，拥有悠久的历史和已被验证的安全性，但缺点是公钥尺寸极大，达到兆字节级别。&lt;/li>
&lt;li>&lt;strong>多变量多项式密码学 (Multivariate Polynomial Cryptography)&lt;/strong>:
基于求解有限域上多变量非线性二次方程组（MQ问题）的困难性。主要作为数字签名提出（如Rainbow等），但在NIST最后一轮评估期间，被发现只需一台普通电脑运行几天即可破解的强大攻击手段，导致许多算法退出了标准化。&lt;/li>
&lt;li>&lt;strong>基于同源的密码学 (Isogeny-based Cryptography)&lt;/strong>:
基于椭圆曲线同源（Isogeny）图上的路径搜索问题。密钥尺寸非常小，曾被寄予厚望成为ECC的正统继任者，但最终候选者“SIKE”在2022年被利用经典数学（如Castryck-Decru攻击）在一台普通PC上仅耗时数小时就被完全破解，这场戏剧性的落幕象征着PQC设计的艰难与可怕。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="4-格密码学的深渊lwe问题与module-lwe的数学基础">4. 格密码学的深渊：LWE问题与Module-LWE的数学基础
&lt;/h2>&lt;p>目前最被看好并成为标准化核心的正是&lt;strong>格密码学&lt;/strong>。其安全性的根基在于&lt;strong>LWE问题 (Learning with Errors: 带错学习问题)&lt;/strong>。2005年由Oded Regev提出，这一划时代的成就让他获得了哥德尔奖。不理解LWE问题，就无法谈论现代PQC。&lt;/p>
&lt;h3 id="41-什么是lwe问题-learning-with-errors">4.1. 什么是LWE问题 (Learning with Errors)
&lt;/h3>&lt;p>首先，我们来考虑一个简单的线性方程组。假设在某个模 $q$ （模 $q$）下，有一个已知的随机矩阵 $A$，一个未知的秘密向量 $\vec{s}$，并给出了它们的乘积 $\vec{b}$。&lt;/p>
$$ \vec{b} = A\vec{s} \pmod q $$
&lt;p>此时，从公开信息 $A$ 和 $\vec{b}$ 求解未知的 $\vec{s}$ 是非常简单的。利用经典算法“高斯消元法”，可以在多项式时间内轻松计算出 $\vec{s}$。&lt;/p>
&lt;p>但是，如果在等式中加入“微小的故意误差（噪声）”，问题的难度就会呈指数级飙升。这就是&lt;strong>LWE问题&lt;/strong>。&lt;/p>
&lt;p>准备一个未知的秘密向量 $\vec{s} \in \mathbb{Z}_q^n$ 和一个随机选择的矩阵 $A \in \mathbb{Z}_q^{m \times n}$。接着，准备一个按照正态分布或二项分布等选择的“元素值足够小”的误差向量 $\vec{e} \in \mathbb{Z}_q^m$，按如下方式计算 $\vec{b}$：&lt;/p>
$$ \vec{b} = A\vec{s} + \vec{e} \pmod q $$
&lt;p>&lt;strong>Search LWE问题&lt;/strong>就是“从公开信息 $(A, \vec{b})$ 中求出秘密信息 $\vec{s}$”的问题。由于存在这个误差 $\vec{e}$，如果尝试使用高斯消元法等代数解法，在方程的加减过程中，误差 $\vec{e}$ 会像滚雪球一样被放大，最终变得与随机值无法区分从而宣告失败。&lt;/p>
&lt;p>LWE问题的伟大之处在于，存在一个强大的理论证明（规约）：除非存在能够解决格上“最坏情况复杂度问题（Worst-case hardness）”——如GapSVP（判定最短向量问题）或SIVP（最短独立向量问题）——的量子算法，否则LWE问题在平均情况（Average-case）下也是无法求解的。也就是说，即使是随机生成的加密密钥，也能保证其具有由理论上限支撑的坚固安全性。&lt;/p>
&lt;h3 id="42-ring-lwe与module-lwe带来的效率飞跃">4.2. Ring-LWE与Module-LWE带来的效率飞跃
&lt;/h3>&lt;p>普通的LWE问题（Standard LWE）安全性依据非常明确，但矩阵 $A$ 的尺寸会变得非常大，导致密钥大小达到兆字节级别，并不实用。于是有人提出了利用多项式环（Polynomial Rings）赋予代数结构的方法。&lt;/p>
&lt;p>&lt;strong>Ring-LWE问题&lt;/strong>中，不使用简单的向量或矩阵，而是使用某个多项式环 $R_q$ 的元素（多项式）。NIST标准中通常使用的是如下的分圆多项式环：&lt;/p>
$$ R_q = \mathbb{Z}_q[X]/(X^n + 1) $$
&lt;p>这里，$n$ 是 2 的幂（例如：256），$q$ 是一个适当的素数。在这个环上，使用元素 $a, s, e \in R_q$ 计算 $b = a \cdot s + e \pmod q$。因为一个多项式 $a$ 拥有 $n$ 个系数，所以可以大幅压缩数据，并且通过使用一种称为&lt;strong>NTT (Number Theoretic Transform: 数论变换)&lt;/strong> 的快速傅里叶变换（FFT）的有限域版本，可以以 $O(n \log n)$ 的计算复杂度超高速地进行多项式乘法。&lt;/p>
&lt;p>但是，Ring-LWE 存在一种担忧：“起因于环的特殊代数结构，可能存在未知的漏洞”。此外，在更改安全级别（如相当于AES-128, 192, 256等）时，必须改变多项式的次数 $n$ 本身，伴随而来的是必须重写包括NTT算法在内的整个实现，这在工程上是个难题。&lt;/p>
&lt;p>因此，被标准化的算法Kyber和Dilithium采用了&lt;strong>Module-LWE (M-LWE) 问题&lt;/strong>。Module-LWE 是位于无结构的Standard LWE和结构性过强的Ring-LWE之间的折中方案，它使用以多项式环 $R_q$ 的元素为成分的 $k \times k$ 矩阵（模块）。&lt;/p>
$$ \vec{b} = A\vec{s} + \vec{e} \pmod{R_q} \quad (A \in R_q^{k \times k}, \vec{s}, \vec{e} \in R_q^k) $$
&lt;p>Module-LWE 的最大优势在于，保持多项式的次数 $n$（NIST标准中 $n=256$）固定不变，仅通过改变矩阵的维度 $k$，就可以轻松实现安全级别的扩展。
例如，Kyber按如下方式调整维度 $k$：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Kyber512 (Level 1)&lt;/strong>: $k = 2$ （相当于AES-128）&lt;/li>
&lt;li>&lt;strong>Kyber768 (Level 3)&lt;/strong>: $k = 3$ （相当于AES-192）&lt;/li>
&lt;li>&lt;strong>Kyber1024 (Level 5)&lt;/strong>: $k = 4$ （相当于AES-256）&lt;/li>
&lt;/ul>
&lt;p>这使得底层的NTT代码和多项式运算硬件电路能够在所有安全级别下实现100%的代码复用，极大地提高了实现的安全性与效率。&lt;/p>
&lt;hr>
&lt;h2 id="5-crystals-kyber-ml-kem下一代密钥封装机制">5. CRYSTALS-Kyber (ML-KEM)：下一代密钥封装机制
&lt;/h2>&lt;p>被正式标准化为 &lt;strong>FIPS 203 (ML-KEM)&lt;/strong> 的 CRYSTALS-Kyber，是基于前述Module-LWE问题的密钥封装机制（KEM）。未来，它将成为TLS 1.3、SSH等协议中安全共享会话密钥的事实上的全球标准。&lt;/p>
&lt;h3 id="51-kem-key-encapsulation-mechanism-的架构">5.1. KEM (Key Encapsulation Mechanism) 的架构
&lt;/h3>&lt;p>在PQC时代，不再像RSA那样采用“客户端生成对称密钥，用服务器公钥加密后发送”的直接方法，而是将KEM这种封装框架作为标准。&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant Client as "客户端 (Alice)"
participant Server as "服务器 (Bob)"
Note over Client: "ML-KEM KeyGen()"
Client->>Client: "生成私钥 (sk) 与公钥 (pk)"
Client->>Server: "发送公钥 (pk)"
Note over Server: "ML-KEM Encaps()"
Server->>Server: "生成随机对称密钥 (K)"
Server->>Server: "用pk封装K，创建密文 (c)"
Server->>Client: "发送密文 (c)"
Note over Client: "ML-KEM Decaps()"
Client->>Client: "使用私钥 (sk) 解密密文 (c)"
Client->>Client: "解开封装，取出对称密钥 (K)"
Note over Client, Server: "利用共享的对称密钥 (K)，开始基于AES等的加密通信"&lt;/div>
&lt;h3 id="52-kyber的内部算法机制与藤崎-冈本变换">5.2. Kyber的内部算法机制与藤崎-冈本变换
&lt;/h3>&lt;p>Kyber的设计非常优雅。首先，它构建了一个仅对CPA（选择明文攻击）安全的公钥加密方案（Kyber.CPAPKE），然后应用一种被称为&lt;strong>藤崎-冈本变换 (Fujisaki-Okamoto Transform)&lt;/strong> 的极强密码学方法，将其升级为对CCA（适应性选择密文攻击）也安全的完美KEM。&lt;/p>
&lt;p>CPAPKE核心的加密和解密机制如下：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>密钥生成 (Key Generation)&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>从随机种子值在NTT域上生成矩阵 $A \in R_q^{k \times k}$。模数 $q$ 使用 $3329$。&lt;/li>
&lt;li>从中心二项分布（CBD）中采样具有小系数的秘密向量 $\vec{s}$ 和误差向量 $\vec{e}$。&lt;/li>
&lt;li>计算 $\vec{t} = A\vec{s} + \vec{e}$。公钥为 $(A, \vec{t})$，私钥为 $\vec{s}$。（实际上 $A$ 以种子值的形式公开，以节省带宽）。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>加密 (Encryption)&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>将希望共享的32字节消息（对称密钥材料）$m$ 编码为多项式。&lt;/li>
&lt;li>生成新的随机向量 $\vec{r}$ 以及微小误差 $\vec{e_1}, e_2$。&lt;/li>
&lt;li>$\vec{u} = A^T\vec{r} + \vec{e_1}$&lt;/li>
&lt;li>$v = \vec{t}^T\vec{r} + e_2 + \lfloor q/2 \rceil \cdot m$&lt;/li>
&lt;li>密文即为 $(\vec{u}, v)$。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>解密 (Decryption)&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>接收方计算 $v - \vec{s}^T\vec{u}$。&lt;/li>
&lt;li>将该式展开如下：
$v - \vec{s}^T\vec{u} = (\vec{t}^T\vec{r} + e_2 + \lfloor q/2 \rceil \cdot m) - \vec{s}^T(A^T\vec{r} + \vec{e_1})$&lt;/li>
&lt;li>代入 $\vec{t} = A\vec{s} + \vec{e}$ 后，主项 $\vec{s}^TA^T\vec{r}$ 相互抵消。&lt;/li>
&lt;li>剩下的部分为 $\lfloor q/2 \rceil \cdot m + (\vec{e}^T\vec{r} + e_2 - \vec{s}^T\vec{e_1})$。&lt;/li>
&lt;li>括号内的项是“微小误差的乘积和求和”，因此整体依然是一个足够小的值（噪声）。于是，通过阈值判定每个系数是接近 $0$ 还是接近 $q/2$，即可完全无误差地还原原消息 $m$ 的每一位（0 或 1）。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>Kyber最大的优势在于其压倒性的&lt;strong>处理速度&lt;/strong>和&lt;strong>适中的密钥大小&lt;/strong>。在Kyber768的情况下，公钥尺寸为1,184字节，密文尺寸为1,088字节，虽然相比于RSA-3072（密钥约384字节）要大，但可以无需进行数据包分割地容纳在现代互联网通信的MTU（最大传输单元）内，对网络延迟几乎没有负面影响。&lt;/p>
&lt;hr>
&lt;h2 id="6-crystals-dilithium-ml-dsa基于格的通用数字签名">6. CRYSTALS-Dilithium (ML-DSA)：基于格的通用数字签名
&lt;/h2>&lt;p>在数字签名的标准化过程中，同样是基于格密码学方法，拥有不同设计理念的算法相互竞争。其中，作为通用数字签名被选定为 &lt;strong>FIPS 204 (ML-DSA)&lt;/strong> 的是 &lt;strong>CRYSTALS-Dilithium&lt;/strong>。&lt;/p>
&lt;h3 id="61-带有中止的fiat-shamir范式-fiat-shamir-with-aborts">6.1. 带有中止的Fiat-Shamir范式 (Fiat-Shamir with Aborts)
&lt;/h3>&lt;p>与Kyber一样，Dilithium也是一种基于Module-LWE（以及Module-SIS问题）的数字签名方案。其设计基础采用了一个极其重要的范式——“&lt;strong>带有中止的Fiat-Shamir变换 (Fiat-Shamir with Aborts)&lt;/strong>”。&lt;/p>
&lt;p>Fiat-Shamir变换本身是将交互式零知识证明协议转换为非交互式数字签名的一种标准方法。证明方（签名者）生成承诺 $y$，计算 $w = Ay$ 并通过哈希函数处理，获得随机挑战 $c$，随后计算响应 $z = y + cs$。&lt;/p>
&lt;p>然而，在格密码学中如果简单应用这一方法，响应 $z$ 的分布会依赖于私钥 $s$ 的值而发生扭曲，导致观察到大量签名的攻击者可以一点点地泄露私钥 $s$ 的信息，这是一个致命的问题（侧信道数学泄露）。&lt;/p>
&lt;p>Dilithium的设计团队（Lyubashevsky等人）引入了“&lt;strong>拒绝采样 (Rejection Sampling)&lt;/strong>”方法。即如果签名计算结果 $z$ 的系数未能落入预设的安全阈值范围内，就废弃（Abort）整个签名过程，利用新的随机数 $y$ 从头开始计算。&lt;/p>
&lt;p>通过这种方法，最终输出的签名 $z$ 成为完全均匀的分布，不再依赖于任何私钥信息，在数学上完美地防范了信息泄露。&lt;/p>
&lt;h3 id="62-dilithium的优势与易实现性">6.2. Dilithium的优势与易实现性
&lt;/h3>&lt;p>Dilithium设计上的巨大优势在于，在签名生成过程中&lt;strong>完全不使用&lt;/strong>复杂的“高斯分布采样”和“浮点运算”。仅需使用均匀分布采样、简单的整数模运算、NTT以及哈希函数（SHAKE）即可实现，因此无论是在嵌入式微控制器还是在云端服务器上，都能轻易地实现安全且恒定时间（Constant-time）的代码。这使得它对针对物理侧信道的定时攻击等具有极强的抵抗力。&lt;/p>
&lt;hr>
&lt;h2 id="7-falcon-fn-dsa极致紧凑的格签名">7. FALCON (FN-DSA)：极致紧凑的格签名
&lt;/h2>&lt;p>NIST选择了**FALCON (Fast-Fourier Lattice-based Compact Signatures over NTRU)**作为具备与Dilithium不同特性的另一款基于格的签名标准化候选方案（目前正作为FN-DSA起草草案）。&lt;/p>
&lt;h3 id="71-ntru格与高斯采样">7.1. NTRU格与高斯采样
&lt;/h3>&lt;p>FALCON最大的特点在于它不使用LWE问题，而是使用自1996年起就存在的历史悠久的&lt;strong>NTRU (N-th degree Truncated polynomial Ring Units) 格&lt;/strong>。此外，它还采用了基于GPV (Gentry-Peikert-Vaikuntanathan) 框架的“&lt;strong>哈希与签名 (Hash-and-Sign)&lt;/strong>”范式。&lt;/p>
&lt;p>在Hash-and-Sign中，将消息的哈希值作为空间内的一个目标点，找到距离该目标点最近的格上的点（最近向量问题的近似解）作为签名。这要求使用作为私钥的“优质的短基”，按照离散高斯分布对点进行采样。&lt;/p>
&lt;p>FALCON利用被称为“&lt;strong>快速傅里叶正交化 (Fast Fourier Orthogonalization: FFO)&lt;/strong>”的技术，使得这种繁重的计算实现了极其显著的加速。&lt;/p>
&lt;h3 id="72-falcon的优缺点">7.2. FALCON的优缺点
&lt;/h3>&lt;p>FALCON压倒性的优势在于其&lt;strong>签名尺寸和公钥尺寸极小（非常紧凑）&lt;/strong>。相较于Dilithium3的签名尺寸约为3,309字节，FALCON-512的签名尺寸仅约666字节。公钥尺寸也非常小，为897字节，在通信带宽受限严重的环境、物联网（IoT）设备或特定网络协议中，它是无可替代的救星。&lt;/p>
&lt;p>然而，它也存在一个重大缺陷。签名生成时必须进行涉及复杂**浮点运算（64位IEEE 754）**的离散高斯采样，因此要实现防止时间泄露的恒定时间实现（Constant-time implementation）极其困难，代码也会变得十分庞大。因此，相对于通用用途（Dilithium），FALCON被定位为面向特定用途的强大特化型算法。&lt;/p>
&lt;div class="mermaid">graph LR
A["数字签名的要求"] --> B{"最优先的约束是什么？"}
B -->|"实现简单、通用性、易于恒定时间实现"| C["Dilithium (ML-DSA)"]
B -->|"通信带宽最小化、数据尺寸的紧凑性"| D["FALCON (FN-DSA)"]
C --> E["通用TLS证书、软件数字签名"]
D --> F["限制封包大小的协议、特殊环境"]&lt;/div>
&lt;hr>
&lt;h2 id="8-sphincs-slh-dsa拥有最强安全性的哈希签名">8. SPHINCS+ (SLH-DSA)：拥有最强安全性的哈希签名
&lt;/h2>&lt;p>为了防范“未来某位天才数学家取得突破从而破解格密码学安全性”这一最坏情况（万一之事），NIST制定了与格密码学截然不同路径的标准——&lt;strong>FIPS 205 (SLH-DSA)&lt;/strong>，即&lt;strong>SPHINCS+&lt;/strong>。&lt;/p>
&lt;p>SPHINCS+ 属于&lt;strong>基于哈希的签名&lt;/strong>。其安全性的依据仅依赖于唯一一点：“所使用的密码学哈希函数（如SHA-2或SHAKE256等）具有抗碰撞性和单向性”。由于它不依赖于LWE或素数分解等具有特定代数结构的数学问题，因此未来无论出现多么强大的量子算法，只需单纯延长哈希函数的输出长度即可应对，夸耀着其极其坚固的安全性（最保守的安全级别）。&lt;/p>
&lt;h3 id="81-利用wots与fors实现的无状态架构">8.1. 利用WOTS+与FORS实现的无状态架构
&lt;/h3>&lt;p>基于哈希的签名历史悠久，可以追溯到20世纪70年代的Lamport签名和Winternitz一次性签名（WOTS）。这些是“只能安全签名一次”的用后即弃密钥。为了能多次使用，开发出了结合默克尔树（Merkle Tree）将无数次一次性密钥统一由一个根哈希来管理的XMSS（eXtended Merkle Signature Scheme）和LMS等算法。&lt;/p>
&lt;p>但是，XMSS和LMS存在一个叫做“&lt;strong>有状态（Stateful）&lt;/strong>”的严重缺陷。每次签名都必须在非易失性内存中严格记录“使用了第几个一次性密钥”的索引状态，如果因为虚拟机快照恢复等原因导致状态回溯，同一个一次性密钥被使用两次，私钥就会立刻泄露导致系统崩溃。&lt;/p>
&lt;p>SPHINCS+则解决了这种状态管理的繁琐，是“&lt;strong>无状态（Stateless）&lt;/strong>”的基于哈希的签名。
其核心技术由以下部分组合而成：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>WOTS+ (Winternitz One-Time Signature Plus)&lt;/strong>: 基本的一次性签名。&lt;/li>
&lt;li>&lt;strong>FORS (Forest of Random Subsets)&lt;/strong>: 少次签名（Few-Time Signature）技术。即使重复使用该密钥数次也能保持安全。&lt;/li>
&lt;li>&lt;strong>Hyper-Tree (巨大树结构)&lt;/strong>: 将默克尔树多层叠加而成的庞大结构。&lt;/li>
&lt;/ol>
&lt;p>SPHINCS+在进行签名时，不再管理状态，而是利用伪随机数从Hyper-Tree底部的海量FORS密钥中随机挑选一个进行签名。由于树叶的数量属于天文数字量级，偶然两次选中同一个密钥的概率（碰撞）小到可以忽略不计，从而实现了无状态化。&lt;/p>
&lt;p>SPHINCS+唯一也是最大的弱点在于，&lt;strong>签名尺寸非常庞大&lt;/strong>。根据参数不同，签名尺寸可达17千字节至49千字节，签名生成速度也远慢于格密码学。因此，相比于日常网络浏览，它更适合被用于软件更新签名或根证书颁发机构（CA）证书等不需频繁签名，且强烈要求长期绝对安全的场景。&lt;/p>
&lt;hr>
&lt;h2 id="9-基于编码的密码学classic-mceliece这个古老而优秀的巨人">9. 基于编码的密码学：Classic McEliece这个古老而优秀的巨人
&lt;/h2>&lt;p>在NIST标准化进程中，作为Round 4的最终候选仍在继续评估的一个重要方案是，&lt;strong>基于编码的密码学&lt;/strong>中的&lt;strong>Classic McEliece&lt;/strong>。&lt;/p>
&lt;p>1978年由Robert McEliece提出的这一算法，是公钥密码学历史中与RSA并肩最古老的算法之一。它利用了被称为“Goppa码（哥帕码）”的代数几何码，在消息中故意加入错误（噪声向量）进行加密，只有拥有作为私钥的Goppa码奇偶校验矩阵的人，才能利用其强大的纠错能力消除错误，从而解密出原始消息。这基于“&lt;strong>伴随式解码问题 (Syndrome Decoding Problem)&lt;/strong>”。&lt;/p>
$$ \vec{c} = \vec{m} G + \vec{e} $$
&lt;p>
（$G$ 是公开的被置换过的生成矩阵，$\vec{e}$ 是汉明重量为 $t$ 的错误向量）&lt;/p>
&lt;p>Classic McEliece令人惊叹之处在于，&lt;strong>它提出已逾40年，经历了全世界密码学界猛烈的破解研究，却从未被发现存在根本性的漏洞&lt;/strong>，展示了压倒性的实绩。在PQC中，它是最具“时间证明之坚固安全性”的算法。&lt;/p>
&lt;p>此外，它的密文尺寸非常小（仅有约100至200字节）。然而，它存在一个致命的缺点：&lt;strong>公钥的尺寸达到了兆字节（MB）级别&lt;/strong>。即便是最低的安全级别（相当于AES-128），公钥也有约250KB，高安全级别下更是超过1MB。&lt;/p>
&lt;p>正因如此，它完全无法适用于诸如TLS握手等每次通信都需要在网络上传输公钥的场景。不过，在预共享VPN密钥交换、将公钥硬编码至固件、或者是卫星通信等可以提前将公钥部署到系统中的特殊用例中，因其强大的安全性，它仍作为一个极具希望的选项被持续探讨。&lt;/p>
&lt;hr>
&lt;h2 id="10-各pqc算法的性能比较与权衡">10. 各PQC算法的性能比较与权衡
&lt;/h2>&lt;p>对于前面解说的各主要算法，在一般安全级别（相当于NIST Level 2〜3、AES-128〜192）下的性能特征，总结于下表。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:left">算法 (标准名)&lt;/th>
&lt;th style="text-align:left">类别&lt;/th>
&lt;th style="text-align:left">数学基础&lt;/th>
&lt;th style="text-align:left">公钥尺寸&lt;/th>
&lt;th style="text-align:left">私钥尺寸&lt;/th>
&lt;th style="text-align:left">密文/签名尺寸&lt;/th>
&lt;th style="text-align:left">处理速度倾向&lt;/th>
&lt;th style="text-align:left">主要特征与用途&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Kyber768&lt;/strong>&lt;br>(ML-KEM)&lt;/td>
&lt;td style="text-align:left">KEM&lt;/td>
&lt;td style="text-align:left">Module-LWE&lt;/td>
&lt;td style="text-align:left">1,184 Bytes&lt;/td>
&lt;td style="text-align:left">2,400 Bytes&lt;/td>
&lt;td style="text-align:left">1,088 Bytes&lt;/td>
&lt;td style="text-align:left">非常快&lt;/td>
&lt;td style="text-align:left">密钥尺寸与速度平衡最佳。TLS 1.3等通用KEM标准。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Dilithium3&lt;/strong>&lt;br>(ML-DSA)&lt;/td>
&lt;td style="text-align:left">签名&lt;/td>
&lt;td style="text-align:left">Module-LWE&lt;/td>
&lt;td style="text-align:left">1,952 Bytes&lt;/td>
&lt;td style="text-align:left">4,032 Bytes&lt;/td>
&lt;td style="text-align:left">3,309 Bytes&lt;/td>
&lt;td style="text-align:left">生成与验证均快&lt;/td>
&lt;td style="text-align:left">实现简单。通用数字签名标准。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>FALCON-512&lt;/strong>&lt;br>(FN-DSA)&lt;/td>
&lt;td style="text-align:left">签名&lt;/td>
&lt;td style="text-align:left">NTRU格&lt;/td>
&lt;td style="text-align:left">897 Bytes&lt;/td>
&lt;td style="text-align:left">1,281 Bytes&lt;/td>
&lt;td style="text-align:left">666 Bytes&lt;/td>
&lt;td style="text-align:left">签名生成偏慢，验证超快&lt;/td>
&lt;td style="text-align:left">签名尺寸极小。但需要浮点运算。适合嵌入式与IoT。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>SPHINCS+&lt;/strong>&lt;br>(SLH-DSA)&lt;/td>
&lt;td style="text-align:left">签名&lt;/td>
&lt;td style="text-align:left">哈希函数&lt;/td>
&lt;td style="text-align:left">32 Bytes&lt;/td>
&lt;td style="text-align:left">64 Bytes&lt;/td>
&lt;td style="text-align:left">约 17,000 Bytes&lt;/td>
&lt;td style="text-align:left">生成非常慢&lt;/td>
&lt;td style="text-align:left">几乎没有数学崩溃的风险。用于根证书等高安全用途。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Classic McEliece&lt;/strong>&lt;/td>
&lt;td style="text-align:left">KEM&lt;/td>
&lt;td style="text-align:left">Goppa码&lt;/td>
&lt;td style="text-align:left">&lt;strong>约 1.04 MB&lt;/strong>&lt;/td>
&lt;td style="text-align:left">13,568 Bytes&lt;/td>
&lt;td style="text-align:left">&lt;strong>188 Bytes&lt;/strong>&lt;/td>
&lt;td style="text-align:left">封装很快&lt;/td>
&lt;td style="text-align:left">40年安全性证明。公钥巨大。适合可硬编码环境。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="权衡的理解">权衡的理解
&lt;/h3>&lt;p>在PQC的世界里，不存在“尺寸小、速度快、数学保证完美”的魔法般的单一算法。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>互联网标准 (Kyber / Dilithium)&lt;/strong>: 性能平衡最佳，最适合作为当前RSA/ECC的直接替换（Drop-in replacement）。&lt;/li>
&lt;li>&lt;strong>极致的保守性 (SPHINCS+)&lt;/strong> : 即使牺牲数据大小和处理速度，也希望对未来的数学突破有一份绝对保险时的选择。&lt;/li>
&lt;li>&lt;strong>面向特殊环境 (FALCON / Classic McEliece)&lt;/strong>: 通信带宽极其狭窄，或是可以提前分发公钥等，根据环境限制而选择的特化型武器。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="11-面向实际应用的挑战与混合加密这一现实解">11. 面向实际应用的挑战与“混合加密”这一现实解
&lt;/h2>&lt;p>随着NIST标准化的完成和FIPS规范的正式发布，全世界IT基础设施向PQC的迁移（&lt;strong>PQC Migration&lt;/strong>）已经正式拉开帷幕。谷歌的Chrome浏览器、苹果的iMessage（PQ3协议）、Cloudflare等网络提供商，已经开始在协议中实现对PQC的支持，并投入实际运营。&lt;/p>
&lt;p>但是，突然完全切换到新密码算法伴随着非常高的风险。假设几年后有一位天才数学家发现了针对Kyber等格密码学的致命攻击手法（甚至连经典计算机也能利用的数学缺陷），那么依赖该算法的所有系统将会瞬间裸奔。&lt;/p>
&lt;p>为了减轻这种不确定性风险，一个务实且被推荐的策略是“&lt;strong>混合加密 (Hybrid Cryptography)&lt;/strong>”。&lt;/p>
&lt;p>混合加密中，同时使用拥有多年实际验证的现行经典加密（如X25519等椭圆曲线加密）与新的PQC（如Kyber768）来进行密钥交换。各自的算法分别生成对称密钥的成分，最后使用安全的密钥派生函数（KDF）将两个成分混合，生成最终的主密钥。&lt;/p>
&lt;div class="mermaid">graph TD
A["客户端"] -->|① 发送 X25519公钥 + Kyber公钥| B["服务器"]
B -->|② 返回 X25519共享密钥 + Kyber封装密文| A
A --> C{"主密钥的派生 (KDF)"}
B --> C
C -->|输入: (X25519对称密钥) || (Kyber对称密钥)| D["安全的通信密钥 (AES-256 / ChaCha20)"]
D -->|"同时抵抗 量子威胁 ＆ 传统经典漏洞"| E["安全的混合加密通信 (TLS 1.3)"]&lt;/div>
&lt;p>如此一来，就实现了坚固的双重安全保障：“万一量子计算机实现并破解了ECC，Kyber能保护通信”；反之，“万一Kyber被发现了未知的数学缺陷，ECC依然能保护通信”。作为典型代表，IETF正在推进标准化的 &lt;strong>X25519MLKEM768 (原 X25519Kyber768)&lt;/strong> 草案，正是目前Web浏览器与最先进的服务器间通信所采用的混合方式。&lt;/p>
&lt;p>此外，在系统设计中，构建一种“不过度依赖特定密码算法，当算法崩溃时能迅速切换到另一算法（如从Kyber切换至McEliece，从Dilithium切换至SPHINCS+）的架构”的理念，即 &lt;strong>Crypto Agility（密码敏捷性）&lt;/strong>，将成为未来系统开发中的必备要求。&lt;/p>
&lt;hr>
&lt;h2 id="12-结语密码技术的新地平线">12. 结语：密码技术的新地平线
&lt;/h2>&lt;p>量子计算机这项人类梦寐以求的科技，讽刺地成为了击破我们长年信赖的“素数分解”和“离散对数问题”等数学防线的最大威胁。然而，全世界的密码学家们并未屈服，他们开拓了诸如格理论、哈希函数树、纠错码等更为复杂且深邃的多维数学领域，筑起了抗量子密码学（PQC）这座新的防御堡垒。&lt;/p>
&lt;p>NIST完成了 FIPS 203 (ML-KEM)、FIPS 204 (ML-DSA)、FIPS 205 (SLH-DSA) 的标准化并不是终点。这仅仅是未来将持续数十年的PQC迁移这场宏大旅程的第一步。对于软件工程师和系统架构师来说，如何将这些新算法带来的“密钥尺寸增大”和“计算成本变化”最优地融入并适应到网络协议和系统中，将是未来重大的技术挑战。&lt;/p>
&lt;p>量子计算机与密码学的较量，是人类数学探索与技术进化交锋最激烈的激动人心的领域。希望通过本文，您能对PQC背后的优美数学理论，以及塑造网络安全未来的各算法令人惊叹的机制，有更深刻的理解。&lt;/p>
&lt;hr>
&lt;p>&lt;em>References:&lt;/em>&lt;/p>
&lt;ul>
&lt;li>&lt;em>NIST Post-Quantum Cryptography Standardization Program&lt;/em>&lt;/li>
&lt;li>&lt;em>FIPS 203: Module-Lattice-Based Key-Encapsulation Mechanism Standard&lt;/em>&lt;/li>
&lt;li>&lt;em>FIPS 204: Module-Lattice-Based Digital Signature Standard&lt;/em>&lt;/li>
&lt;li>&lt;em>FIPS 205: Stateless Hash-Based Digital Signature Standard&lt;/em>&lt;/li>
&lt;/ul></description></item><item><title>量子计算机实用的日子：2026年的现状</title><link>http://kenji.blog/zh-cn/p/quantum-computing-2026-current-status/</link><pubDate>Fri, 11 Sep 2026 06:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/p/quantum-computing-2026-current-status/</guid><description>&lt;img src="http://kenji.blog/p/quantum-computing-2026-current-status/img/eyecatch.jpg" alt="Featured image of post 量子计算机实用的日子：2026年的现状" />&lt;h2 id="1-引言2026年量子计算机发展到了什么程度">1. 引言：2026年，量子计算机发展到了什么程度
&lt;/h2>&lt;p>截至2026年，量子计算已经完成了从昔日“理论上的梦想”到“工程上的现实”的决定性转变。随着几年前还是主流的**NISQ（Noisy Intermediate-Scale Quantum，含噪声中等规模量子）**设备的局限性变得明显，世界各地的研究机构和科技巨头纷纷转向实现“容错量子计算（FTQC: Fault-Tolerant Quantum Computing）”。&lt;/p>
&lt;p>本文将结合2026年的最新突破，深入探讨量子计算机的现状。特别是，将详细讲解量子纠错（表面码）、物理量子比特与逻辑量子比特的区别、拓扑量子计算的进展，以及超导和离子阱方式的最前沿。&lt;/p>
&lt;hr>
&lt;h2 id="2-量子态基础与保真度fidelity">2. 量子态基础与保真度（Fidelity）
&lt;/h2>&lt;p>量子计算机的基本单位是量子比特（Qubit），与经典比特（0或1）不同，它可以处于0和1的叠加态（Superposition）。单个量子比特的状态可以表示为希尔伯特空间上的向量，如下所示：&lt;/p>
$$
|\psi\rangle = \alpha|0\rangle + \beta|1\rangle
$$
&lt;p>这里，$\alpha$ 和 $\beta$ 是复数概率幅，并满足以下归一化条件：&lt;/p>
$$
|\alpha|^2 + |\beta|^2 = 1
$$
&lt;p>在衡量量子计算性能时，一个极其重要的指标是&lt;strong>保真度（Fidelity）&lt;/strong>。理想量子态 $|\psi\rangle$ 与因噪声退化而变成混合态的实际密度矩阵 $\rho$ 之间的保真度 $F$ 定义如下：&lt;/p>
$$
F(\rho, |\psi\rangle) = \langle \psi | \rho | \psi \rangle
$$
&lt;p>到2026年，双量子比特门（例如：CNOT门和CZ门）的保真度在超导方式下已经能够稳定突破**99.99%**的壁垒（即所谓的“4个9”）。这一数值大幅超过了表面码纠错的阈值（约99%），是迈向实用化的最大突破之一。&lt;/p>
&lt;hr>
&lt;h2 id="3-nisq时代的局限与向ftqc的范式转变">3. NISQ时代的局限与向FTQC的范式转变
&lt;/h2>&lt;p>2010年代后半期到2020年代前半期是NISQ（Noisy Intermediate-Scale Quantum）时代，即拥有几十到几百个量子比特且不具备纠错能力的设备。然而，NISQ存在明显的局限性。&lt;/p>
&lt;p>随着电路深度（Depth）的增加，错误会呈指数级积累，导致无法获得有意义的计算结果。电路深度 $D$ 下的整体成功概率 $P_{success}$ 随单门保真度 $f$ 和门数量 $N$ 衰减如下：&lt;/p>
$$
P_{success} \approx f^N
$$
&lt;p>如果 $f = 0.99$ 并应用1000个门，那么 $0.99^{1000} \approx 4.3 \times 10^{-5}$，结果将几乎完全被随机噪声淹没。因此，在2026年，相较于直接扩展NISQ算法（如VQE或QAOA等），资源更多地集中在生成**逻辑量子比特（Logical Qubit）**上。&lt;/p>
&lt;hr>
&lt;h2 id="4-量子纠错与逻辑量子比特表面码的最前沿">4. 量子纠错与逻辑量子比特：表面码的最前沿
&lt;/h2>&lt;p>量子纠错（QEC: Quantum Error Correction）是一种通过编码多个“物理量子比特”来创建一个“逻辑量子比特”，从而检测并纠正错误的技术。目前最被看好的是&lt;strong>表面码（Surface Code）&lt;/strong>。&lt;/p>
&lt;h3 id="41-表面码surface-code的结构">4.1 表面码（Surface Code）的结构
&lt;/h3>&lt;p>在表面码中，量子比特被排列成二维网格状。数据量子比特（保存实际信息）和测量量子比特（用于综合征测量）像棋盘格一样交错排列。&lt;/p>
&lt;div class="mermaid">graph TD
A["数据量子比特 (D1)"] --- B["测量量子比特 (M1)"]
B --- C["数据量子比特 (D2)"]
C --- D["测量量子比特 (M2)"]
D --- E["数据量子比特 (D3)"]
B --- F["数据量子比特 (D4)"]
D --- G["数据量子比特 (D5)"]
style A fill:#e1f5fe,stroke:#039be5
style C fill:#e1f5fe,stroke:#039be5
style E fill:#e1f5fe,stroke:#039be5
style F fill:#e1f5fe,stroke:#039be5
style G fill:#e1f5fe,stroke:#039be5
style B fill:#fff3e0,stroke:#fb8c00
style D fill:#fff3e0,stroke:#fb8c00&lt;/div>
&lt;p>使用稳定子算符 $S_x$ 和 $S_z$ 不断监控比特翻转（X错误）和相位翻转（Z错误）。&lt;/p>
$$
S_x = \prod_{i \in \text{star}} X_i, \quad S_z = \prod_{j \in \text{plaquette}} Z_j
$$
&lt;p>2026年的重大进展是完全突破了“盈亏平衡点（Break-even point）”。也就是说，通过纠错消除的噪声大于执行纠错所需的额外电路引起的噪声，逻辑量子比特的寿命开始比物理量子比特的寿命高出好几个数量级。&lt;/p>
&lt;h3 id="42-量子纠错循环">4.2 量子纠错循环
&lt;/h3>&lt;p>纠错作为一个持续的反馈回路运行。&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant D as "数据量子比特"
participant M as "辅助/测量量子比特"
participant C as "经典控制器"
loop "综合征提取周期 (约 1 微秒)"
D->>M: "纠缠 (CNOT/CZ)"
M->>C: "测量状态 (综合征)"
C->>C: "解码综合征 (例如最小权重完美匹配)"
C-->>D: "应用泡利纠正 (如果需要)"
end&lt;/div>
&lt;p>目前，利用FPGA或专用ASIC在纳秒级别执行这种经典解码处理（综合征分析）的技术已经成熟，实时纠错已进入实用阶段。&lt;/p>
&lt;hr>
&lt;h2 id="5-硬件架构的演进2026年版">5. 硬件架构的演进（2026年版）
&lt;/h2>&lt;p>2026年的量子硬件主要在“超导方式”、“离子阱方式”和“拓扑方式”这三个方向上演进。&lt;/p>
&lt;h3 id="51-超导量子比特的集成化">5.1 超导量子比特的集成化
&lt;/h3>&lt;p>超导方式是IBM和Google主导的领域，主要使用基于约瑟夫森结的Transmon量子比特。到2026年，实现了在单芯片上集成数千到一万个物理量子比特的超大型芯片。&lt;/p>
&lt;p>值得一提的是**模块间量子通信（Quantum Interconnects）**的确立。利用微波光子实现芯片间的量子隐形传态已达到商业应用水平，从而能够避开单一稀释制冷机尺寸的限制。&lt;/p>
&lt;h3 id="52-离子阱的二维扩展与光互连">5.2 离子阱的二维扩展与光互连
&lt;/h3>&lt;p>在离子阱方式（由Quantinuum和IonQ等主导）中，使用悬浮在真空中的离子的内部能态作为量子比特。与超导方式相比，其优势在于T1/T2相干时间极长，并且支持全连接（All-to-All Connectivity）。&lt;/p>
&lt;p>2026年的突破在于QCCD（Quantum Charge Coupled Device）架构的二维化，以及使用光子互连在多阱之间生成高速纠缠。这使得离子阱曾存在的“门操作速度慢”和“可扩展性”等弱点得到了极大的改善。&lt;/p>
&lt;h3 id="53-拓扑量子计算任意子的控制">5.3 拓扑量子计算：任意子的控制
&lt;/h3>&lt;p>长期以来被认为是理论存在的&lt;strong>拓扑量子计算&lt;/strong>在2026年终于进入了实验验证阶段。由Microsoft等公司推动的这种方式，使用被称为“马约拉纳零模（Majorana Zero Modes）”的非阿贝尔任意子（Non-Abelian Anyons）。&lt;/p>
&lt;p>通过交换任意子粒子位置的“编织（Braiding）”操作来执行量子门。&lt;/p>
$$
|\psi_{final}\rangle = B_{ij} |\psi_{initial}\rangle
$$
&lt;p>这里 $B_{ij}$ 是编织算符。拓扑方式的信息存储不依赖于粒子的局域状态，而是依赖于整体“结”的拓扑结构，因此它本质上对环境噪声具有免疫力（硬件级别的容错能力）。2026年世界上首次确认了高保真度拓扑逻辑量子比特的生成，作为通向FTQC的强大捷径而备受瞩目。&lt;/p>
&lt;hr>
&lt;h2 id="6-面向实用化的路线图与展望">6. 面向实用化的路线图与展望
&lt;/h2>&lt;p>要让量子计算机在“化学计算”、“材料科学”、“金融建模”等领域真正发挥出压倒经典计算机（超级计算机）的&lt;strong>量子霸权（Quantum Advantage，也称量子优势）&lt;/strong>，需要数千个逻辑量子比特。&lt;/p>
&lt;div class="mermaid">gantt
title "量子计算路线图 (2026年修订版)"
dateFormat YYYY
axisFormat %Y
section "NISQ 时代"
"含噪声量子比特 (&lt;1000)" :done, 2018, 2024
section "早期 FTQC"
"盈亏平衡点演示" :done, 2024, 2026
"数百个逻辑量子比特" :active, 2026, 2028
section "全面 FTQC"
"1000+ 逻辑量子比特 (商业应用)" : 2028, 2030
"通用容错量子计算机" : 2030, 2035&lt;/div>
&lt;h3 id="61-当前面临的挑战与未来">6.1 当前面临的挑战与未来
&lt;/h3>&lt;p>截至2026年，最大的挑战在于维持极低温的巨大低温恒温器（稀释制冷机）的冷却能力，以及连接室温控制设备与极低温量子芯片的布线（I/O瓶颈）。针对这一点，能够在极低温环境下运行的CMOS（Cryo-CMOS）控制芯片的开发正在全速推进。&lt;/p>
&lt;h3 id="结论">结论
&lt;/h3>&lt;p>2026年将在量子计算机的历史上被记载为“逻辑量子比特扩展元年”。通过纠错算法的验证、硬件的模块化以及拓扑方法的快速进展，“实用化的日子”已不再是遥远的未来，而是可以预见为未来几年内的具体里程碑。对于量子算法开发者和企业而言，现在正是开始全面投资于量子原生问题解决方案的绝佳时机。&lt;/p>
&lt;hr>
&lt;p>&lt;em>本文基于截至2026年最新的量子计算研究论文及行业动态撰写。&lt;/em>&lt;/p></description></item></channel></rss>