Малая теорема Ферма и её применения

Изучаем теорему, которая лежит в основе быстрых вычислений по модулю и проверки чисел на простоту.

Малая теорема Ферма: если p — простое число и a не делится на p, то a^(p−1) ≡ 1 (mod p).

Зачем нужна теорема Ферма

Это одна из самых полезных теорем прикладной математики. Она даёт быстрый способ находить обратные элементы по модулю (а значит, расшифровывать в криптографии), лежит в основе тестов на простоту (как проверить, простое ли 300-значное число, не перебирая делители?), и используется в самом RSA. Малая теорема Ферма — пример того, как чисто теоретический факт XVII века стал инструментом, защищающим сегодня каждую интернет-транзакцию.

Что утверждает теорема

Возьмём простое p и любое a, не кратное p. Тогда, сколько ни возводи a в степень p−1 и бери остаток по модулю p, всегда получится единица. Удивительно: для разных a результат один и тот же — 1. Проверим это для p = 7: возведём 1, 2, ..., 6 в шестую степень по модулю 7.

p = 7
results = [pow(a, p - 1, p) for a in range(1, p)]   # a^(p-1) mod p
print(f"a^(p-1) mod {p} для a = 1..{p-1}:", results)
print("Все равны 1:", all(r == 1 for r in results))

# Проверим ещё для p = 13
p = 13
results = [pow(a, p - 1, p) for a in range(1, p)]
print(f"a^(p-1) mod {p} для a = 1..{p-1}:", results)

Вывод:

a^(p-1) mod 7 для a = 1..6: [1, 1, 1, 1, 1, 1]
Все равны 1: True
a^(p-1) mod 13 для a = 1..12: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

Действительно: для каждого a от 1 до 6 величина a^6 mod 7 равна единице, и то же для модуля 13. Это не совпадение, а строгий закон. Интуиция доказательства: умножение на a по модулю p просто переставляет ненулевые остатки {1, 2, ..., p−1} между собой (это биекция!). Перемножив все остатки до и после перестановки, получаем равенство, из которого и следует a^(p−1) ≡ 1. Снова биекция из раздела про функции работает на нас.

Применение 1: быстрое нахождение обратного элемента

В прошлом уроке мы искали обратный элемент перебором или расширенным Евклидом. Если модуль p простой, теорема Ферма даёт прямую формулу. Из a^(p−1) ≡ 1 следует a · a^(p−2) ≡ 1, то есть обратный элемент к a — это a^(p−2) mod p. Никакого перебора — одно возведение в степень.

p = 7
a = 3
inverse = pow(a, p - 2, p)        # a^(p-2) mod p — обратный по Ферма
print(f"Обратный к {a} по модулю {p}: {inverse}")
print(f"Проверка: {a} * {inverse} mod {p} =", (a * inverse) % p)

Вывод:

Обратный к 3 по модулю 7: 5
Проверка: 3 * 5 mod 7 = 1

Формула 3^(7−2) mod 7 = 3^5 mod 7 = 5 мгновенно дала обратный элемент — то же число 5, что мы раньше искали перебором. Это и есть способ «делить» по простому модулю в один вызов pow. Расшифровка в RSA устроена ровно так — через возведение в степень по модулю.

Применение 2: тест на простоту

Теорему можно «перевернуть» для проверки простоты. Если для какого-то a вдруг a^(n−1) mod n ≠ 1, то n точно составное (ведь для простого было бы 1). Это тест Ферма на простоту: берём несколько случайных a и проверяем условие.

Тонкость: обратное неверно в полную силу — есть редкие составные числа (числа Кармайкла), которые «притворяются» простыми, проходя тест для многих a. Поэтому тест Ферма — вероятностный: он может ошибиться в сторону «простое», но если сказал «составное» — это точно. На его идее построены практичные тесты (Миллера-Рабина), которыми проверяют огромные простые для криптографии. Посмотрим тест в действии.

def fermat_test(n, a):
    # Если a^(n-1) mod n != 1, то n ТОЧНО составное
    if n <= 1:
        return False
    return pow(a, n - 1, n) == 1

# 561 — число Кармайкла: составное (561 = 3·11·17), но обманывает тест
print("561 проходит тест для a=2?", fermat_test(561, 2))
print("561 проходит тест для a=5?", fermat_test(561, 5))
# a=3 разоблачает 561 (3 делит 561)
print("561 проходит тест для a=3?", fermat_test(561, 3))

# 13 — настоящее простое, проходит для всех a
print("13 проходит тест для a=2,5,7?",
      all(fermat_test(13, a) for a in (2, 5, 7)))

Вывод:

561 проходит тест для a=2? True
561 проходит тест для a=5? True
561 проходит тест для a=3? False
13 проходит тест для a=2,5,7? True

Число 561 (это 3·11·17, составное!) обмануло тест для a = 2 и a = 5 — оба раза условие выполнилось. Но a = 3 его разоблачило: 3^560 mod 561 ≠ 1. Это наглядно показывает, почему тест Ферма вероятностный: для надёжности нужно проверять несколько разных a. А настоящее простое 13 честно прошло все проверки. Так дискретная математика балансирует между «быстро» и «надёжно» в реальной криптографии.

Китайская теорема об остатках: собрать число по кусочкам

Один из самых полезных результатов модулярной арифметики — китайская теорема об остатках, известная ещё древнекитайским математикам. Она утверждает: если знать остатки числа по нескольким взаимно простым модулям, то само число (в пределах их произведения) восстанавливается однозначно. Скажем, число, дающее остаток 2 при делении на 3, остаток 3 при делении на 5 и остаток 2 при делении на 7, — это единственное число от 0 до 104 (тут 3·5·7 = 105), а именно 23.

Зачем это программисту? Теорема позволяет заменить вычисления с одним гигантским модулем на несколько вычислений с маленькими модулями, которые можно вести параллельно, а потом «склеить» ответ. На этом основаны быстрые алгоритмы умножения больших чисел, ускорение расшифровки в RSA, распределённые вычисления и системы остаточных классов в процессорах. Красота в том, что разные остатки несут независимую информацию о числе, и вместе они задают его целиком — ещё одно проявление того, как модулярная арифметика превращает большое в управляемо-маленькое.

Теорема Эйлера: обобщение Ферма на любой модуль

Малая теорема Ферма работает только для простого модуля. Её обобщил Леонард Эйлер — и именно теорема Эйлера лежит в основе RSA, где модуль составной. Для этого вводят функцию Эйлера φ(n) — количество чисел от 1 до n, взаимно простых с n. Например, φ(10) = 4 (это 1, 3, 7, 9). Для простого p, очевидно, φ(p) = p − 1 — все меньшие числа взаимно просты с простым.

Теорема Эйлера: если a и n взаимно просты, то a^φ(n) ≡ 1 (mod n). Подставьте сюда простое n = p, и φ(p) = p − 1 — получите ровно малую теорему Ферма. То есть Ферма — частный случай Эйлера. Эта теорема позволяет находить обратные элементы и «отматывать» возведение в степень по любому модулю, а не только по простому, — что и нужно RSA, где модуль n = p·q является произведением двух простых. Так теорема Ферма оказывается лишь первой ступенью к более общему и ещё более мощному инструменту.

Типичные ошибки

  • Применять теорему при составном модуле. Малая теорема Ферма требует простого p. Для составного модуля есть обобщение — теорема Эйлера с функцией φ(n).
  • Забывать условие «a не кратно p». Если a делится на p, то a ≡ 0, и a^(p−1) ≡ 0, а не 1.
  • Считать тест Ферма безошибочным. Числа Кармайкла обманывают его. Тест вероятностный: «составное» — точно, «простое» — с высокой вероятностью.

Итог

  • Малая теорема Ферма: для простого p и a, не кратного p, верно a^(p−1) ≡ 1 (mod p).
  • Доказательство опирается на то, что умножение по модулю переставляет остатки (биекция).
  • Применение: обратный элемент по простому модулю — это a^(p−2) mod p.
  • Тест Ферма проверяет простоту, но вероятностно (числа Кармайкла обманывают его) — основа быстрых тестов для криптографии.
Проверьте себя
1. Что утверждает малая теорема Ферма для простого p и a, не кратного p?
Aa^(p−1) ≡ 1 (mod p)
Ba^p ≡ a (mod p)
Ca^(p−1) ≡ 0 (mod p)
Da^p ≡ 1 (mod p)
2. Как по теореме Ферма найти обратный к a по простому модулю p?
Aa^(p−1) mod p
Ba^(p−2) mod p
Ca^p mod p
Dp − a
3. Почему тест Ферма на простоту является вероятностным?
AОн использует случайные числа для ускорения
BОн работает только для чётных чисел
CСуществуют составные числа (Кармайкла), проходящие тест для многих a
DОн всегда ошибается
Поддержать проект