Простые числа — это натуральные числа больше 1, которые делятся только сами на себя или на 1. Часто возникающая (например, при шифровании данных) задача — найти все простые числа от 2 до заданного N. Для этого придумано несколько алгоритмов разной эффективности. Давайте рассмотрим их реализацию на PHP.
Простой перебор делителей
Это простейший и наименее эффективный алгоритм из рассмотренных. Для небольших чисел, однако, он вполне применим.
Суть алгоритма: мы просто последовательно перебираем все числа от 2 до N и проверяем, простые они, или нет. Функция проверки на простоту перебирает все нечетные числа от 2 до квадратного корня из N и, если находит такое, на которое N делится без остатка, то число считается составным, а если не находит, то простым.
<?php function isPrime($number) { if ($number==2) return true; if ($number%2==0) return false; $i=3; $max_factor = (int)sqrt($number); while ($i<=$max_factor){ if ($number%$i == 0) return false; $i+=2; } return true; } function getPrimes($max_number) { $primes = []; for ($i=3; $i<=$max_number; $i++){ if (isPrime($i)) $primes[] = $i; } return $primes; }
Эффективность такого алгоритма: O(n * sqrt(n))
Решето Эратосфена
Этот алгоритм как бы «просеивает» все числа от 0 до максимального N через условное решето несколько раз. Сначала последовательно исключаются все числа, кратные 2. Само число добавляется в массив простых чисел. Далее из оставшихся исключаются все числа, кратные следующему простому числу — трем. 4 уже было удалено из исходного массива, соответственно, следом удаляются все числа, кратные 5, и так далее, пока не будут перебраны все числа.
Вот пример решета Эратосфена на PHP:
<?php function getPrimes($max_number) { $primes = []; $is_composite = []; for ($i=4; $i<=$max_number; $i+=2){ $is_composite[$i] = true; } $next_prime = 3; while ($next_prime<=(int)sqrt($max_number)){ for ($i=$next_prime*2; $i<=$max_number; $i+=$next_prime){ $is_composite[$i] = true; } $next_prime += 2; while ($next_prime<=$max_number && isset($is_composite[$next_prime])){ $next_prime+=2; } } for ($i=2; $i<=$max_number; $i++){ if (!isset($is_composite[$i])) $primes[] = $i; } return $primes; }
Такой алгоритм имеет эффективность O(n * log(log n)) .
Метод нахождения псевдопростых чисел с использованием теста простоты Ферма
Данный метод позволяет находить простые числа гораздо быстрее предыдущих двух, однако, это будут псевдопростые числа, то есть числа, которые являются простыми лишь с некоторой (тем не менее, очень высокой, около 100%) вероятностью.
Здесь используется тест Ферма, который гласит: если p-простое число, то если возвести число n в степень (p-1), а потом взять результат возведения в степень по модулю p (остаток от деления на p), то в итоге получится 1.
Проблема в том, что если p не является простым, то есть вероятность, что в результате описанных вычислений вы все равно получите 1. Тогда n называется обманщиком Ферма, а если результат не равен 1, то число n называют свидетелем Ферма, так как оно верно указывает на составную природу тестируемого числа p. Вероятность того, что выбранное произвольно для теста число n окажется свидетелем Ферма 50%. Это, конечно, лишает смысла одиночный тест на простоту, но если провести несколько тестов с разными n, то вероятность можно многократно увеличить до приемлемых величин. Скажем, 10 последовательных тестов с разными n, которые выдали, что число является простым, дадут вероятность того, что число все же было составным, всего в 0.00098. 100 тестов и вовсе дадут число, кратное десяти в степени -31. Для практического применения такой низкой вероятности ошибки вполне достаточно.
Вот реализация поиска простых чисел с использованием теста Ферма на PHP:
<?php function isPrimeByFerma($number, $max_tests) { for ($i=1; $i<=$max_tests; $i++){ $n = mt_rand (1, $number-1); if (($n**($number-1))%$number != 1) return false; } // probability of the number being prime is 1/2^max_tests return true; } function getPrimes($max_number, $max_tests) { $primes = []; for ($i=2; $i<=$max_number; $i++){ if (isPrimeByFerma($i, $max_tests)) $primes[] = $i; } return $primes; }
Эффективность такого алгоритма O(n * log2n * log (log n) * log (log (log n)))
Кроме указанных алгоритмов существуют и такие, как:
Кроме того, существует множество тестов простоты числа, как истинных, так и вероятностных. Возможно, в будущем я дополню эту статью реализацией других алгоритмов поиска простых чисел на PHP.
spasibo