Простое число — натуральное (целое положительное) число, имеющее ровно два различных натуральных делителя — единицу и самого себя.
Чтобы проверить является ли какое-то число простым, напрашивается наивный метод решения - проверить делимость его на все натуральные числа от 1 до n (хотя достаточно лишь до квадратного корня из него). Если в какой-то момент остаток от деления даст 0 (то есть число поделилось без остатка), значит оно составное, а в противном случае является простым.
Такой алгоритм прост, но требует довольно много проверок на делимость, из-за чего может быть применён для проверки небольшого числа. А что будет, если применить его для поиска простого числа по заданному номеру n?
Решение кажется очевидным: начнём перебирать подряд числа и проверять их на простоту. Как только обнаружим n простых чисел, остановимся и вернём в результате последнее найденное число. Однако, даже для не очень большого числа n данный метод будет работать чрезвычайно долго, поскольку чем больше будет число, тем дольше будут перебираться его делители.
Вот тут нам на помощь и приходит быстрый алгоритм — решето Эратосфена. Данный алгоритм позволяет найти все простые числа в интервале от 2 до N за довольно короткое время, но нам то нужно найти n-ое простое число, а про индексы в данном алгоритме ничего не сказано. Если бы можно было узнать, в каком интервале находится n-ое простое число, то задача была бы мгновенно решена применением решета. Однако как же решить данную задачу, не зная примерное значение числа?
Мы предлагаем следующее решение: получим число n и создадим два массива размерности n: primes - массив для хранения будущих простых чисел и numbers - массив для хранения обычных чисел для применения просеивания решетом. Заполним массив numbers числами от 0 до n - 1, а в нулевой элемент массива primes положим 2, поскольку 2 — первое простое число.
Код программы для поиска простого числа по его номеру
#include <iostream> int main () { int n; std::cout << "Enter number of prime number: "; std::cin >> n; // считываем номер простого числа, которое нужно найти int size = n; // число элементов для массива чисел для просеивания int *primes = new int[n]; // массив простых чисел int *numbers = new int[size]; // массив для чисел for (int i = 0; i < size; i++) numbers[i] = i; // заполняем массив (число равно индексу элемента) primes[0] = 2; // первое простое число - 2 int i = 0; // индекс текущего простого числа while (i < n) { int p = primes[i++]; // запоминаем текущее простое число for (int j = p * 2; j < size; j += p) numbers[j] = 0; // обнуляем все кратные ему числа в массиве while (numbers[p + 1] == 0) p++; // ищем следующее ненулевое число if (p + 1 >= size) { // если выйдем за границы, расширяем массив int *tmp = new int[size * 2]; for (int k = 0; k < size; k++) tmp[k] = numbers[k]; delete[] numbers; size *= 2; numbers = tmp; for (int j = size / 2; j < size; j++) numbers[j] = j; // заполняем новую часть массива числами i = 0; // возвращаемся к начальной стадии просеивания } else primes[i] = p + 1; // запоминаем новое простое число } std::cout << n << " prime number is " << primes[n - 1] << std::endl; delete[] numbers; delete[] primes; }
Теперь нам нужно искать числа до тех пор, пока не найдём все n, поэтому обнуляем переменную i, обозначающую текущий индекс простого числа и запускаем цикл пока с условием i < n.
Запоминаем в переменной p текущее простое число (на первой итерации это будет 2), увеличивая текущий индекс простого числа на 1 и просеивая массив чисел от 2p до размера массива чисел (тем элементам массива, где стоят кратные p числа (кроме самого p), проставляем нули, после чего ищем первое ненулевое число — оно и будет следующим простым числом.
В дальнейшем может возникнуть проблема, что n-ое простое число ещё не найдено, а массив чисел уже закончился. Простым решением данной проблемы будет увеличение его размера в два раза и до заполнение числами. Но если так сделать, то в нём останутся числа, кратные тем, что ещё уже были просеяны, и требуется их обработать. Простым решением данной проблемы является запуск алгоритма сначала, а именно обнуление индекса текущего простого числа, благодаря чему все операции просеивания будут проведены повторно, но уже для нового более длинного массива.
Если же изменение массива не требовалось, в массив primes просто записывается найденное простое число (первое ненулевое число в массиве numbers).
Когда же цикл завершён, достаточно просто вывести n - 1 элемент в массиве primes или весь массив, если нужны все простые числа до указанного номера.