<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Math on kenji.blog</title><link>http://kenji.blog/zh-cn/tags/math/</link><description>Recent content in Math 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/tags/math/index.xml" rel="self" type="application/rss+xml"/><item><title>【面向初学者】解读Transformer模型的数学结构</title><link>http://kenji.blog/zh-cn/p/transformer-mathematical-structure/</link><pubDate>Fri, 11 Sep 2026 22:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/p/transformer-mathematical-structure/</guid><description>&lt;img src="http://kenji.blog/p/transformer-mathematical-structure/img/eyecatch.jpg" alt="Featured image of post 【面向初学者】解读Transformer模型的数学结构" />&lt;h1 id="引言为什么要学习transformer的数学原理">引言：为什么要学习Transformer的数学原理？
&lt;/h1>&lt;p>毫不夸张地说，“Transformer”架构改写了现代自然语言处理（NLP）乃至整个AI的历史。该模型在2017年由Google的研究人员们在论文《Attention Is All You Need》中首次提出，目前作为OpenAI的GPT系列（ChatGPT的基础技术）、Google的BERT以及Anthropic的Claude等席卷全球的大型语言模型（LLM）的核心组件而发挥着作用。&lt;/p>
&lt;p>然而，目前的情况是，关于Transformer的工作原理，虽然经常能看到诸如“使用Attention（注意力机制）来理解上下文”这样的定性解释，但针对初学者的关于其背后的&lt;strong>数学结构&lt;/strong>的深入解说是出乎意料地少的。为了真正理解AI是如何将“语言”作为“数学公式”来处理，并生成令人惊叹的自然文章的，解读其数学机制是不可或缺的。&lt;/p>
&lt;p>在本文中，将以具备数学和编程基础知识的人（了解高中水平的矩阵和微分概念的人）为对象，彻底且通俗易懂地解开Transformer核心部分的“Self-Attention机制”、“查询·键·值（Q/K/V）模型”、“通过Softmax函数的归一化”以及“Positional Encoding”等数学结构。&lt;/p>
&lt;p>你可能会被一连串的数学公式所压倒，但每一个计算都有其明确的“意义”。当读完这篇文章时，你应该能够理解Transformer并不是什么魔法般的黑盒，而是经过精心设计的数学和统计学的结晶。&lt;/p>
&lt;hr>
&lt;h1 id="1-传统方法的局限性与transformer的创新性">1. 传统方法的局限性与Transformer的创新性
&lt;/h1>&lt;p>在Transformer出现之前，自然语言处理的主流是循环神经网络（RNN）及其派生的LSTM（Long Short-Term Memory）。RNN是为了处理时间序列数据而设计的，它会逐个单词地从开头按顺序读取文章。&lt;/p>
&lt;p>但是，RNN存在两个致命的弱点。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>难以学习长期依赖关系&lt;/strong>：当文章变长时，最开始输入的单词信息在到达最后时会变得淡薄（梯度消失问题）。&lt;/li>
&lt;li>&lt;strong>无法进行并行计算&lt;/strong>：因为必须按顺序处理单词，所以很难利用GPU进行大规模的并行计算，训练需要花费大量的时间。&lt;/li>
&lt;/ol>
&lt;p>Transformer彻底抛弃了RNN的结构，引发了一场只使用“Attention”来捕捉上下文的范式转换。由此，无论序列长度有多长，都不会发生信息丢失，并且能够并行化计算，从而最大限度地发挥GPU的性能。&lt;/p>
&lt;hr>
&lt;h1 id="2-transformer的整体架构">2. Transformer的整体架构
&lt;/h1>&lt;p>首先，让我们俯瞰一下Transformer的整体架构。Transformer大致可以分为“Encoder（编码器）”和“Decoder（解码器）”两个模块。以翻译任务为例，Encoder将输入语言（例如：英语）转换为数学上的向量表示，而Decoder则根据该向量表示生成输出语言（例如：日语）。&lt;/p>
&lt;p>下图简化的展示了Encoder模块的内部结构。&lt;/p>
&lt;div class="mermaid">graph TD
A["输入Token (Input Tokens)"] --> B["输入嵌入 (Input Embedding)"]
B --> C["位置编码 (Positional Encoding)"]
C --> D["多头自注意力机制 (Multi-Head Self-Attention)"]
D --> E["相加与层归一化 (Add &amp; Layer Normalization)"]
E --> F["前馈神经网络 (Feed Forward Network)"]
F --> G["相加与层归一化 (Add &amp; Layer Normalization)"]
G --> H["输出到下一层 (Output to Next Layer)"]
C -.->|"残差连接 (Residual Connection)"| E
E -.->|"残差连接 (Residual Connection)"| G&lt;/div>
&lt;p>从这里开始，让我们按顺序来看看各个组件中进行的数学操作。&lt;/p>
&lt;hr>
&lt;h1 id="3-单词的向量化与位置编码positional-encoding">3. 单词的向量化与位置编码（Positional Encoding）
&lt;/h1>&lt;p>计算机无法直接理解文本。输入的文本首先会被分割成被称为“Token（词元）”的单位，然后每一个Token都会被转换为固定长度的向量。这就是&lt;strong>Input Embedding&lt;/strong>。&lt;/p>
&lt;h2 id="31-input-embedding的数学">3.1 Input Embedding的数学
&lt;/h2>&lt;p>假设词汇表（Vocabulary）的大小为 $V$，嵌入向量的维度数为 $d_{model}$（在原论文中 $d_{model} = 512$）。每个单词 $w_i$ 使用嵌入矩阵 $W_E \in \mathbb{R}^{V \times d_{model}}$ 转换为向量 $x_i \in \mathbb{R}^{d_{model}}$。&lt;/p>
$$ x_i = W_E \cdot \text{one\_hot}(w_i) $$
&lt;p>由此，整篇文章被表示为矩阵 $X \in \mathbb{R}^{N \times d_{model}}$（$N$ 是文章的长度）。&lt;/p>
&lt;h2 id="32-positional-encoding位置编码的必要性与数学公式">3.2 Positional Encoding（位置编码）的必要性与数学公式
&lt;/h2>&lt;p>Transformer并不像RNN那样按顺序处理单词，而是同时并行处理所有单词。从计算速度的角度来看这是一个很大的优势，但同时也会引起&lt;strong>丢失“单词顺序（语序）”这一重要信息&lt;/strong>的问题。例如，“狗咬人”和“人咬狗”，虽然输入的单词集合相同，但含义却完全不同。&lt;/p>
&lt;p>为了将这种语序信息提供给模型，人们设计出了&lt;strong>Positional Encoding&lt;/strong>。
位置 $pos$ 处的单词的第 $i$ 个维度的Positional Encoding $PE$ 是使用以下三角函数来计算的。&lt;/p>
$$ PE_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{2i/d_{model}}}\right) $$
$$ PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i/d_{model}}}\right) $$
&lt;p>这里，$pos$ 是单词的位置（$0, 1, 2, \dots, N-1$），$i$ 是向量维度的索引（$0, 1, \dots, d_{model}/2 - 1$）。&lt;/p>
&lt;h3 id="为什么要使用正弦和余弦">为什么要使用正弦和余弦？
&lt;/h3>&lt;p>乍一看这似乎是非常复杂和奇怪的数学公式，但其中有着深刻的数学原因。通过使用三角函数，模型不仅能够容易地学习到**“绝对位置”&lt;strong>，还能容易地学习到&lt;/strong>“相对位置”的差异**。&lt;/p>
&lt;p>请回想一下高中数学中学到的三角函数加法定理。
&lt;/p>
$$ \sin(\alpha + \beta) = \sin\alpha \cos\beta + \cos\alpha \sin\beta $$
$$ \cos(\alpha + \beta) = \cos\alpha \cos\beta - \sin\alpha \sin\beta $$
&lt;p>距离某个位置 $pos$ 偏移了 $k$ 的位置 $pos + k$ 的Positional Encoding，可以表示为位置 $pos$ 的Positional Encoding的线性组合。也就是说，可以使用矩阵 $M_k$ 将其写成如下形式：&lt;/p>
$$ PE_{pos+k} = M_k \cdot PE_{pos} $$
&lt;p>由此，Attention机制就可以通过内积计算，轻松识别单词之间“相距多远”这种相对距离。此外，通过组合不同波长的多个正弦和余弦波，它还具有这样一个优点：无论文章多长，都能生成唯一的位置向量。&lt;/p>
&lt;p>最终的输入矩阵 $X_{input}$，是将单词的嵌入向量与此位置编码相加而得的结果。&lt;/p>
$$ X_{input} = X + PE $$
&lt;hr>
&lt;h1 id="4-self-attention自注意力机制的深渊数学">4. Self-Attention（自注意力机制）的深渊数学
&lt;/h1>&lt;p>终于我们要踏入Transformer最重要的组件——**Self-Attention（自注意力机制）**了。Self-Attention的目的是“计算文章中所有单词彼此之间的相关度，并将每个单词的向量更新为考虑到上下文的更丰富的表示”。&lt;/p>
&lt;p>在这里，使用了“搜索系统”的类比。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Query (Q)&lt;/strong>: 查询（搜索词）。“我现在正在寻找的信息是什么？”&lt;/li>
&lt;li>&lt;strong>Key (K)&lt;/strong>: 键（标题）。“我所拥有的信息是什么？”&lt;/li>
&lt;li>&lt;strong>Value (V)&lt;/strong>: 值（实体）。“我实际提供的信息是什么？”&lt;/li>
&lt;/ul>
&lt;h2 id="41-矩阵-q-k-v-的生成">4.1 矩阵 $Q, K, V$ 的生成
&lt;/h2>&lt;p>对于输入矩阵 $X \in \mathbb{R}^{N \times d_{model}}$（为了简化说明，这里忽略批次大小），通过将其与可学习的权重矩阵 $W^Q, W^K, W^V \in \mathbb{R}^{d_{model} \times d_k}$ 相乘，来计算查询 $Q$、键 $K$、值 $V$。（通常 $d_k = d_v = d_{model} / h$）&lt;/p>
$$ Q = X W^Q $$
$$ K = X W^K $$
$$ V = X W^V $$
&lt;p>这里，$Q, K, V$ 全都是 $\mathbb{R}^{N \times d_k}$ 的矩阵。&lt;/p>
&lt;h2 id="42-attention-score的计算内积">4.2 Attention Score的计算（内积）
&lt;/h2>&lt;p>为了衡量每个单词的Query与其他所有单词的Key之间有多大程度的关联，需要计算向量的&lt;strong>内积&lt;/strong>。用矩阵运算来写的话如下所示：&lt;/p>
$$ \text{Scores} = Q K^T $$
&lt;p>通过该计算得到的矩阵 $\text{Scores} \in \mathbb{R}^{N \times N}$ 的每个元素 $s_{ij}$，表示第 $i$ 个单词的Query与第 $j$ 个单词的Key的内积，即“相关度的强度”。&lt;/p>
&lt;h2 id="43-缩放scale">4.3 缩放（Scale）
&lt;/h2>&lt;p>通过内积计算得分存在一个问题。当向量的维度 $d_k$ 变大时，内积的值会变得极端地大或极端地小。&lt;/p>
&lt;p>让我们从数学上证明这一点。
假设查询的每个元素 $q \sim \mathcal{N}(0, 1)$、键的每个元素 $k \sim \mathcal{N}(0, 1)$ 服从独立的标准正态分布。
求内积 $q \cdot k = \sum_{i=1}^{d_k} q_i k_i$ 的均值和方差。
均值：因为 $\mathbb{E}[q_i k_i] = \mathbb{E}[q_i] \mathbb{E}[k_i] = 0 \times 0 = 0$，所以总和的均值也是 $0$。
方差：由于独立性，$q_i k_i$ 的方差为 $\text{Var}(q_i k_i) = \mathbb{E}[(q_i k_i)^2] - (\mathbb{E}[q_i k_i])^2 = 1 \times 1 - 0 = 1$。
因此，整个内积的方差等于维度数 $d_k$。&lt;/p>
$$ \text{Var}(q \cdot k) = d_k $$
&lt;p>当方差变大时，在之后应用的Softmax函数中，除最大值以外的梯度会变得极其小，发生“梯度消失”，导致学习无法进行。
为了防止这种情况，将得分除以 $\sqrt{d_k}$（进行缩放），从而使方差始终保持在 $1$。&lt;/p>
$$ \text{Scaled Scores} = \frac{Q K^T}{\sqrt{d_k}} $$
&lt;h2 id="44-通过softmax函数实现概率化">4.4 通过Softmax函数实现概率化
&lt;/h2>&lt;p>为了将得到的得分转换为总和为 $1$ 的概率分布（权重），对每一行应用 &lt;strong>Softmax函数&lt;/strong>。&lt;/p>
$$ a_{ij} = \text{softmax}(s_i)_j = \frac{\exp(s_{ij} / \sqrt{d_k})}{\sum_{m=1}^N \exp(s_{im} / \sqrt{d_k})} $$
&lt;p>矩阵 $A \in \mathbb{R}^{N \times N}$ 被称为Attention Weight（注意力权重）矩阵。查看该矩阵的每一行 $i$，它用0到1的值表示了“在理解单词 $i$ 时，应该对其他哪个单词 $j$ 给予多大的关注（Attention）”。&lt;/p>
&lt;h2 id="45-value的加权和">4.5 Value的加权和
&lt;/h2>&lt;p>最后，使用得到的Attention Weight矩阵 $A$，计算Value矩阵 $V$ 的加权和。&lt;/p>
$$ \text{Output} = A V = \text{softmax}\left(\frac{Q K^T}{\sqrt{d_k}}\right) V $$
&lt;p>通过该运算输出的矩阵 $Z \in \mathbb{R}^{N \times d_v}$，就是“考虑到上下文而更新的单词向量表示”的集合。
这就是论文中所定义的 &lt;strong>Scaled Dot-Product Attention&lt;/strong> 的全貌。&lt;/p>
&lt;hr>
&lt;h1 id="5-multi-head-attention多头注意力机制">5. Multi-Head Attention（多头注意力机制）
&lt;/h1>&lt;p>如果只进行一次Attention计算（单头），可能只能捕捉到一个视角（例如“语法关系”）的上下文。因此，为了同时捕捉语言所具有的多种语义和句法关系（如“主语和谓语”、“代词及其指代对象”等），引入了 &lt;strong>Multi-Head Attention&lt;/strong>。&lt;/p>
&lt;p>将前面的 $Q, K, V$ 的生成与Attention计算并行进行 $h$ 次（头的数量。在原论文中 $h=8$）。&lt;/p>
$$ \text{head}_i = \text{Attention}(X W_i^Q, X W_i^K, X W_i^V) $$
&lt;p>这里，$W_i^Q, W_i^K, W_i^V \in \mathbb{R}^{d_{model} \times d_k}$ 是第 $i$ 个头专用的可学习的权重矩阵。&lt;/p>
&lt;p>从各个头输出的结果 $\text{head}_i \in \mathbb{R}^{N \times d_v}$ 在横向上进行拼接（Concatenate）。&lt;/p>
$$ \text{Concat}(\text{head}_1, \dots, \text{head}_h) \in \mathbb{R}^{N \times (h \cdot d_v)} $$
&lt;p>通常设定使得 $h \cdot d_v = d_{model}$，因此拼接后的维度将再次恢复为与输入相同的 $d_{model}$。最后，将此矩阵乘以权重矩阵 $W^O \in \mathbb{R}^{d_{model} \times d_{model}}$，从而得到最终的输出。&lt;/p>
$$ \text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, \dots, \text{head}_h) W^O $$
&lt;div class="mermaid">graph TD
X["输入 X (Input X)"] --> Q1["Q1"]
X --> K1["K1"]
X --> V1["V1"]
Q1 &amp; K1 &amp; V1 --> H1["头 1 (Head 1)"]
X --> Q2["Q2"]
X --> K2["K2"]
X --> V2["V2"]
Q2 &amp; K2 &amp; V2 --> H2["头 2 (Head 2)"]
X --> QN["..."]
X --> KN["..."]
X --> VN["..."]
QN &amp; KN &amp; VN --> HN["头 h (Head h)"]
H1 &amp; H2 &amp; HN --> C["拼接 (Concatenate)"]
C --> WO["乘以 WO (Multiply by WO)"]
WO --> OUT["多头输出 (Multi-Head Output)"]&lt;/div>
&lt;hr>
&lt;h1 id="6-feed-forward-neural-network-ffn">6. Feed-Forward Neural Network (FFN)
&lt;/h1>&lt;p>Multi-Head Attention的输出接下来将被输入到 &lt;strong>Position-wise Feed-Forward Network (FFN)&lt;/strong> 中。
这是一个应用于序列中“每个位置（单词）独立”的两层全连接神经网络。&lt;/p>
&lt;p>用数学公式表示如下：&lt;/p>
$$ \text{FFN}(x) = \max(0, x W_1 + b_1) W_2 + b_2 $$
&lt;p>这里，$\max(0, z)$ 表示ReLU（Rectified Linear Unit）激活函数（在最近的模型中也经常使用GELU或SwiGLU）。&lt;/p>
&lt;p>这个网络的作用非常重要。Attention机制学习的是“单词之间的关系（空间・序列关系）”，而FFN负责的是“每个单词向量本身的非线性特征变换”。
通常，通过第一层的权重 $W_1$ 暂时将维度大幅扩大（例如从 $d_{model}=512$ 扩大到4倍的 $d_{ff}=2048$），在特征空间上进行复杂的计算后，再通过第二层的权重 $W_2$ 恢复到原来的维度。这种“维度的扩大与缩小”使模型的表达能力得到了飞跃性的提升。&lt;/p>
&lt;hr>
&lt;h1 id="7-残差连接residual-connection与层归一化layer-normalization">7. 残差连接（Residual Connection）与层归一化（Layer Normalization）
&lt;/h1>&lt;p>在深度学习中，如果加深网络的层数，在训练时就会出现梯度消失或爆炸的问题，导致无法很好地学习。为了防止这种情况发生，在Transformer的每个子层（Attention和FFN）周围，都配置了 &lt;strong>残差连接（Residual Connection）&lt;/strong> 和 &lt;strong>层归一化（Layer Normalization）&lt;/strong>。&lt;/p>
&lt;p>用数学公式来写，子层的输出将被如下处理：&lt;/p>
$$ \text{Output} = \text{LayerNorm}(x + \text{Sublayer}(x)) $$
&lt;h2 id="71-残差连接-x--textsublayerx">7.1 残差连接 ($x + \text{Sublayer}(x)$)
&lt;/h2>&lt;p>将输入 $x$ 直接加到子层的输出上。这样一来，在反向传播时梯度会通过捷径直接传递到较浅的层，因此即使层数加深，训练也能保持稳定。&lt;/p>
&lt;h2 id="72-layer-normalization的数学">7.2 Layer Normalization的数学
&lt;/h2>&lt;p>Layer Normalization是计算特征维度方向上的均值和方差，并对数据进行归一化的技术。在批次大小 $B$、序列长度 $N$、维度数 $d_{model}$ 的输入中，对某一个单词向量 $x \in \mathbb{R}^{d_{model}}$ 进行归一化。&lt;/p>
&lt;p>计算均值 $\mu$ 和方差 $\sigma^2$。
&lt;/p>
$$ \mu = \frac{1}{d_{model}} \sum_{i=1}^{d_{model}} x_i $$
$$ \sigma^2 = \frac{1}{d_{model}} \sum_{i=1}^{d_{model}} (x_i - \mu)^2 $$
&lt;p>然后，得到归一化后的输出 $\hat{x}$。
&lt;/p>
$$ \text{LN}(x) = \frac{x - \mu}{\sqrt{\sigma^2 + \epsilon}} \odot \gamma + \beta $$
&lt;p>
（$\epsilon$ 是防止除以零的极小常数。$\gamma, \beta$ 是可学习的缩放和平移参数）&lt;/p>
&lt;p>之所以采用层方向的归一化（Layer Normalization）而不是批次方向的归一化（Batch Normalization），是因为在处理像文章这样长度不固定的序列数据时，批次间的统计量容易变得不稳定。通过Layer Normalization，Transformer可以实现不依赖于批次大小的稳定学习。&lt;/p>
&lt;hr>
&lt;h1 id="8-解码器特有的结构masked-attention与cross-attention">8. 解码器特有的结构：Masked Attention与Cross-Attention
&lt;/h1>&lt;p>到目前为止讲解的结构都是编码器的。在生成文章的解码器模块中，结构稍有不同。&lt;/p>
&lt;h2 id="81-masked-multi-head-attention">8.1 Masked Multi-Head Attention
&lt;/h2>&lt;p>解码器的作用是“根据过去的单词预测下一个单词”。因此，如果在训练时看到了“未来的单词”，那就等于作弊了。为了防止这种情况发生的数学操作就是 &lt;strong>Masking（掩码）&lt;/strong>。&lt;/p>
&lt;p>对于得分矩阵 $Q K^T$，将其加上一个在上三角部分（相当于未来信息）设置为接近 $-\infty$ 的极小值的掩码矩阵 $M$。&lt;/p>
$$ M_{ij} = \begin{cases} 0 &amp; (i \le j) \\ -\infty &amp; (i > j) \end{cases} $$
$$ \text{Masked Attention}(Q, K, V) = \text{softmax}\left(\frac{Q K^T + M}{\sqrt{d_k}}\right) V $$
&lt;p>在计算Softmax函数时，因为 $\exp(-\infty) = 0$，所以对未来单词的Attention Weight将完全变为 $0$。由此，就可以实现保留了因果关系（Causality）的自回归生成。&lt;/p>
&lt;h2 id="82-encoder-decoder-cross-attention">8.2 Encoder-Decoder Cross-Attention
&lt;/h2>&lt;p>解码器的第二个子层是参考编码器输出的 &lt;strong>Cross-Attention&lt;/strong>。
在这里，$Q$ 是从前一个解码器层生成的，而 $K$ 和 $V$ 则是从编码器的最后一层的输出中生成的。&lt;/p>
$$ Q_{decoder} = X_{dec} W^Q $$
$$ K_{encoder} = X_{enc} W^K $$
$$ V_{encoder} = X_{enc} W^V $$
&lt;p>通过这个计算，在翻译等任务中，模型就能学习到“现在正在翻译的单词，与原外语文章的哪个部分有着很强的关联”。&lt;/p>
&lt;hr>
&lt;h1 id="9-计算复杂度与现代优化的数学">9. 计算复杂度与现代优化的数学
&lt;/h1>&lt;p>Transformer是一个出色的模型，但也存在源于其数学结构的“弱点”。
请关注Self-Attention的计算复杂度。在计算得分矩阵 $Q K^T$ 时，因为要将 $(N \times d_k)$ 的矩阵与 $(d_k \times N)$ 的矩阵相乘，所以其计算复杂度为 &lt;strong>$O(N^2 \cdot d_{model})$&lt;/strong>。&lt;/p>
&lt;p>也就是说，&lt;strong>相对于序列长度 $N$，计算复杂度和内存使用量呈平方级增长&lt;/strong>。
如果文章很短则不成问题，但如果想将像一整本书这样超长的上下文输入到LLM中，$N$ 就会达到数万至数十万，在传统的Attention计算下GPU的内存会瞬间耗尽。&lt;/p>
&lt;p>为了打破这种 $O(N^2)$ 的诅咒，近年来从数学和硬件方法的角度提出了各种各样的优化方案。
其中具有代表性的例子就是 &lt;strong>FlashAttention&lt;/strong>。FlashAttention是一种为了将GPU的内存层级（SRAM和HBM）之间的数据传输（内存访问）降至最低，将Attention计算分割成瓦片状（Tiling）来执行的算法。尽管在数学公式上它输出的是与标准Attention完全相同的结果（Exact Attention），但通过硬件级别的优化实现了惊人的速度提升和内存削减，使得GPT-4等长上下文模型的实现成为了可能。&lt;/p>
&lt;p>除此之外，将计算复杂度近似为 $O(N \log N)$ 或 $O(N)$ 的 Sparse Attention 和 Linear Attention 等研究也正在积极进行中。&lt;/p>
&lt;hr>
&lt;h1 id="10-实现的印象类似pytorch的伪代码">10. 实现的印象（类似PyTorch的伪代码）
&lt;/h1>&lt;p>如果将到目前为止的数学结构落实到实际的编程代码（Python / PyTorch）中，你会发现它可以用令人惊讶的简单方式编写出来。下面展示了Self-Attention核心部分的伪代码。&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;/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">torch&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">torch.nn.functional&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">F&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="k">def&lt;/span> &lt;span class="nf">scaled_dot_product_attention&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">k&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">mask&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">None&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># q, k, v的形状: [batch_size, num_heads, seq_length, d_k]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">d_k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">q&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">size&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 1. 通过内积计算得分: Q * K^T&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">scores&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">matmul&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">k&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">transpose&lt;/span>&lt;span class="p">(&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>&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">scores&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">scores&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">sqrt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">d_k&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. 掩码 (适用于Masked Attention的情况)&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">mask&lt;/span> &lt;span class="ow">is&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">scores&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">scores&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">masked_fill&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mask&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mf">1e9&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"># 4. 通过Softmax实现概率化&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">attention_weights&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">F&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">softmax&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">scores&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">dim&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"># 5. 乘以Value矩阵&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">matmul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">attention_weights&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">v&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">output&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">attention_weights&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>可以直观地看到，用数学公式表达的 $Q K^T / \sqrt{d_k}$ 被直接实现为了 &lt;code>torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(d_k)&lt;/code>。借助于高级优化库的力量，只需几行代码就能实现数学理论，这是深度学习非常有趣的一面。&lt;/p>
&lt;hr>
&lt;h1 id="结语从数学公式中看到的智能的形状">结语：从数学公式中看到的“智能”的形状
&lt;/h1>&lt;p>在本文中，我们解开了Transformer模型深处的数学结构。&lt;/p>
&lt;p>将单词映射到多维向量空间的Embedding、通过合成三角波来表示位置信息的Positional Encoding、以及源于信息检索类比的矩阵内积计算——Self-Attention机制。这每一个组件，不过是线性代数、微积分、概率统计等基础数学知识的累积罢了。&lt;/p>
&lt;p>然而，当这些简单的矩阵运算重叠了无数层，通过几十亿、几千亿的参数从庞大的数据集中学习模式时，在那之中就浮现出了仿佛能理解我们的“语言”、进行逻辑推理、时而还能产生创造性想法的“智能的形状”。&lt;/p>
&lt;p>正如“Attention Is All You Need”这个带有挑衅性的标题所示，彻底抛弃复杂的循环处理和卷积处理，专注于纯粹的“Attention（相关度）”计算，这种架构的美，正是在于其数学上的简洁性。&lt;/p>
&lt;p>未来，也许会出现超越Transformer的全新架构（例如State Space Model的Mamba等），但Transformer所建立的“通过Attention理解上下文”的数学框架，必将被永远铭刻在AI的历史中。&lt;/p>
&lt;p>如果你今后有机会使用ChatGPT或Claude等LLM，请想象一下在它们的后台中，每秒正进行着数万亿次 $Q K^T$ 的矩阵乘法运算，以及Softmax函数正在计算着概率的情景。这样你对技术的清晰度会提升，也一定能感受到AI的世界更加有趣。&lt;/p>
&lt;h3 id="参考文献">参考文献
&lt;/h3>&lt;ul>
&lt;li>Vaswani, A., et al. (2017). &amp;ldquo;Attention Is All You Need.&amp;rdquo; &lt;em>Advances in Neural Information Processing Systems&lt;/em>.&lt;/li>
&lt;li>Alammar, J. (2018). &amp;ldquo;The Illustrated Transformer.&amp;rdquo;&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>&lt;em>这篇文章是为学习自然语言处理和AI的数学基础的人们撰写的指南。如果有任何疑问或讨论，请务必在评论区告诉我！&lt;/em>&lt;/p></description></item><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>【数学解说】让高中生也能看懂的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></channel></rss>