<?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/ru/tags/math/</link><description>Recent content in Math on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ru</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 22:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/ru/tags/math/index.xml" rel="self" type="application/rss+xml"/><item><title>【Для начинающих】Разбор математической структуры модели Transformer</title><link>http://kenji.blog/ru/p/transformer-mathematical-structure/</link><pubDate>Fri, 11 Sep 2026 22:00:00 +0900</pubDate><guid>http://kenji.blog/ru/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) и ИИ в целом. Впервые предложенная в 2017 году исследователями Google в статье «Attention Is All You Need», эта модель функционирует как сердце крупномасштабных языковых моделей (LLM), которые сейчас доминируют в мире, таких как серия GPT от OpenAI (базовая технология ChatGPT), BERT от Google и Claude от Anthropic.&lt;/p>
&lt;p>Однако, хотя часто встречаются качественные объяснения работы Transformer, такие как «понимание контекста с помощью механизма внимания (Attention)», на самом деле существует на удивление мало подробных объяснений &lt;strong>математической структуры&lt;/strong>, стоящей за этим, для начинающих. Чтобы по-настоящему понять, как ИИ обрабатывает «слова» как «математические формулы» и генерирует удивительно естественные тексты, необходимо разобраться в его математических механизмах.&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["Входные токены"] --> B["Входное вложение"]
B --> C["Позиционное кодирование"]
C --> D["Многоголовое внутреннее внимание"]
D --> E["Сложение и нормализация слоя"]
E --> F["Сеть прямой связи"]
F --> G["Сложение и нормализация слоя"]
G --> H["Вывод на следующий слой"]
C -.->|"Остаточное соединение"| E
E -.->|"Остаточное соединение"| G&lt;/div>
&lt;p>С этого момента мы шаг за шагом рассмотрим математические операции, выполняемые в каждом компоненте.&lt;/p>
&lt;hr>
&lt;h1 id="3-векторизация-слов-и-позиционное-кодирование-positional-encoding">3. Векторизация слов и позиционное кодирование (Positional Encoding)
&lt;/h1>&lt;p>Компьютеры не могут понимать текст как таковой. Введенный текст сначала разбивается на единицы, называемые «токенами (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$, а размерность вектора вложения (embedding) равна $d_{model}$ (в оригинальной статье $d_{model} = 512$). Каждое слово $w_i$ преобразуется в вектор $x_i \in \mathbb{R}^{d_{model}}$ с использованием матрицы вложения $W_E \in \mathbb{R}^{V \times 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>.
Позиционное кодирование $PE$ для $i$-го измерения слова, находящегося в позиции $pos$, вычисляется с использованием следующих тригонометрических функций:&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$, сдвинутой на $k$ относительно позиции $pos$, может быть представлено как линейная комбинация позиционного кодирования позиции $pos$. Другими словами, используя матрицу $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 — &lt;strong>Self-Attention (Механизм внутреннего внимания)&lt;/strong>. Цель 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}}$ (для упрощения мы здесь игнорируем размер батча (batch size)), умножая на обучаемые весовые матрицы $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>Каждый элемент $s_{ij}$ полученной таким образом матрицы $\text{Scores} \in \mathbb{R}^{N \times N}$ представляет собой скалярное произведение Query $i$-го слова и Key $j$-го слова, то есть «силу связи».&lt;/p>
&lt;h2 id="43-масштабирование-scale">4.3 Масштабирование (Scale)
&lt;/h2>&lt;p>При вычислении оценки (score) с помощью скалярного произведения возникает одна проблема. Если размерность вектора $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, применяемой после этого, становятся экстремально маленькими для значений, отличных от максимального, происходит «исчезновение градиента», и обучение останавливается.
Чтобы предотвратить это, оценка (score) делится (масштабируется) на $\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>К полученным оценкам по строкам применяется &lt;strong>функция Softmax&lt;/strong>, чтобы преобразовать их в распределение вероятностей (веса), сумма которых равна $1$.&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, которые показывают, «насколько много внимания (Attention) следует уделить любому другому слову $j$ для понимания слова $i$».&lt;/p>
&lt;h2 id="45-взвешенная-сумма-value">4.5 Взвешенная сумма Value
&lt;/h2>&lt;p>Наконец, используя полученную матрицу весов внимания $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"] --> Q1["Q1"]
X --> K1["K1"]
X --> V1["V1"]
Q1 &amp; K1 &amp; V1 --> H1["Голова 1"]
X --> Q2["Q2"]
X --> K2["K2"]
X --> V2["V2"]
Q2 &amp; K2 &amp; V2 --> H2["Голова 2"]
X --> QN["..."]
X --> KN["..."]
X --> VN["..."]
QN &amp; KN &amp; VN --> HN["Голова h"]
H1 &amp; H2 &amp; HN --> C["Объединить"]
C --> WO["Умножить на WO"]
WO --> OUT["Многоголовый вывод"]&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$ временно значительно увеличивает размерность (например, в 4 раза с $d_{model}=512$ до $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>В глубоком обучении по мере углубления слоев сети возникает проблема исчезновения или взрыва градиентов во время обучения, из-за чего обучение проходит неудачно. Для предотвращения этого вокруг каждого подслоя (Attention и FFN) в Transformer размещены &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$ прибавляется матрица маски $M$, в которой верхнетреугольная часть (соответствующая будущей информации) заполнена очень маленькими значениями, близкими к $-\infty$.&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$ достигнет десятков или сотен тысяч, и память GPU мгновенно исчерпается при традиционных вычислениях Attention.&lt;/p>
&lt;p>Чтобы разорвать это проклятие $O(N^2)$, в последние годы были предложены различные оптимизации с точки зрения математики и аппаратного обеспечения.
Типичным примером является &lt;strong>FlashAttention&lt;/strong>. FlashAttention — это алгоритм, который делит вычисления Attention на плитки (Tiling), чтобы минимизировать передачу данных (доступ к памяти) между иерархиями памяти GPU (SRAM и HBM). Хотя математически он выдает точно такой же результат, как стандартный Attention (Exact Attention), оптимизация на аппаратном уровне обеспечивает резкое ускорение и сокращение используемой памяти, что сделало возможным создание моделей с длинным контекстом, таких как GPT-4.&lt;/p>
&lt;p>Кроме того, активно ведутся исследования таких подходов, как Sparse Attention и Linear Attention, которые аппроксимируют вычислительную сложность до $O(N \log N)$ или $O(N)$.&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» (Внимание - это все, что вам нужно), красота этой архитектуры, отбросившей сложную рекуррентную и сверточную обработку и специализирующейся исключительно на вычислении «внимания (степени связи)», заключается именно в ее математической простоте.&lt;/p>
&lt;p>Возможно, в будущем появятся новые архитектуры, превосходящие Transformer (например, Mamba, являющаяся State Space Model), но математическая база «понимания контекста с помощью Attention», созданная Transformer, навсегда останется в истории ИИ.&lt;/p>
&lt;p>Если у вас будет возможность использовать LLM, такие как ChatGPT или Claude в будущем, представьте себе, как в фоновом режиме каждую секунду вычисляются триллионы матричных умножений $Q K^T$, а функция Softmax выдает вероятности. Разрешение вашего понимания технологий повысится, и мир ИИ покажется вам еще более интересным.&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>Эта статья была написана как руководство для тех, кто изучает математические основы обработки естественного языка и ИИ. Если у вас есть вопросы или вы хотите что-то обсудить, пожалуйста, дайте знать в комментариях!&lt;/em>&lt;/p></description></item><item><title>Основы и реализация криптографии с использованием малой теоремы Ферма</title><link>http://kenji.blog/ru/p/fermats-little-theorem-cryptography-implementation/</link><pubDate>Fri, 11 Sep 2026 22:00:00 +0900</pubDate><guid>http://kenji.blog/ru/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>Это читается как «$a$ и $b$ сравнимы по модулю $n$». Например, остаток от деления 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$. В качестве $a$ выберем целые числа, не кратные $p$.&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$, полученное умножением каждого элемента на целое число $a$, взаимно простое с $p$:
&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>Функция Эйлера (также называемая тотиентом или $\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 или обмен ключами Диффи-Хеллмана) необходимо быстро находить «огромные простые числа», длина которых может составлять сотни цифр. Однако, чтобы определить, является ли огромное число $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 -- "Да" --> RandomA["Случайно выбрать целое число a в диапазоне 1 &lt; a &lt; p-1"]
RandomA --> Calc["Вычислить модульное возведение в степень a^(p-1) mod p"]
Calc --> CheckPrime{"Результат равен 1 ?"}
CheckPrime -- "Нет" --> ReturnComposite["p является составным (точно)"]
CheckPrime -- "Да" --> Increment["Увеличить i на 1"]
Increment --> Condition
Condition -- "Нет" --> ReturnPrime["p, вероятно, является простым (с высокой вероятностью)"]
ReturnComposite --> End["Конец"]
ReturnPrime --> End&lt;/div>
&lt;h3 id="53-подводный-камень-числа-кармайкла-псевдопростые">5.3 Подводный камень: числа Кармайкла (псевдопростые)
&lt;/h3>&lt;p>Тест Ферма очень быстр, но у него есть один существенный недостаток. Существуют такие дьявольские числа, которые, будучи составными, удовлетворяют условию $a^{N-1} \equiv 1 \pmod N$ для всех возможных $a$. Такие числа называются &lt;strong>числами Кармайкла (Carmichael numbers)&lt;/strong>. Наименьшим числом Кармайкла является $561$ ($3 \times 11 \times 17$).&lt;/p>
&lt;p>Из-за существования чисел Кармайкла только лишь с помощью теста Ферма нельзя абсолютно точно определить простоту числа. Поэтому в реальных криптографических системах (таких как OpenSSL) стандартно используется усовершенствованная версия теста Ферма — &lt;strong>тест простоты Миллера-Рабина (Miller-Rabin Primality Test)&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"># Выбираем случайное число a от 2 до p-2&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>Самым великим применением малой теоремы Ферма (и теоремы Эйлера) является &lt;strong>алгоритм RSA&lt;/strong>, разработанный в 1977 году Ривестом, Шамиром и Адлеманом.
RSA — это инновационная криптографическая система с «открытым ключом», в которой ключ для шифрования (открытый ключ) доступен всему миру, а ключ для дешифрования (закрытый ключ) известен только самому получателю.&lt;/p>
&lt;p>Эта асимметричность основана на вычислительной безопасности, обусловленной тем, что «разложение огромного составного числа на простые множители является чрезвычайно сложной задачей».&lt;/p>
&lt;h3 id="71-механизм-rsa-генерация-ключей-шифрование-дешифрование">7.1 Механизм RSA (генерация ключей, шифрование, дешифрование)
&lt;/h3>&lt;p>Давайте рассмотрим весь процесс обмена данными в алгоритме RSA с помощью диаграммы последовательности Mermaid.&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>Выбирается целое число $e$ (открытая экспонента), взаимно простое с $\phi(N)$ (часто используется $e = 65537$).&lt;/li>
&lt;li>Вычисляется модульный обратный элемент $d$ (закрытая экспонента) для $e$. То есть, находится такое $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. Хотя это обучающая «игрушечная реализация», используемая математика абсолютно реальна.&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"># Расширенный алгоритм Евклида (находим x, y для ax + by = gcd(a,b))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Используется для нахождения d из e*d ≡ 1 (mod φ(N))&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 лет спустя, в 1970-х годах, на заре компьютерных сетей теорема Ферма совершила драматическое возрождение в качестве криптографической технологии, необходимой для создания безопасных протоколов связи. Метод проверки простоты, основанный на малой теореме Ферма, и алгоритм RSA, основанный на теореме Эйлера, буквально поддерживают инфраструктуру современного Интернета.&lt;/p>
&lt;p>И сообщения в мессенджерах, которые мы отправляем каждый день, и покупки на Amazon — все это танцует на фундаменте одной простой и красивой формулы: $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/ru/p/lattice-based-cryptography-math-intuition/</link><pubDate>Fri, 11 Sep 2026 21:00:00 +0900</pubDate><guid>http://kenji.blog/ru/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). Эти методы шифрования основывают свою безопасность на математической сложности таких задач, как &amp;ldquo;факторизация целых чисел&amp;rdquo; и &amp;ldquo;проблема дискретного логарифма&amp;rdquo;, которые, как считается, не могут быть эффективно решены (требуют экспоненциального времени) классическими компьютерами.&lt;/p>
&lt;p>Однако &amp;ldquo;алгоритм Шора&amp;rdquo;, опубликованный Питером Шором (Peter Shor) в 1994 году, потряс криптографический мир. Этот алгоритм математически доказал, что когда будут созданы крупномасштабные квантовые компьютеры, они смогут решать проблемы факторизации и дискретного логарифма за полиномиальное время. Это означает, что широко используемая сегодня криптография с открытым ключом в будущем станет полностью уязвимой для расшифровки.&lt;/p>
&lt;p>Чтобы противостоять этой &amp;ldquo;квантовой угрозе&amp;rdquo; (Quantum Threat), возникла острая необходимость в исследовании новых методов шифрования, которые трудно взломать даже с помощью квантовых компьютеров. Эта область называется &amp;ldquo;постквантовой криптографией&amp;rdquo; (Post-Quantum Cryptography: PQC).&lt;/p>
&lt;p>В PQC существует несколько перспективных кандидатов, таких как криптография на основе хешей, криптография на основе кодов, многомерная полиномиальная криптография и криптография на основе изогений. Однако &amp;ldquo;криптография на решетках&amp;rdquo; (Lattice-based cryptography) в настоящее время привлекает наибольшее внимание и является центральным элементом процесса стандартизации PQC Национальным институтом стандартов и технологий США (NIST). По сравнению с другими методами, криптография на решетках обладает чрезвычайно высокой скоростью шифрования и дешифрования, а также выдающейся особенностью: она имеет очень сильное доказательство безопасности в криптографии — сведение от &amp;ldquo;сложности в худшем случае&amp;rdquo; (Worst-case complexity) к &amp;ldquo;сложности в среднем случае&amp;rdquo; (Average-case complexity).&lt;/p>
&lt;p>В этой статье мы подробно рассмотрим криптографию на решетках, начиная с математического определения &amp;ldquo;решетки&amp;rdquo; (Lattice), которая является ее основой. Мы обсудим сложные задачи на решетках, такие как SVP (задача поиска кратчайшего вектора) и CVP (задача поиска ближайшего вектора), а также проблему &amp;ldquo;LWE (Learning With Errors)&amp;rdquo;, которая является сердцем современной криптографии на решетках. Будут представлены математические формулы, геометрическая интуиция и конкретные числовые примеры.&lt;/p>
&lt;h1 id="2-математическое-определение-решетки-lattice-и-геометрическая-интуиция">2. Математическое определение решетки (Lattice) и геометрическая интуиция
&lt;/h1>&lt;h2 id="21-векторные-пространства-и-решетки">2.1 Векторные пространства и решетки
&lt;/h2>&lt;p>В математике &amp;ldquo;решетка&amp;rdquo; (Lattice) — это множество дискретных точек, регулярно расположенных в $n$-мерном действительном векторном пространстве $\mathbb{R}^n$. Оно похоже на векторное пространство (Vector Space), изучаемое в линейной алгебре, но есть одно решающее отличие. В то время как векторное пространство — это непрерывное пространство, представленное линейными комбинациями базисных векторов с &amp;ldquo;действительными коэффициентами&amp;rdquo;, решетка — это дискретное пространство, представленное линейными комбинациями базисных векторов с &amp;ldquo;целыми коэффициентами&amp;rdquo;.&lt;/p>
&lt;p>Дадим строгое математическое определение. Рассмотрим $n$ ($n \le m$) линейно независимых векторов $\mathbf{b}_1, \mathbf{b}_2, \dots, \mathbf{b}_n$ в $m$-мерном действительном векторном пространстве $\mathbb{R}^m$. Пусть матрица, содержащая эти векторы в качестве столбцов, будет $B = [\mathbf{b}_1, \mathbf{b}_2, \dots, \mathbf{b}_n] \in \mathbb{R}^{m \times n}$. Эта матрица $B$ называется &amp;ldquo;базисом&amp;rdquo; (Basis) решетки.&lt;/p>
&lt;p>Решетка $\mathcal{L}(B)$, порожденная этим базисом $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}$. В результате образуется &amp;ldquo;множество дискретных точек&amp;rdquo;, подобных равномерно расположенным перекресткам, а не непрерывное пространство с бесконечным множеством точек.&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$ на координатной плоскости. Это самая простая &amp;ldquo;квадратная решетка&amp;rdquo;.&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>Существует важное свойство, касающееся основы безопасности криптографии на решетках: &amp;ldquo;Существует бесконечное количество базисов, порождающих одну и ту же решетку&amp;rdquo;.&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$ называется &amp;ldquo;унимодулярной матрицей&amp;rdquo; (Unimodular matrix).&lt;/p>
&lt;p>Основная идея применения в криптографии состоит в том, чтобы использовать &amp;ldquo;хороший базис&amp;rdquo; (почти ортогональный, состоящий из коротких векторов) в качестве секретного ключа, и &amp;ldquo;плохой базис&amp;rdquo; (состоящий из очень длинных векторов, сильно скошенных друг относительно друга) в качестве открытого ключа. Вычислить хороший базис из плохого становится крайне сложно по мере увеличения размерности. В этом заключается базовая интуиция криптографии на решетках.&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>Математически это задача поиска вектора $\mathbf{v}$, такого что достигается минимум $\min_{\mathbf{v} \in \mathcal{L}(B) \setminus \{\mathbf{0}\}} \| \mathbf{v} \|$. Эта минимальная длина обозначается как $\lambda_1(\mathcal{L})$ и называется &amp;ldquo;первым последовательным минимумом решетки&amp;rdquo;.&lt;/p>
&lt;p>В низких измерениях, таких как 2D или 3D, можно нарисовать график и визуально найти самый короткий вектор, или эффективно решить задачу с помощью алгоритма редукции решетки Гаусса. Однако известно, что когда размерность $n$ достигает сотен или тысяч, точное решение SVP является NP-трудным.&lt;/p>
&lt;p>В реальной криптографии используется приближенная SVP ($\gamma$-SVP), которая находит &amp;ldquo;приближенно короткий вектор&amp;rdquo;, а не строго кратчайший вектор. Когда коэффициент аппроксимации $\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{v} \in \mathcal{L}(B)$, ближайшую к $\mathbf{t}$.&lt;/p>
&lt;p>Математически это задача поиска точки решетки $\mathbf{v}$, такой что достигается минимум $\min_{\mathbf{v} \in \mathcal{L}(B)} \| \mathbf{v} - \mathbf{t} \|$.&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 (алгоритм Ленстры-Ленстры-Ловаса). Алгоритм LLL работает за полиномиальное время и может в некоторой степени &amp;ldquo;редуцировать&amp;rdquo; базис решетки до &amp;ldquo;хорошего базиса&amp;rdquo;. Однако кратчайший вектор, который может найти алгоритм LLL, имеет коэффициент аппроксимации, который экспоненциально зависит от размерности ($2^{\mathcal{O}(n)}$) по сравнению с истинным кратчайшим вектором, что недостаточно для нарушения безопасности криптографии.&lt;/p>
&lt;p>С помощью более мощных алгоритмов редукции базиса, таких как алгоритм BKZ (Block Korkine-Zolotarev), который является улучшением LLL, можно найти более короткие векторы, но вычислительная сложность возрастает экспоненциально в зависимости от размера блока. В криптографии на решетках безопасные параметры (например, значение размерности $n$) определяются путем оценки времени выполнения этого алгоритма BKZ. В текущих стандартных параметрах PQC размерность $n$ выбирается от 500 до 1000 и более, и считается, что для взлома потребуется время, превышающее возраст Вселенной, даже при использовании суперкомпьютеров или будущих квантовых компьютеров.&lt;/p>
&lt;h1 id="4-математическая-формулировка-проблемы-lwe-learning-with-errors">4. Математическая формулировка проблемы LWE (Learning With Errors)
&lt;/h1>&lt;p>Большая часть современной криптографии на решетках основана на проблеме &amp;ldquo;LWE (Learning With Errors)&amp;rdquo;, предложенной Одедом Регевом (Oded Regev) в 2005 году. Красота проблемы LWE заключается в простоте ее формулировки и мощном математическом доказательстве &amp;ldquo;сведения от сложности в худшем случае к сложности в среднем случае&amp;rdquo;.&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)$, мы можем легко восстановить секретный вектор $\mathbf{s}$, используя &amp;ldquo;метод исключения Гаусса&amp;rdquo; из линейной алгебры. Эту задачу легко решить за полиномиальное время.&lt;/p>
&lt;h2 id="42-определение-проблемы-lwe-добавление-шума">4.2 Определение проблемы LWE: Добавление шума
&lt;/h2>&lt;p>Что произойдет, если мы добавим немного &amp;ldquo;шума (ошибки)&amp;rdquo; в эту задачу?
В этом суть проблемы 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}$ на их основе называется &amp;ldquo;проблемой поиска LWE&amp;rdquo; (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, называемая &amp;ldquo;проблемой решения LWE&amp;rdquo; (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, становятся &amp;ldquo;вычислительно неразличимыми&amp;rdquo; (Computationally Indistinguishable) от полностью случайных пар данных. Это свойство является основой того, что криптография на базе LWE может генерировать &amp;ldquo;шифротексты, неотличимые от случайных чисел&amp;rdquo;.&lt;/p>
&lt;h2 id="44-сведение-от-сложности-в-худшем-случае-к-сложности-в-среднем-случае-теорема-регева">4.4 Сведение от сложности в худшем случае к сложности в среднем случае (Теорема Регева)
&lt;/h2>&lt;p>Величайшим достижением Одеда Регева было математическое связывание сложности этой проблемы LWE со сложностью вышеупомянутых задач на решетках (SVP и CVP).&lt;/p>
&lt;p>Используя квантовое сведение (Quantum reduction), он доказал, что: &amp;ldquo;Если существует алгоритм полиномиального времени, который может решить проблему LWE в среднем (для случайно выбранных $A$ и $\mathbf{e}$), то существует квантовый алгоритм полиномиального времени, который может решить Gap-SVP в худшем случае (самый сложный случай) для любой решетки&amp;rdquo;. (Позже Пейкерт (Peikert) и другие также продемонстрировали классическое сведение).&lt;/p>
&lt;p>Это свойство, похожее на мечту в криптографии. Причина в том, что оно устраняет опасение: &amp;ldquo;Шифр может быть взломан, потому что мы случайно выбрали слабый ключ (часть среднего случая)&amp;rdquo;, и дает сильную гарантию: &amp;ldquo;Если мы можем решить LWE в среднем, то мы можем решить все сложные задачи на решетках (поэтому LWE абсолютно сложна)&amp;rdquo;.&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-криптосистема-регева">5. Построение криптографии с открытым ключом с использованием LWE (Криптосистема Регева)
&lt;/h1>&lt;p>Поняв сложность проблемы LWE, давайте посмотрим на базовую криптосистему с открытым ключом, предложенную Одедом Регевом, чтобы увидеть, как с ее помощью выполняются шифрование и дешифрование. Здесь мы объясним самый основной механизм шифрования 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>Открытый ключ - это буквально &amp;ldquo;экземпляр проблемы LWE&amp;rdquo;. Поиск секретного ключа $\mathbf{s}$ по открытому ключу $(A, \mathbf{b})$ эквивалентен решению задачи поиска 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>Интуитивный смысл шифрования заключается во взятии &amp;ldquo;суммы случайного подмножества&amp;rdquo; для матрицы открытого ключа $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{u}, v)$, используя свой секретный ключ $\mathbf{s}$.&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}$, Боб может точно определить (расшифровать) сообщение $M$, просто посмотрев, к чему ближе результат вычислений: к $0$ или к $\lfloor \frac{q}{2} \rfloor$. Это прекрасный механизм, благодаря которому работает криптография на базе 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>Я думаю, что трудно прочувствовать это только с помощью ряда формул, поэтому давайте фактически проследим вычисления от шифрования до дешифрования, установив очень маленькие числовые параметры.
(&lt;em>В реальных криптографических системах для обеспечения безопасности используются значения $n$ от 500 и выше, и $q$ от нескольких тысяч и выше&lt;/em>)&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$ явно ближе к $8$, чем к $0$, Боб смог правильно восстановить $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), которую мы объясняли до сих пор, имеет очень сильное доказательство безопасности, но на практике у нее есть фатальные недостатки: &amp;ldquo;огромный размер ключа&amp;rdquo; и &amp;ldquo;высокая вычислительная стоимость&amp;rdquo;.&lt;/p>
&lt;p>В Standard LWE открытый ключ включает огромную матрицу $A \in \mathbb{Z}_q^{m \times n}$. Когда параметр $n$ достигает сотен или тысяч, размер этой матрицы достигает нескольких мегабайт, что слишком тяжело для передачи и приема каждый раз в интернет-протоколах связи (таких как TLS). Кроме того, умножение матрицы на вектор требует сложности вычислений $\mathcal{O}(n^2)$.&lt;/p>
&lt;p>Для решения этой проблемы были внедрены &amp;ldquo;Ring-LWE (RLWE)&amp;rdquo; и &amp;ldquo;Module-LWE (MLWE)&amp;rdquo;, которые включают алгебраическую структуру, называемую кольцами многочленов (Polynomial rings), в решетку.&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$ выбирается как степень двойки).&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>Поскольку это умножение многочленов, за счет использования &amp;ldquo;теоретико-числового преобразования (Number Theoretic Transform: NTT)&amp;rdquo;, аналогичного быстрому преобразованию Фурье (FFT), вычислительная сложность может быть резко снижена до $\mathcal{O}(n \log n)$. Кроме того, поскольку размер открытого ключа уменьшается с матрицы до одного многочлена, размер данных сокращается до $\mathcal{O}(n)$. Это дает подавляющее преимущество в пропускной способности связи.&lt;/p>
&lt;p>С математической точки зрения Ring-LWE сводится к задаче на решетке с особой симметрией, называемой &amp;ldquo;идеальной решеткой&amp;rdquo; (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 эффективен, существовало некоторое опасение, что специальная алгебраическая структура идеальных решеток может стать отправной точкой для будущих атак. Поэтому &amp;ldquo;Module-LWE (MLWE)&amp;rdquo; был создан, чтобы взять &amp;ldquo;лучшее из обоих миров&amp;rdquo;: консервативную безопасность Standard LWE и эффективность Ring-LWE.&lt;/p>
&lt;p>В Module-LWE мы рассматриваем небольшие матрицы и векторы, элементами которых являются многочлены. Другими словами, мы работаем с модулями над кольцом.
В настоящее время &amp;ldquo;CRYSTALS-Kyber&amp;rdquo; (название стандарта: ML-KEM), выбранный NIST в качестве стандарта алгоритма инкапсуляции ключей (KEM) для PQC, построен именно на сложности этой проблемы Module-LWE.&lt;/p>
&lt;h1 id="8-почему-это-безопасно-против-квантовых-компьютеров">8. Почему это безопасно против квантовых компьютеров?
&lt;/h1>&lt;p>Наконец, мы коснемся сути вопроса: &amp;ldquo;Почему считается, что криптография на решетках не может быть взломана даже с помощью квантовых компьютеров?&amp;rdquo;&lt;/p>
&lt;p>Алгоритм Шора, с помощью которого квантовые компьютеры взламывают шифрование RSA и криптографию на эллиптических кривых, по сути, является алгоритмом для решения &amp;ldquo;проблемы скрытой подгруппы (Hidden Subgroup Problem: HSP)&amp;rdquo;. Математическая структура (конечные абелевы группы), лежащая в основе RSA и ECC, обладает периодичностью, и эта периодичность (скрытая подгруппа) может быть извлечена за один шаг путем использования специальной операции квантового алгоритма, называемой квантовым преобразованием Фурье (QFT).&lt;/p>
&lt;p>Однако задачи на решетках в корне отличаются. Решетки также имеют периодичность, но то, что ищется в SVP и CVP, — это геометрические нелинейные свойства, такие как &amp;ldquo;кратчайшее расстояние&amp;rdquo; и &amp;ldquo;удаление шума&amp;rdquo;. Даже если применить &amp;ldquo;квантовое преобразование Фурье на абелевой группе&amp;rdquo;, подобно алгоритму Шора, непосредственно к задачам на решетках, полезную информацию для их решения нельзя эффективно извлечь. На сегодняшний день не было найдено квантовых алгоритмов, способных решить SVP или LWE за полиномиальное время. Широко распространено мнение, что даже с мощью параллельных вычислений квантовых компьютеров единственным эффективным средством является поиск, близкий к полному перебору (ускорение в виде квадратного корня с помощью алгоритма Гровера).&lt;/p>
&lt;h1 id="9-заключение">9. Заключение
&lt;/h1>&lt;p>В этой статье мы подробно рассмотрели математическую интуицию криптографии на решетках, начиная с геометрического определения решеток, переходя к формулировке проблемы LWE и заканчивая построением криптографии с открытым ключом.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Решетка (Lattice)&lt;/strong> — это дискретное пространство, представленное линейными комбинациями базисных векторов с целыми коэффициентами, и в высоких измерениях становится трудно найти &amp;ldquo;хороший базис&amp;rdquo;, близкий к ортогональному (SVP).&lt;/li>
&lt;li>&lt;strong>Проблема LWE (Learning With Errors)&lt;/strong> — это проблема решения систем линейных уравнений с шумом. Поскольку она связана со сложностью задач на решетках в худшем случае, это обеспечивает сильное доказательство безопасности.&lt;/li>
&lt;li>Используя проблему LWE, шифрование и дешифрование (&lt;strong>Криптосистема Регева&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>В условиях надвигающегося беспрецедентного сдвига вычислительной парадигмы в виде квантовых компьютеров весьма романтично думать, что &amp;ldquo;криптография на решетках&amp;rdquo;, родившаяся из глубин классической линейной алгебры и теории чисел, сформирует основу будущей интернет-безопасности. Математика, лежащая в основе криптографии на решетках, не является чрезмерно сложной, и ее красивую структуру можно полностью понять, обладая базовыми знаниями в области линейной алгебры и теории вероятностей. Мы надеемся, что эта статья поможет вам понять криптографию на решетках, которая является ядром PQC.&lt;/p></description></item><item><title>Гипотеза Римана и распределение простых чисел: глубокая связь с современной криптографией</title><link>http://kenji.blog/ru/p/riemann-hypothesis-prime-distribution-cryptography/</link><pubDate>Fri, 11 Sep 2026 16:00:00 +0900</pubDate><guid>http://kenji.blog/ru/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>Ближе всего к разгадке тайны простых чисел подошла &lt;strong>«Гипотеза Римана» (Riemann Hypothesis)&lt;/strong>, предложенная немецким математиком Бернхардом Риманом в 1859 году. Гипотеза Римана является одной из самых важных и нерешенных проблем в современной математике. Она также включена в список Задач тысячелетия Института Клэя, за решение которой назначена награда в 1 миллион долларов.&lt;/p>
&lt;p>На первый взгляд может показаться, что сложная проблема чистой математики, касающаяся распределения простых чисел, не имеет ничего общего с нашей повседневной жизнью. Однако безопасность инфраструктуры современного общества, в особенности интернета, опирается на &lt;strong>современные криптографические технологии, такие как шифрование RSA и криптография на эллиптических кривых (ECC)&lt;/strong>, которые глубоко зависят от свойств огромных простых чисел.&lt;/p>
&lt;p>В этой статье мы совершим математическое путешествие: от распределения простых чисел к теореме о распределении простых чисел, дзета-функции Римана и, наконец, к сути гипотезы Римана. Мы чрезвычайно подробно и глубоко рассмотрим, как всё это связано с современными криптографическими технологиями, и что произойдет с миром, если гипотеза Римана будет доказана.&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-летний гениальный математик Карл Фридрих Гаусс, вычислив огромные таблицы простых чисел, обнаружил, что частота появления простых чисел убывает обратно пропорционально натуральному логарифму $\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-дзета-функция-римана-и-произведение-эйлера">3. Дзета-функция Римана и произведение Эйлера
&lt;/h1>&lt;p>Самым мощным оружием в анализе распределения простых чисел является &lt;strong>дзета-функция Римана (Riemann Zeta Function)&lt;/strong>. Изначально это был бесконечный ряд, определенный Леонардом Эйлером для вещественных чисел $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> Изучение дзета-функции равносильно изучению распределения простых чисел.&lt;/p>
&lt;hr>
&lt;h1 id="4-аналитическое-продолжение-и-расширение-на-комплексную-плоскость">4. Аналитическое продолжение и расширение на комплексную плоскость
&lt;/h1>&lt;p>Гениальность Римана заключалась в том, что он расширил переменную $s$ в функции $\zeta(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>Далее он вывел красивое функциональное уравнение (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>Комплексные числа $s$, при которых значение дзета-функции становится равным 0, называются «нулями».
Из функционального уравнения следует, что когда $s$ — отрицательное четное число ($-2, -4, -6, \dots$), значение $\sin(\pi s / 2)$ равно 0, поэтому $\zeta(s) = 0$. Они называются &lt;strong>тривиальными нулями (Trivial zeros)&lt;/strong>.&lt;/p>
&lt;p>Однако для распределения простых чисел важны другие нули — те, что находятся в «критической полосе» (Critical strip) $0 \le \sigma \le 1$, так называемые &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(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["Дзета-функция Римана ζ(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>Почему гипотеза Римана так важна? Потому что нули дзета-функции &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$ дзета-функции.
Главным членом является $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$ преобразуется в зашифрованный текст $C$ с использованием открытого ключа $e, N$.
&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, нужно найти (факторизовать) исходные простые числа $p$ и $q$ из гигантского числа $N$. Считается, что даже с использованием самых передовых на сегодня алгоритмов (таких как общий метод решета числового поля, 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>В настоящее время на практике применяется вероятностный алгоритм — &lt;strong>тест простоты Миллера-Рабина (Miller-Rabin primality test)&lt;/strong>. Этот алгоритм быстр, но существует крайне низкая вероятность того, что составное число будет ошибочно признано простым (так называемые «псевдопростые числа»).&lt;/p>
&lt;p>Однако, если предположить, что &lt;strong>«Обобщенная гипотеза Римана» (Generalized Riemann Hypothesis, GRH)&lt;/strong>, которая распространяет гипотезу Римана на L-функции Дирихле, верна, то ситуация кардинально меняется.
Если GRH истинна, математически гарантируется верхняя граница количества проверок в тесте Миллера-Рабина, и вероятностный алгоритм &lt;strong>превращается в «детерминированный алгоритм полиномиального времени»&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>Чтобы точно оценить частоту появления гладких чисел, необходимо глубокое понимание распределения простых чисел. Здесь также используются методы аналитической теории чисел, напрямую связанные с дзета-функцией и гипотезой Римана. Если гипотеза Римана будет доказана, и погрешность распределения простых чисел будет полностью определена, станет возможным более точно установить пределы эффективности алгоритмов факторизации.&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>Если будут разработаны неизвестные ранее методы алгебраической геометрии или некоммутативной геометрии, позволяющие полностью манипулировать свойствами нулей дзета-функции Римана, нельзя исключать, что это в конечном итоге приведет к открытию революционного алгоритма факторизации (например, классического алгоритма, который снизит время вычислений до полиномиального). В этом смысле криптографы никогда не смогут отвести взгляд от того, что происходит вокруг гипотезы Римана.&lt;/p>
&lt;h3 id="квантовые-компьютеры-и-алгоритм-шора">Квантовые компьютеры и алгоритм Шора
&lt;/h3>&lt;p>Куда более прямой и реальной угрозой для криптографических технологий является не доказательство гипотезы Римана, а &lt;strong>квантовые компьютеры&lt;/strong>. Опубликованный Питером Шором (Peter Shor) в 1994 году «алгоритм Шора» доказал, что при наличии достаточно мощного квантового компьютера факторизация может быть решена за полиномиальное время. Это фундаментально разрушит шифрование RSA и криптографию на эллиптических кривых.&lt;/p>
&lt;p>В настоящее время по всему миру идет переход на &lt;strong>постквантовую криптографию (Post-Quantum Cryptography, PQC)&lt;/strong> (например, криптографию на решетках), которую не смогут взломать даже квантовые компьютеры. Криптографические технологии, основанные на простых числах, возможно, подходят к концу своего золотого века, но математическая ценность самих простых чисел не будет утрачена никогда.&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>Неустанные поиски простых чисел, ведущиеся со времен Древней Греции, благодаря гению Римана превратились в прекрасную симфонию на комплексной плоскости (нули дзета-функции). И поразительно, что этот кристалл чистейшей математики спустя столетия применяется как самый мощный щит, гарантирующий безопасность интернет-общества.&lt;/p>
&lt;p>Гипотеза Римана символизирует одновременно «абстрактную красоту» математики и её «удивительную применимость в физическом мире и реальном обществе».&lt;/p>
&lt;p>Когда эта гигантская математическая гора, вершины которой еще никто не достиг, будет однажды покорена, мы не только полностью поймем истину Вселенной, скрытую в простых числах, но и обретем новый взгляд на фундамент нашего информационного общества. Изучение криптографии — это, по сути, путешествие по страницам истории человеческой мудрости.&lt;/p></description></item><item><title>【Математическое объяснение】Как работает алгоритм RSA, объяснено для старшеклассников</title><link>http://kenji.blog/ru/p/rsa-encryption-math-explained-for-beginners/</link><pubDate>Fri, 11 Sep 2026 13:00:00 +0900</pubDate><guid>http://kenji.blog/ru/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 (шифрование RSA). Оплата кредитными картами в интернет-магазинах, общение с друзьями в соцсетях, отправка и получение конфиденциальной корпоративной информации — большая часть связи, которую мы небрежно используем каждый день, защищена алгоритмом RSA или технологиями-преемниками.&lt;/p>
&lt;p>Однако при слове &amp;ldquo;криптография&amp;rdquo; (шифрование) вы можете представить себе сложные шифровальные машины из шпионских фильмов или сверхсложную математику, понятную лишь избранным гениям. Безусловно, современная теория криптографии основана на продвинутой математике, но &lt;strong>фундаментальный механизм алгоритма RSA вполне можно понять, обладая знаниями математики на уровне старшей школы (свойства целых чисел, простые числа, сравнения по модулю и т.д.)&lt;/strong>.&lt;/p>
&lt;p>В этой статье, взяв за отправную точку знания школьной математики, мы шаг за шагом подробно объясним, на каких математических принципах работает алгоритм RSA и почему его так сложно взломать. Мы будем использовать конкретные примеры и объяснять все так тщательно, чтобы даже те, кто немного не в ладах с математикой, смогли во всем разобраться.&lt;/p>
&lt;hr>
&lt;h2 id="1-симметричное-и-асимметричное-шифрование-с-открытым-ключом">1. Симметричное и асимметричное шифрование (с открытым ключом)
&lt;/h2>&lt;p>Прежде чем переходить к математическим механизмам алгоритма RSA, давайте проясним базовые концепции криптографии. Методы шифрования в целом делятся на два типа: &amp;ldquo;шифрование с симметричным ключом&amp;rdquo; и &amp;ldquo;шифрование с открытым ключом&amp;rdquo; (асимметричное).&lt;/p>
&lt;h3 id="11-ограничения-симметричного-шифрования">1.1 Ограничения симметричного шифрования
&lt;/h3>&lt;p>Большинство шифров, используемых с древних времен, называются &amp;ldquo;системами симметричного шифрования&amp;rdquo;. Это метод, при котором &lt;strong>для &amp;ldquo;шифрования&amp;rdquo; (превращения сообщения в секретный шифр) и &amp;ldquo;расшифрования&amp;rdquo; (возвращения зашифрованного текста в исходное сообщение) используется один и тот же ключ&lt;/strong>.&lt;/p>
&lt;p>Например, предположим, что Алиса отправляет Бобу секретное письмо. Алиса кладет письмо в шкатулку и запирает ее на навесной замок (общий ключ). Чтобы Боб мог открыть эту шкатулку, у него должен быть точно такой же ключ, какой использовала Алиса.&lt;/p>
&lt;p>У этого метода есть серьезная проблема: &amp;ldquo;проблема распределения ключей&amp;rdquo;. Если находящиеся далеко друг от друга Алиса и Боб связываются впервые, как им безопасно обменяться ключами, чтобы их никто не перехватил? Если ключ украдет третье лицо во время пересылки, все последующие зашифрованные сообщения станут достоянием гласности.&lt;/p>
&lt;h3 id="12-революционное-изобретение-шифрование-с-открытым-ключом">1.2 Революционное изобретение: &amp;ldquo;Шифрование с открытым ключом&amp;rdquo;
&lt;/h3>&lt;p>Для решения этой проблемы с распределением ключей была изобретена &amp;ldquo;система шифрования с открытым ключом&amp;rdquo;. Алгоритм RSA относится к этому типу.&lt;/p>
&lt;p>В системе с открытым ключом используются &lt;strong>два разных ключа: &amp;ldquo;ключ для шифрования (открытый ключ)&amp;rdquo; и &amp;ldquo;ключ для расшифрования (закрытый ключ)&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;ol>
&lt;li>Получатель, Боб, создает пару ключей: &amp;ldquo;открытый ключ&amp;rdquo; и &amp;ldquo;закрытый ключ&amp;rdquo;.&lt;/li>
&lt;li>Боб публикует свой &amp;ldquo;открытый ключ&amp;rdquo; на весь мир (неважно, кто его получит).&lt;/li>
&lt;li>Отправительница, Алиса, использует &amp;ldquo;открытый ключ&amp;rdquo; Боба, чтобы зашифровать сообщение и отправить его.&lt;/li>
&lt;li>Зашифрованное сообщение можно расшифровать только с помощью &amp;ldquo;закрытого ключа&amp;rdquo;, который есть только у Боба.&lt;/li>
&lt;/ol>
&lt;p>Если снова привести аналогию с навесным замком, то Боб создает множество &amp;ldquo;открытых навесных замков (открытых ключей)&amp;rdquo; и разбрасывает их по всему миру. Алиса кладет свое сообщение для Боба в шкатулку, берет один из замков Боба и защелкивает его. Как только замок закрыт, его можно открыть только с помощью &amp;ldquo;мастер-ключа (закрытого ключа)&amp;rdquo;, который есть у Боба. Даже если кто-то украдет шкатулку по дороге, он не сможет ее открыть без мастер-ключа.&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;strong>&amp;ldquo;односторонняя функция (математическая головоломка с односторонним движением)&amp;rdquo;&lt;/strong>, которая позволяет &amp;ldquo;легко зашифровать с помощью открытого ключа, но абсолютно невозможно расшифровать без закрытого&amp;rdquo;. И в качестве деталей для этой головоломки были выбраны хорошо знакомые нам &amp;ldquo;простые числа&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="2-математические-основы-rsa-1-простые-числа-и-факторизация">2. Математические основы RSA 1: Простые числа и факторизация
&lt;/h2>&lt;p>Безопасность алгоритма RSA основана на математическом факте: &lt;strong>&amp;ldquo;факторизация (разложение на простые множители) огромных чисел — чрезвычайно сложная задача&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;h3 id="21-что-такое-простые-числа">2.1 Что такое простые числа?
&lt;/h3>&lt;p>Простое число — это &amp;ldquo;натуральное число больше 1, которое делится без остатка только на 1 и на само себя&amp;rdquo;.
Пример: $2, 3, 5, 7, 11, 13, 17, 19, 23...$&lt;/p>
&lt;p>Простые числа — это словно &amp;ldquo;атомы&amp;rdquo; всех целых чисел. Любое натуральное число можно разложить в виде произведения простых чисел. Это называется &lt;strong>факторизацией (разложением на простые множители)&lt;/strong>. Например, $60 = 2^2 \times 3 \times 5$. Тот факт, что любое число можно разложить на простые множители единственным образом (если не учитывать порядок), известен как &amp;ldquo;основная теорема арифметики&amp;rdquo;.&lt;/p>
&lt;h3 id="22-сложность-факторизации-односторонняя-функция">2.2 Сложность факторизации (Односторонняя функция)
&lt;/h3>&lt;p>Здесь важна асимметрия: &lt;strong>&amp;ldquo;умножать легко, а раскладывать на множители — сложно&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>Например, попробуйте вычислить в уме произведение следующих двух простых чисел:
$11 \times 13 = ?$
Это просто. Ответ $143$.&lt;/p>
&lt;p>А как насчет следующего числа?
Разложите на простые множители $323$.
Ну как? Это должно занять некоторое время. (Ответ $17 \times 19$).&lt;/p>
&lt;p>Если числа небольшие, человек еще может как-то справиться, но по мере увеличения чисел вычисления становятся взрывообразно сложными даже для компьютеров. В распространенном сегодня алгоритме RSA используется число $N = p \times q$, полученное путем перемножения двух невероятно огромных простых чисел $p$ и $q$ размером 2048 бит (около 600 цифр в десятичной системе).&lt;/p>
&lt;p>Когда даны два огромных простых числа $p$ и $q$, вычисление $N$ занимает у компьютера долю секунды (меньше миллисекунды). Однако, если дано только $N$, поиск исходных $p$ и $q$ займет столько времени, что даже самые быстрые суперкомпьютеры современности не смогут решить эту задачу и за триллионы лет.&lt;/p>
&lt;p>Эта &lt;strong>&amp;ldquo;вычислительная асимметрия (в одну сторону легко, в обратную — сложно)&amp;rdquo;&lt;/strong> является фундаментом для создания связи между открытым и закрытым ключами.&lt;/p>
&lt;hr>
&lt;h2 id="3-математические-основы-rsa-2-сравнения-арифметика-остатков--модульная-арифметика">3. Математические основы RSA 2: Сравнения (Арифметика остатков / модульная арифметика)
&lt;/h2>&lt;p>Вычисления в алгоритме RSA выполняются не в виде привычных нам сложений и умножений, где числа могут бесконечно расти, а в мире &amp;ldquo;остатков&amp;rdquo; от деления на определенное число. Это называется &lt;strong>сравнением по модулю (модульной арифметикой)&lt;/strong>.&lt;/p>
&lt;h3 id="31-математика-часов">3.1 Математика часов
&lt;/h3>&lt;p>Модульную арифметику часто сравнивают с &amp;ldquo;математикой часов&amp;rdquo;. Предположим, сейчас 10 часов. Который час будет через 5 часов? $10 + 5 = 15$ часов, но на обычных 12-часовых часах мы ответим &amp;ldquo;3 часа&amp;rdquo;. Это потому, что остаток от деления 15 на 12 равен 3.&lt;/p>
&lt;p>В математике это записывается следующим образом:
&lt;/p>
$$ 15 \equiv 3 \pmod{12} $$
&lt;p>
Это читается как &amp;ldquo;15 сравнимо с 3 по модулю 12 (остатки от деления на 12 равны)&amp;rdquo;.&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>Особенно важным является свойство &amp;ldquo;возведения в степень&amp;rdquo;. Оно означает, что &lt;strong>&amp;ldquo;остаток от степени равен степени от остатка&amp;rdquo;&lt;/strong>.
Например, мы хотим найти остаток от деления $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. Появляется &amp;ldquo;теорема Эйлера&amp;rdquo;, которая является обобщением &amp;ldquo;малой теоремы Ферма&amp;rdquo;.&lt;/p>
&lt;h3 id="41-функция-эйлера-тотиент-phin">4.1 Функция Эйлера (тотиент) $\phi(N)$
&lt;/h3>&lt;p>Функция Эйлера (функция $\phi$) для некоторого натурального числа $N$ — это функция, которая возвращает &lt;strong>&amp;ldquo;количество натуральных чисел от 1 до $N$, которые взаимно просты с $N$ (наибольший общий делитель равен 1)&amp;rdquo;&lt;/strong>.&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>
Это свойство служит &amp;ldquo;потайной дверью&amp;rdquo; (лазейкой) в алгоритме RSA. Тот, кто знает $p$ и $q$ (создатель ключа), может вычислить $\phi(N)$ за мгновение, но третье лицо, знающее только $N$, не сможет вычислить $\phi(N)$ без разложения $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>Это удивительное свойство: &amp;ldquo;если умножить некоторое число $a$ само на себя $\phi(N)$ раз и разделить на $N$, остаток всегда будет равен $1$&amp;rdquo;. (Если $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;strong>&amp;ldquo;после шифрования и расшифрования мы возвращаемся к исходному значению&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h2 id="5-алгоритм-rsa-шаги-генерации-ключей-шифрования-и-расшифрования">5. Алгоритм RSA: Шаги генерации ключей, шифрования и расшифрования
&lt;/h2>&lt;p>Теперь, когда у нас есть базовые знания, давайте посмотрим на конкретные шаги алгоритма RSA. Алгоритм RSA можно разделить на три основные фазы: &amp;ldquo;1. Генерация ключей&amp;rdquo;, &amp;ldquo;2. Шифрование&amp;rdquo; и &amp;ldquo;3. Расшифрование&amp;rdquo;.&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["Выбрать e, взаимно простое с φ(N)"]
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>Получатель, Боб, генерирует для себя &amp;ldquo;открытый ключ&amp;rdquo; и &amp;ldquo;закрытый ключ&amp;rdquo;.&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> Выбирается целое число $e$ такое, что $1 &lt; e &lt; \phi(N)$ и $e$ взаимно просто с $\phi(N)$.&lt;/li>
&lt;li>&lt;strong>Вычисление закрытого ключа $d$:&lt;/strong> Находится целое число $d$, удовлетворяющее следующему условию:
$$ e \times d \equiv 1 \pmod{\phi(N)} $$
Иными словами, &amp;ldquo;$d$ — это такое число, при котором остаток от деления $e \times d$ на $\phi(N)$ равен $1$&amp;rdquo;.&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>Она вычисляет &amp;ldquo;остаток $C$ от деления сообщения $M$ в степени $e$ на $N$&amp;rdquo;. Это $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>Если вычислить &amp;ldquo;остаток от деления зашифрованного текста $C$ в степени $d$ на $N$&amp;rdquo;, то удивительным образом восстанавливается исходное сообщение $M$!&lt;/p>
&lt;hr>
&lt;h2 id="6-почему-расшифрование-возвращает-исходное-сообщение-математическое-доказательство">6. Почему расшифрование возвращает исходное сообщение? (Математическое доказательство)
&lt;/h2>&lt;p>Вы можете задаться вопросом: &amp;ldquo;Как так получается, что простое возведение $C$ в степень $d$ возвращает нас к исходному $M$?&amp;rdquo;. Вот тут-то и проявляет свою силу ранее упомянутая &amp;ldquo;теорема Эйлера&amp;rdquo;.&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)}$. Это означает, что &amp;ldquo;$ed$ — это число, кратное $\phi(N)$, плюс $1$&amp;rdquo;. Используя целое число $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$, поэтому получить $M$ из $C$ может только Боб, владеющий ключом $d$.&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>Выбираем открытый ключ $e$, взаимно простой с $\phi(N)=120$. Пусть $e=7$.&lt;/li>
&lt;li>Находим закрытый ключ $d$. Ищем такое $d$, чтобы выполнялось $7 \times d \equiv 1 \pmod{120}$.
В уравнении $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>, результат блестяще окажется равным &amp;ldquo;&lt;strong>9&lt;/strong>&amp;rdquo;! Исходное сообщение успешно получено.&lt;/p>
&lt;hr>
&lt;h2 id="8-как-найти-закрытый-ключ-d-расширенный-алгоритм-евклида">8. Как найти закрытый ключ $d$: Расширенный алгоритм Евклида
&lt;/h2>&lt;p>В примере с ручными вычислениями мы нашли $d=103$, подобрав $k$ интуитивно, но если числа состоят из сотен цифр, этот метод не сработает. В реальных программах используется алгоритм, называемый &lt;strong>&amp;ldquo;расширенным алгоритмом Евклида&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>Решить $7d \equiv 1 \pmod{120}$ — это то же самое, что найти целые числа $d$ и $y$, удовлетворяющие уравнению $7d + 120y = 1$. Выполняя алгоритм Евклида в обратном порядке, это можно вычислить механически.&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>В мире по модулю $120$, число $-17$ имеет то же значение, что и $120 - 17 = 103$. Следовательно, мы мгновенно находим $d = 103$. Этот метод позволяет выполнять вычисления очень быстро даже для огромных чисел.&lt;/p>
&lt;hr>
&lt;h2 id="9-другое-лицо-rsa-цифровая-подпись">9. Другое лицо RSA: Цифровая подпись
&lt;/h2>&lt;p>Замечательной особенностью алгоритма RSA является то, что, поменяв ролями открытый и закрытый ключи, его можно использовать как &lt;strong>&amp;ldquo;цифровую подпись&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>При шифровании порядок был таким: &amp;ldquo;шифрование открытым ключом $\Rightarrow$ расшифрование закрытым ключом&amp;rdquo;,
а в цифровой подписи выполняется процедура &amp;ldquo;шифрование закрытым ключом $\Rightarrow$ расшифрование открытым ключом&amp;rdquo;.&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$ для выполнения проверочных вычислений. Если результат вычислений совпадает с исходным сообщением, это одновременно доказывает, что &amp;ldquo;эти данные могли быть созданы только с помощью закрытого ключа Алисы&amp;rdquo; и что &amp;ldquo;сообщение не было изменено в пути&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="10-rsa-на-практике-в-коде">10. RSA на практике в коде
&lt;/h2>&lt;p>Возведение в степень, которое сложно сделать вручную, очень легко реализовать с помощью Python. Ниже представлен код на Python, позволяющий опробовать основную логику алгоритма RSA.&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>Функция &lt;code>pow(base, exp, mod)&lt;/code> в Python внутри использует быстрый алгоритм, называемый &amp;ldquo;алгоритмом возведения в степень справа налево&amp;rdquo; (быстрое возведение в степень по модулю), поэтому вычисления для чисел из сотен цифр завершаются мгновенно.&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$, но очень сложно найти $p, q$ по заданному $N$.&lt;/li>
&lt;li>&lt;strong>Сравнения и теорема Эйлера:&lt;/strong> Благодаря закону $a^{\phi(N)} \equiv 1 \pmod N$ создается волшебная потайная дверь, позволяющая &amp;ldquo;вернуться к исходному значению при возведении в определенную степень&amp;rdquo;.&lt;/li>
&lt;li>&lt;strong>Открытый и закрытый ключи:&lt;/strong> Зашифровать может кто угодно, но расшифровать способен только законный получатель.&lt;/li>
&lt;/ol>
&lt;p>Используемое в настоящее время в алгоритме RSA число $N$ состоит из более чем 600 цифр, и даже если задействовать все суперкомпьютеры в мире, разложение его на множители займет больше времени, чем возраст Вселенной. Однако, когда &amp;ldquo;квантовые компьютеры&amp;rdquo;, исследования которых ведутся в последние годы, будут введены в практическое использование в будущем, с помощью &amp;ldquo;алгоритма Шора&amp;rdquo; эту задачу факторизации можно будет решить за мгновение. По этой причине во всем мире сейчас ускоренными темпами ведется разработка &amp;ldquo;постквантовой криптографии&amp;rdquo;, которая не может быть взломана даже квантовыми компьютерами.&lt;/p>
&lt;p>Продвинутая математика, которая часто считается &amp;ldquo;бесполезной&amp;rdquo;, на самом деле фундаментально защищает нашу повседневную жизнь. Алгоритм RSA — это прекрасный учебный материал, который демонстрирует глубину и красоту математики. Мы надеемся, что благодаря этой статье вы смогли хотя бы немного почувствовать, насколько интересными могут быть криптография и математика.&lt;/p></description></item></channel></rss>