miller-robin
This commit is contained in:
parent
eda99760f3
commit
48f640b55d
10 changed files with 1291 additions and 1 deletions
102
80-math/Untitled.md
Normal file
102
80-math/Untitled.md
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
**Великая (Последняя) теорема Ферма:** $a^n + b^n = c^n$ не имеет решений в целых числах для $n > 2$.
|
||||
|
||||
**Малая теорема Ферма:** Если $p$ — простое число, то для любого целого $a$: $a^p \equiv a \pmod p$.
|
||||
|
||||
Это отличный вопрос. В IT "математика" — понятие растяжимое. Кому-то нужно считать интегралы, а кому-то достаточно понимать, как работает остаток от деления.
|
||||
|
||||
Вот "дорожная карта" математических тем, отфильтрованная по реальным задачам, а не университетской программе.
|
||||
|
||||
***
|
||||
|
||||
### 1. Embedded-разработчик (Rust/C)
|
||||
Здесь математика очень "железная". Вам нужно понимать, как числа живут в памяти и как физика переходит в цифру.
|
||||
|
||||
**Must Have (Обязательно):**
|
||||
* **Булева алгебра и Битовая магия:**
|
||||
* Операции `AND`, `OR`, `XOR`, `NOT`, сдвиги (`<<`, `>>`).
|
||||
* Маскирование битов (установка/сброс конкретных флагов в регистрах).
|
||||
* Понимание Little-endian vs Big-endian.
|
||||
* **Системы счисления:**
|
||||
* Свободное чтение Hex (0xFF) и Bin (0b1010).
|
||||
* Представление отрицательных чисел (Two's complement).
|
||||
* **Арифметика с фиксированной точкой (Fixed-point arithmetic):**
|
||||
* Как считать дробные числа на процессорах без FPU (например, `Q15`, `Q31` форматы).
|
||||
|
||||
**Advanced (Для серьезных задач):**
|
||||
* **Цифровая обработка сигналов (DSP):**
|
||||
* Преобразование Фурье (FFT) — если работаете со звуком или радио.
|
||||
* Цифровые фильтры (Калмана, PID-регуляторы) — если управляете дронами, моторами или температурой.
|
||||
|
||||
***
|
||||
|
||||
### 2. ML-инженер (Python — архитектура, Rust — инференс/оптимизация)
|
||||
Тут математика — это основной рабочий инструмент. Без неё вы просто "импортер библиотек".
|
||||
|
||||
**Must Have (Фундамент):**
|
||||
* **Линейная алгебра (Царица ML):**
|
||||
* Матричное умножение, векторы, тензоры.
|
||||
* Размерности пространств (понимание, почему `[3, 512] * [512, 10]` работает, а наоборот — нет).
|
||||
* Собственные векторы и значения (Eigenvectors) — для PCA и сжатия данных.
|
||||
* **Мат. анализ (Calculus):**
|
||||
* Градиенты и частные производные (суть Backpropagation).
|
||||
* Цепное правило (Chain rule).
|
||||
* Понимание того, что такое локальный минимум и седловая точка.
|
||||
* **Теория вероятностей и Статистика:**
|
||||
* Распределения (Нормальное, Бернулли, Пуассона).
|
||||
* Байесовская теорема.
|
||||
* Метрики (Precision, Recall, F1-score, ROC-AUC).
|
||||
|
||||
**Rust Specific (Для оптимизации):**
|
||||
* **Численные методы:** Как хранить `f32`/`f16` так, чтобы не потерять точность при миллионе сложений (проблемы float point arithmetic).
|
||||
* **SIMD-математика:** Параллельные вычисления векторов на CPU.
|
||||
|
||||
***
|
||||
|
||||
### 3. Backend-разработчик
|
||||
Здесь математика нужна для оценки нагрузки, защиты данных и правильной работы с деньгами.
|
||||
|
||||
**Must Have (Повседневность):**
|
||||
* **Асимптотический анализ (Big O):**
|
||||
* Понимать разницу между $O(N)$, $O(N \log N)$ и $O(N^2)$.
|
||||
* Понимать Cost Model (почему чтение из RAM быстрее чтения с SSD в математических порядках).
|
||||
* **Дискретная математика (Графы и Множества):**
|
||||
* Теория множеств (Set theory) — это база SQL (`JOIN`, `UNION`, `INTERSECT`).
|
||||
* Деревья (B-Tree, LSM-Tree) — чтобы понимать, как работают индексы в PostgreSQL.
|
||||
* Графы — если пишете социальные сети, карты или системы зависимостей.
|
||||
* **Модульная арифметика и Теория чисел:**
|
||||
* То, что мы обсуждали выше (простые числа, остатки от деления).
|
||||
* Основы криптографии (Хэширование, RSA, Эллиптические кривые).
|
||||
|
||||
**Ops/Highload:**
|
||||
* **Статистика для мониторинга:**
|
||||
* Перцентили (p50, p95, p99) — почему "среднее время ответа" лжет.
|
||||
* Гистограммы распределения запросов.
|
||||
|
||||
***
|
||||
|
||||
### 4. Frontend (SSR) разработчик
|
||||
Обычно математики тут меньше всего, если вы не делаете WebGL-игры или сложные дашборды.
|
||||
|
||||
**Must Have:**
|
||||
* **Базовая геометрия:**
|
||||
* Координаты (x, y), ширины, высоты.
|
||||
* Box Model (сложение отступов).
|
||||
* **Пропорции и проценты:**
|
||||
* Aspect Ratio, расчет резиновой верстки (`flex-grow`).
|
||||
|
||||
**Advanced (Анимации и Графика):**
|
||||
* **Тригонометрия:**
|
||||
* `Sin`, `Cos`, `Atan2` — если нужно вращать элементы или рисовать на `<canvas>`.
|
||||
* **Интерполяция:**
|
||||
* Кривые Безье (как работают `ease-in-out` анимации).
|
||||
* Линейная интерполяция (Lerp) для плавных переходов цветов или позиций.
|
||||
* **Матрицы трансформаций (CSS transform):**
|
||||
* Понимание, как работает `matrix3d()` для 3D-эффектов в браузере.
|
||||
|
||||
### Итог: Что учить в первую очередь?
|
||||
|
||||
Учитывая твой профиль (Rust, Backend, Systems):
|
||||
1. **Bitwise operations** (для низкоуровневых оптимизаций).
|
||||
2. **Big O & Data Structures** (деревья, хэш-мапы — база бэкенда).
|
||||
3. **Модульная арифметика** (для понимания криптографии и алгоритмов типа того же Миллера-Рабина).
|
||||
4. **Статистика (перцентили)** — чтобы грамотно делать дашборды мониторинга для своих сервисов.
|
||||
362
80-math/miller-rabin.md
Normal file
362
80-math/miller-rabin.md
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
|
||||
|
||||
пример кода, близкого к эталонному:
|
||||
```rust
|
||||
pub fn nth(n: u32) -> u32 {
|
||||
let mut num = 1;
|
||||
for _ in 0..=n {
|
||||
loop {
|
||||
num += 1;
|
||||
if miller_rabin(num as u64) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
num
|
||||
}
|
||||
|
||||
fn miller_rabin(n: u64) -> bool {
|
||||
const HINT: &[u64] = &[2];
|
||||
|
||||
// we have a strict upper bound, so we can just use the witness
|
||||
// table of Pomerance, Selfridge & Wagstaff and Jeaschke to be as
|
||||
// efficient as possible, without having to fall back to
|
||||
// randomness. Additional limits from Feitsma and Galway complete
|
||||
// the entire range of `u64`. See also:
|
||||
// https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test#Testing_against_small_sets_of_bases
|
||||
const WITNESSES: &[(u64, &[u64])] = &[
|
||||
(2_046, HINT),
|
||||
(1_373_652, &[2, 3]),
|
||||
(9_080_190, &[31, 73]),
|
||||
(25_326_000, &[2, 3, 5]),
|
||||
(4_759_123_140, &[2, 7, 61]),
|
||||
(1_112_004_669_632, &[2, 13, 23, 1662803]),
|
||||
(2_152_302_898_746, &[2, 3, 5, 7, 11]),
|
||||
(3_474_749_660_382, &[2, 3, 5, 7, 11, 13]),
|
||||
(341_550_071_728_320, &[2, 3, 5, 7, 11, 13, 17]),
|
||||
(3_825_123_056_546_413_050, &[2, 3, 5, 7, 11, 13, 17, 19, 23]),
|
||||
(std::u64::MAX, &[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]),
|
||||
];
|
||||
|
||||
if n % 2 == 0 {
|
||||
return n == 2;
|
||||
}
|
||||
if n == 1 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut d = n - 1;
|
||||
let mut s = 0;
|
||||
while d % 2 == 0 {
|
||||
d /= 2;
|
||||
s += 1
|
||||
}
|
||||
|
||||
let witnesses = WITNESSES
|
||||
.iter()
|
||||
.find(|&&(hi, _)| hi >= n)
|
||||
.map(|&(_, wtnss)| wtnss)
|
||||
.unwrap();
|
||||
'next_witness: for &a in witnesses.iter() {
|
||||
let mut power = mod_exp(a, d, n);
|
||||
assert!(power < n);
|
||||
if power == 1 || power == n - 1 {
|
||||
continue 'next_witness;
|
||||
}
|
||||
|
||||
for _r in 0..s {
|
||||
power = mod_sqr(power, n);
|
||||
assert!(power < n);
|
||||
if power == 1 {
|
||||
return false;
|
||||
}
|
||||
if power == n - 1 {
|
||||
continue 'next_witness;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn mod_mul_(a: u64, b: u64, m: u64) -> u64 {
|
||||
(u128::from(a) * u128::from(b) % u128::from(m)) as u64
|
||||
}
|
||||
|
||||
fn mod_mul(a: u64, b: u64, m: u64) -> u64 {
|
||||
match a.checked_mul(b) {
|
||||
Some(r) => {
|
||||
if r >= m {
|
||||
r % m
|
||||
} else {
|
||||
r
|
||||
}
|
||||
}
|
||||
None => mod_mul_(a, b, m),
|
||||
}
|
||||
}
|
||||
|
||||
fn mod_sqr(a: u64, m: u64) -> u64 {
|
||||
if a < (1 << 32) {
|
||||
let r = a * a;
|
||||
if r >= m { r % m } else { r }
|
||||
} else {
|
||||
mod_mul_(a, a, m)
|
||||
}
|
||||
}
|
||||
|
||||
fn mod_exp(mut x: u64, mut d: u64, n: u64) -> u64 {
|
||||
let mut ret: u64 = 1;
|
||||
while d != 0 {
|
||||
if d % 2 == 1 {
|
||||
ret = mod_mul(ret, x, n)
|
||||
}
|
||||
d /= 2;
|
||||
x = mod_sqr(x, n);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Что такое Miller-Rabin (с нуля, метафора "Судья и Лжецы")
|
||||
|
||||
Представь простое число как **честного судью**, а составное — как **лжеца**. У тебя есть **несколько свидетелей** (маленькие числа 2, 3, 7...), которые задают судье **сложные вопросы**.
|
||||
|
||||
**Правило игры:** Если судья честный (число простое), он всегда отвечает правильно. Если лжец (составное), то с высокой вероятностью **хотя бы один свидетель** поймает его на лжи.
|
||||
|
||||
Miller-Rabin — это **математический допрос**: мы заставляем число доказать свою честность, вычисляя сложные степени по модулю.
|
||||
|
||||
## Шаг 1: nth(n) — Простой перебор с MR-тестом
|
||||
|
||||
```rust
|
||||
pub fn nth(n: u32) -> u32 {
|
||||
let mut num = 1; // начинаем с 2 (1-е простое)
|
||||
for _ in 0..=n { // ищем n+1 простое число
|
||||
loop {
|
||||
num += 1;
|
||||
if miller_rabin(num as u64) { break; } // MR говорит "простое!"
|
||||
}
|
||||
}
|
||||
num
|
||||
}
|
||||
```
|
||||
|
||||
**Логика:** "Ходи по числам, пока не найдешь n простых". Медленно (O(n log n)), но **корректно**. Для Exercism (n до 10k) работает мгновенно.
|
||||
|
||||
## Шаг 2: Подготовка MR-теста (d и s)
|
||||
|
||||
```rust
|
||||
let mut d = n - 1;
|
||||
let mut s = 0;
|
||||
while d % 2 == 0 { d /= 2; s += 1 }
|
||||
```
|
||||
|
||||
**Математика:** Каждое простое удовлетворяет **малой теореме Ферма**: \(a^{n-1} \equiv 1 \pmod n\).
|
||||
|
||||
Но вместо прямого вычисления \(a^{n-1}\) мы раскладываем показатель:
|
||||
\[ n-1 = d \cdot 2^s \]
|
||||
где \(d\) — нечетное. **Почему?** Чтобы проверять промежуточные квадраты.
|
||||
|
||||
**Метафора:** "Не спрашивай сразу 'ты честный?', а сначала проверь базовое свойство, потом возводи в квадрат шаг за шагом".
|
||||
|
||||
## Шаг 3: Witness Table (функциональный стиль)
|
||||
|
||||
```rust
|
||||
let witnesses = WITNESSES.iter()
|
||||
.find(|&&(hi, _)| hi >= n) // ищем нужный диапазон
|
||||
.map(|&(_, wtnss)| wtnss) // извлекаем список свидетелей
|
||||
.unwrap();
|
||||
```
|
||||
|
||||
**Гениальность:** Для каждого диапазона известен **минимальный набор свидетелей**, гарантирующий 100% точность.
|
||||
|
||||
| Диапазон | Свидетели | Тестов |
|
||||
| ---------- | --------------------- | ------ |
|
||||
| < 2047 | | 1 |
|
||||
| < 4.7e9 | [1] | 3 |
|
||||
| < u64::MAX | [7][1][2][3][4][5][6] | 12 |
|
||||
|
||||
**Функциональный стиль:** `iter().find().map()` — чистый, без мутабельности, легко тестировать.
|
||||
|
||||
## Шаг 4: Основной цикл MR (сердце алгоритма)
|
||||
|
||||
```rust
|
||||
for &a in witnesses.iter() { // для каждого свидетеля
|
||||
let mut power = mod_exp(a, d, n); // x0 = a^d mod n
|
||||
|
||||
if power == 1 || power == n-1 { continue } // тест пройден
|
||||
|
||||
for _r in 0..s { // s раз возводим в квадрат
|
||||
power = mod_sqr(power, n); // xr+1 = xr^2 mod n
|
||||
if power == n-1 { continue 'next_witness } // тест пройден!
|
||||
if power == 1 { return false } // составное!
|
||||
}
|
||||
return false; // все свидетели поймали на лжи
|
||||
}
|
||||
```
|
||||
|
||||
**Логика теста:**
|
||||
1. Вычисляем \(x_0 = a^d \mod n\)
|
||||
2. Если \(x_0 = 1\) или \(x_0 = n-1\) → свидетель доволен
|
||||
3. Иначе делаем \(s\) квадратов: \(x_{r+1} = x_r^2 \mod n\)
|
||||
4. Если хоть раз получили \(n-1\) → свидетель доволен
|
||||
5. Если дошли до конца → **лжец пойман!**
|
||||
|
||||
## Шаг 5: Быстрое возведение в степень (Binary Exponentiation)
|
||||
|
||||
```rust
|
||||
fn mod_exp(mut x: u64, mut d: u64, n: u64) -> u64 {
|
||||
let mut ret: u64 = 1;
|
||||
while d != 0 {
|
||||
if d % 2 == 1 { // если бит степени = 1
|
||||
ret = mod_mul(ret, x, n); // ret *= x
|
||||
}
|
||||
d /= 2; // сдвиг степени
|
||||
x = mod_sqr(x, n); // x = x^2 (для следующего бита)
|
||||
}
|
||||
ret
|
||||
}
|
||||
```
|
||||
|
||||
**Метафора:** "Не умножай 2^1000 = 2×2×2... 1000 раз. Используй (2^2)^2 = 2^4, ((2^4)^2)^2 = 2^16 и т.д."
|
||||
|
||||
O(log d) умножений вместо O(d).
|
||||
|
||||
## Шаг 6: Безопасное умножение по модулю (u64 → u128)
|
||||
|
||||
```rust
|
||||
fn mod_mul(a: u64, b: u64, m: u64) -> u64 {
|
||||
match a.checked_mul(b) {
|
||||
Some(r) => if r >= m { r % m } else { r },
|
||||
None => mod_mul_(a, b, m), // fallback на u128
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Проблема:** \(2^{64}-1 \times 2^{64}-1 = 2^{128}\), переполнение!
|
||||
**Решение:** `checked_mul` + fallback на `u128::from(a) * u128::from(b) % u128::from(m)`.
|
||||
|
||||
**Оптимизация `mod_sqr`:** Для \(a < 2^{32}\) обычное `a*a` быстрее `u128`.
|
||||
|
||||
## Итог: Почему код Production-Ready
|
||||
|
||||
```
|
||||
✅ Детерминированный (не вероятностный)
|
||||
✅ O(√n) на свидетель (с log n на mod_exp)
|
||||
✅ Безопасен от overflow (u128 fallback)
|
||||
✅ Функциональный стиль (иммутабельные итераторы)
|
||||
✅ Таблица свидетелей покрывает весь u64
|
||||
✅ Микрооптимизации (mod_sqr для малых чисел)
|
||||
```
|
||||
|
||||
**Когда использовать:** Проверка **конкретных** больших чисел (10^18 + 7?).
|
||||
**Когда НЕ использовать:** Генерация последовательности простых — тут sieve в 1000x быстрее.
|
||||
|
||||
Этот код — **золотой стандарт** для криптографии и системного программирования.[4]
|
||||
|
||||
[1](https://github.com/SergioBenitez/Rocket/issues/1522)
|
||||
[2](https://gliderkite.github.io/posts/sr-cache/)
|
||||
[3](https://stackoverflow.com/questions/16761898/redis-as-write-back-view-count-cache-for-mysql)
|
||||
[4](https://github.com/jedisct1/rust-sieve-cache)
|
||||
[5](https://dev.to/artslob/32-rust-crates-you-better-know-about-4aa7)
|
||||
[6](https://lib.rs/crates/staticvec)
|
||||
[7](https://www.perplexity.ai/search/444a1554-6943-4987-9a27-5923865aea84)
|
||||
|
||||
|
||||
## Малая теорема Ферма (шаг 2 MR)
|
||||
|
||||
**Формулировка:**
|
||||
Если \( p \) — простое число и \( a \) не делится на \( p \), то:
|
||||
\[ a^{p-1} \equiv 1 \pmod{p} \]
|
||||
или эквивалентно:
|
||||
\[ a^p \equiv a \pmod{p} \] [1]
|
||||
|
||||
### Метафора "Кольцо часов"
|
||||
|
||||
Представь \( p = 5 \) простое число как **циферблат с 5 делениями** (0,1,2,3,4). Умножение на \( a = 3 \) — это **поворот стрелки**:
|
||||
|
||||
```
|
||||
Стрелка на 1 → ×3 = 3
|
||||
Стрелка на 2 → ×3 = 6 ≡ 1 (mod 5)
|
||||
Стрелка на 3 → ×3 = 9 ≡ 4 (mod 5)
|
||||
Стрелка на 4 → ×3 = 12 ≡ 2 (mod 5)
|
||||
```
|
||||
|
||||
**Важно:** Поворот на \( 1,2,3,4 \) дал **все возможные позиции** (1,3,4,2) — **полный цикл**! Умножение на 3 **проходится по всему кольцу**.
|
||||
|
||||
**Теорема говорит:** Если \( p \) простое, то любое \( a \) (не кратное \( p \)) за \( p-1 \) повортов **вернется в исходную точку**.
|
||||
|
||||
### Пример вычисления
|
||||
|
||||
```rust
|
||||
// p = 13 (простое), a = 5
|
||||
5^12 mod 13 = ? // малая теорема: должно быть 1
|
||||
|
||||
5^1 ≡ 5
|
||||
5^2 ≡ 25 ≡ 12 ≡ -1
|
||||
5^4 ≡ (-1)^2 ≡ 1
|
||||
5^8 ≡ 1^2 ≡ 1
|
||||
5^12 = 5^8 × 5^4 ≡ 1 × 1 ≡ 1 ✓
|
||||
```
|
||||
|
||||
### Почему это работает (доказательство через перестановки)
|
||||
|
||||
**Ключевой инсайт:** Остатки \( 1, 2, ..., p-1 \) при умножении на \( a \) дают **перестановку** тех же чисел:
|
||||
|
||||
```rust
|
||||
p = 7, a = 3
|
||||
1×3 = 3
|
||||
2×3 = 6
|
||||
3×3 = 9 ≡ 2
|
||||
4×3 = 12 ≡ 5
|
||||
5×3 = 15 ≡ 1
|
||||
6×3 = 18 ≡ 4
|
||||
|
||||
Получили: [3,6,2,5,1,4] — все числа 1..6 в другом порядке!
|
||||
```
|
||||
|
||||
**Произведение всех элементов одинаково:**
|
||||
\[ 1 \times 2 \times \dots \times (p-1) = a \times 2a \times \dots \times (p-1)a \pmod{p} \]
|
||||
\[ (p-1)! \equiv a^{p-1} \times (p-1)! \pmod{p} \]
|
||||
Сокращаем \( (p-1)! \) (оно не 0 mod p):
|
||||
\[ 1 \equiv a^{p-1} \pmod{p} \]
|
||||
|
||||
### Почему MR использует именно эту теорему
|
||||
|
||||
**Прямая проверка \( a^{p-1} \mod p \) неэффективна** — показатель степени огромный (до \( 2^{64} \)).
|
||||
|
||||
**Хитрость MR:** Вместо \( a^{p-1} \) проверяем **промежуточные квадраты**:
|
||||
\[ n-1 = d \times 2^s \]
|
||||
\[ a^{n-1} = (a^d)^{2^s} \]
|
||||
|
||||
Если \( a^d \equiv 1 \) или хотя бы один квадрат дает \( n-1 \), то по свойствам степеней теорема выполняется.
|
||||
|
||||
**Метафора:** Вместо "сделай 1 трлн повортов стрелки" говорим "сделай 40 повортов, но проверяй промежуточные позиции".
|
||||
|
||||
### Проверка на практике
|
||||
|
||||
```rust
|
||||
fn fermat_test(n: u64, a: u64) -> bool {
|
||||
mod_exp(a, n-1, n) == 1 // прямая малая теорема
|
||||
}
|
||||
|
||||
// Но! 561 — псевдопростое по основанию 2:
|
||||
// 2^560 ≡ 1 mod 561, хотя 561 = 3×11×17
|
||||
```
|
||||
|
||||
**Вывод:** Малая теорема — **необходимое** условие простоты, но **недостаточное** (есть "лжецы"). MR усиливает тест **структурированными промежуточными проверками** + **несколькими основаниями**.
|
||||
|
||||
Теперь понятен шаг 2? Идем к witness table? 🚀
|
||||
|
||||
[1](https://ru.wikipedia.org/wiki/%D0%9C%D0%B0%D0%BB%D0%B0%D1%8F_%D1%82%D0%B5%D0%BE%D1%80%D0%B5%D0%BC%D0%B0_%D0%A4%D0%B5%D1%80%D0%BC%D0%B0)
|
||||
[2](https://math.mosolymp.ru/upload/files/2022/other/1568/8-1/Malaya_teorema_Ferma.pdf)
|
||||
[3](https://foxford.ru/wiki/matematika/malaya-teorema-ferma)
|
||||
[4](https://www.lirmm.fr/~ashen/mathclass/09fermat-prob.pdf)
|
||||
[5](https://wiki.algocode.ru/index.php?title=%D0%9C%D0%B0%D0%BB%D0%B0%D1%8F_%D1%82%D0%B5%D0%BE%D1%80%D0%B5%D0%BC%D0%B0_%D0%A4%D0%B5%D1%80%D0%BC%D0%B0)
|
||||
[6](https://kvant.mccme.ru/pdf/2000/04/kv0400ferm.pdf)
|
||||
[7](https://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D0%BE%D1%80%D0%B5%D0%BC%D0%B0_%D0%A4%D0%B5%D1%80%D0%BC%D0%B0_%E2%80%94_%D0%AD%D0%B9%D0%BB%D0%B5%D1%80%D0%B0)
|
||||
[8](https://journalpro.ru/articles/malaya-teorema-ferma-i-ee-primenenie-v-kriptosistemakh/)
|
||||
[9](https://www.reddit.com/r/math/comments/2v6db7/intuition_for_fermats_little_theorem/)
|
||||
[10](https://math.fandom.com/ru/wiki/%D0%9C%D0%B0%D0%BB%D0%B0%D1%8F_%D1%82%D0%B5%D0%BE%D1%80%D0%B5%D0%BC%D0%B0_%D0%A4%D0%B5%D1%80%D0%BC%D0%B0)
|
||||
Loading…
Add table
Add a link
Reference in a new issue