Тесты на простоту
Определять, является ли число простым можно с помощью решёт, но этот способ нельзя назвать полноценным тестом на простоту, так как попутно мы проверяем на простоту все числа до исходного. Тесты на простоту делятся на два типа: истинные и вероятностные. Истинные тесты точно отвечают, является ли число простым, вероятностные же могут дать ответ, который будет верным с некоторой возможностью ошибки. Зачастую такие тесты используют в связке: сначала проверяют число вероятностным тестом, а потом подтверждают это детерменированным.
Вероятностные тесты
Тест Ферма
Тест Ферма основывается на малой теореме Ферма, которая гласит
Чтобы узнать, является ли простым числом, нужно взять случайное число и проверить, выполняется ли . Число называется свидетелем простоты. Если равенство не выполняется, то с полной уверенностью можно сказать, что число составное. Если же прошло тест, то всё равно есть вероятность, что оно не простое.
Заметьте, что в малой теореме Ферма импликация идёт только в одну сторону: из взаимной простоты следует сравнение, но обратная импликация не всегда верна. Составные числа , удовлетворяющие сравнению для всех целых , взаимнопростых с , называются числами Кармайкла. Числа Кармайкла успешно проходят тест Ферма, хотя являются составными.
Давайте посмотрим на наименьшее число Кармайкла — . Из китайской теоремы об остатках следует, что . Возьмём взаимнопростое со всеми делителями , то есть
, и являются делителями , значит .
Такие числа встречаются довольно редко, но их бесконечно много.
Вероятность ошибки одного теста будет . Выполнив тест Ферма раз, получим вероятность верного ответа .
function fermat_test(int n, int k) -> bool:
for int i = 0; i < k; i++:
select a = random uniform int [2, n-1]
if pow(a, n-1) % n != 1:
return false
return trueИспользуя алгоритмы быстрого возведения в степень по модулю, можно получить время работы .
Тест Соловея-Штрассена
Тест Соловея-Штрассена предполагает проверку квадратичных вычетов, для этого нам понадобится критерий Эйлера.
Критерий Эйлера
— простое, и
Рассмотрим три утверждения:
- Существует такой , что
Из следует, что , а это то же самое, что , поэтому утверждения и не могут выполняться одновременно.
Пусть существует такое, что . Возведём левую и правую часть в степень и получим . По малой теореме Ферма , а значит . Получается, что утверждения и всегда выполняются вместе.
Рассмотрим последовательность
Из следует
Получается, что сущетвует ровно квадратов по модулю . Обозначим их . Если равно , то сравнение имеет решение, а это значит, что и второе выполняется для любого . Следовательно, сравнение имеет ровно решений, отсюда же следует, что также имеет решений, при каждом из которых первое сравнение не выполняется.
Если число простое, то , где целое, а нечётное. Очевидно, что . Значит, если мы возьмём случайное , то оно должно быть либо квадратичным вычетом, либо невычетом по модулю . Это можно выразить через символы Якоби следующим образом:
function solovay_strassen_test(int n, int k) -> bool:
for int i = 0; i < k; i++:
select a = random uniform int [2, n-1]
if pow(a, (n-1)/2) % n != jacobi(a, p):
return false
return trueАсимптотическая сложность этого алгоритма равняется , а вероятность ошибки .
Тест Миллера-Рабина
Давайте усовершенствуем разобранные нами тесты. В предыдущем пункте мы выяснили, что , значит мы можем брать корень из , пока не выполнится одно из двух:
Если при взятии корня мы получили сравнимость по модулю с чем-то кроме , то число точно составное. Вероятность ошибки этого теста . Значит, повторив тест Миллера-Рабина раз, мы получим вероятность верного ответа не меньше , что является очень хорошим результатом.
function miller_rabin_test(int n, int k) -> bool:
for int i = 0; i < k; i++:
select a = random uniform int [2, n-1]
for int j = n-1; j % 2 == 0; j /= 2:
if pow(a, j) % n == n-1:
break
else if pow(a, j) % n != 1:
return false
return trueТест Миллера-Рабина способен определять некоторые числа Кармайкла, например, , но вот , третье число Кармайкла, успешно проходит тест.
Сложность этого алгоритма составляет .
Тест Фробениуса
Для использования теста Фробениуса нужно рассмотреть такое понятие как квадратичная иррациональность. Квадратичной иррациональностью будем называть число , где , и — целые и свободно от квадратов. Сопряжённым к будет . А остаток от деления определим как .
Теорема Фробениуса
Пусть — простое, символ Якоби и , тогда
Сутью теста Фробениуса является подбор подходящих коэффициентов , и . Начать проще всего с последнего. Чтобы выполнялась теорема Фробениуса, нам нужно такое , что , в качестве значения возьмём или наименьшее простое число, удовлетворяющее условию. Если , то , иначе . Если , то число простое с вероятностью . Несмотря на то, что тест вероятностный, на данный момент неизвестно ни одно составное число, проходящее его, и строго доказано, что таких чисел меньше не существует.
function frobenius_test(int n) -> bool:
tuple [int, complex] z
for int c in [-1, 2, 3, 5, 7, ...]:
if jacobi(c, n) == -1:
if c <= 2:
z.first = 2
z.second = sqrt(c)
else:
z.first = 1
z.second = sqrt(c)
if pow(z, n) % n == !z:
return true
else:
return falseИстинные тесты
Тест Люка
Мы уже выяснили, что . Можно разложить на произведение простых множителей . Если число простое, то . В таком случае для каждого выполняется
Если составное, то либо для него не выполняется тест Ферма, тогда мы точно знаем, что оно не простое, либо . Если во втором случае будет выполняться и поскольку , мы получим, что в группе есть элемент порядка . При этом порядок самой группы равен , а это меньше , так как число составное. То есть получается, что порядок какого-то элемента группы больше порядка самой группы, что противоречит теореме Лагранжа. Получается, что для составных чисел такое невозможно.
function lucas_test(int n) -> bool:
while select a = random uniform int [2, n-1]:
if pow(a, n-1) % n != n-1:
return false
bool flag = true
for int q in divisors(n-1):
if pow(a, (n-1)/q) % n == 1:
flag = false
break
if flag:
return trueПреимуществом этого алгоритма является его точность, он никогда не примет составное число за простое, но тест Люка имеет два существенных недостатка: нам нужно знать делители и перебирать большое количество .
Тест Люка-Лемера
Одними из самых лёгких для проверки на простоту являются числа Мерсена, имеющие вид . Этот метод использует рекуррентную последовательность , в которой , а первый элемент равен четырём.
Если — простое нечётное число, то число Мерсена простое только когда оно делит . Получается, что для проверки простоты нам всего-то нужно проверить, делится ли на .
function lucas_lehmer_test(int n) -> bool:
int s = 4
int m = 2 << n - 1
for int i = 1; i < n-1; i++:
s = (s * s - 2) % m
if s == 0:
return true
else:
return falseАсимптотическая сложнось этого теста равна , но при использовании алгоритмов быстрого умножения больших чисел сложность уменьшается до
Для доказательства работы этого теста нам понадобятся последовательности Люка. Напомню, что
Где и — это корни квадратного уравнения
Также нам понадобятся следующие свойства таких последовательностей:
Если , , и , то
Если простое, такое, что , то , где , а — символ Лежандра.
Пусть , и .
Из четвёртого свойства следует, что
А по второму свойству
Таким образом получаем, что
, поэтому если простое, то .
Из свойств и следует, что делит
А из свойств и получаем, что
По третьему свойству
И в итоге получаем, что . Таким образом мы доказали необходимость условий. Но, чтобы называть тест детерменированным, нам нужно также доказать достаточность.
Если , то оно делит и . По первому свойству , а по второму . Тогда каждый простой делитель можно представить как , это больше, чем , а значит, что простое.