Python vs C++: какой язык быстрее найдет все простые числа до миллиарда
Рассказываем, какие есть алгоритмы для поиска простых чисел и реализуем наиболее популярный и простой на Python и C++.
486 открытий2К показов

Баттлы языков — извечная тема, которая никогда себя не изживет. Понятно, что язык программирования нужно выбирать под конкретные цели. Но если мы только учимся, можно попробовать решить какую-нибудь задачку с помощью двух разных инструментов — хотя бы для того чтобы понять, какой нравится больше.
Сегодня будем искать все простые числа до миллиарда с помощью кода на Python и C++. Если вы думаете, что результат совсем очевиден, то это не так.
Напоминание: простые числа — те, которые имеют два различных делителя. Простыми словами, они делятся только сами на себя и на единицу. Например: 2, 3, 5, 7 и так далее.
Для начала: быстро окунемся в математику
Есть несколько алгоритмов, которые помогут найти все простые числа до N. Это материал для отдельной статьи, поэтому пробежимся по основным.
Решето Эратосфена
Один из самых древних и известных алгоритмов, чтобы найти простые числа до N. Плюс он понятный, не занимает слишком много памяти (если речь не идет об очень больших N). Работает так:
- Создается список чисел от 2 до N.
- Все кратные числа кратные 2 (4, 6, 8, ...) помечаются как составные.
- Переходим к 3 и вычеркиваем все числа, кратные ему.
- То же самое повторяем со всеми числами из списка до N
- Так до тех пор, пока не проверятся все числа до N.
Решето Сундарама
Этот алгоритм работает немного по другой логике:
- Создается список чисел от 1 до (N-1)/2.
- Исключаются числа типа i + j + 2ij, где i и j — натуральные числа, и i <= j.
- Оставшиеся числа умножаются на 2 и увеличиваются на 1 — так получаем простые числа.
Этот алгоритм занимает меньше памяти, чем решето Эратосфена, поскольку работает только с половиной диапазона. Но при этом он сложнее в плане понимания и реализации.
Решето Аткина
Это уже современный алгоритм (появился в начале 21 веке). Он использует более сложные математические концепции и работает так:
- Создается список чисел от 1 до N.
- Используются квадратичные формы, чтобы предварительно отсеять составные числа.
- Затем составные числа просеиваются финально для полного исключения.
Решето Аткина более эффективно, когда речь идет об огромных числах, по сравнению с первыми двумя алгоритмами. Оно тоже не простое в реализации, плюс может быть медленнее при работе с небольшим N.
Это три основных алгоритма для поиска простых чисел до N. Сейчас существуют различные оптимизации этих методов, например, колесная факторизация (оптимизированный алгоритм Эратосфена).
Ищем простые числа до миллиарда на Python и C++
Python — не самый быстрый язык для таких операций. Если не использовать библиотеки, то программа выдает ограничение и не может завершиться. Например, вот код на чистом Питоне с алгоритмом Эратосфена:
PyCharm с ограничением 1 000 000 000 не завершает программу. Если же поставить лимит = 100 000 000, то получим такой результат (почти 8 секунд!):
Теперь попробуем проделать то же самое с библиотекой numpy, которая как раз подходит для работы с большими числами:
Результат будет таким:
Давайте попробуем сделать то же самое на С++:
Эта программа будет выполняться больше минуты:
Чтобы оптимизировать программу, можно использовать сегментированное решето Эратосфена:
Время выполнения уменьшилось на 20 секунд:
Итоги
В нашей задачке numpy действительно может оказаться быстрее, чем обычная реализация на C++. На то есть несколько причин:
- Если в NumPy используется эффективный векторизованный алгоритм — в нашем случае решето Эратосфена, — преимущества SIMD-операций (одновременное выполнение одной операции над множеством данных) могут сильно ускорить вычисления.
- Неоптимизированная реализация решета на C++ с обычными циклами может быть медленнее оптимизированной векторизованной реализации в NumPy.
- NumPy использует внутренне оптимизированные C/C++ библиотеки, в которых есть куча оптимизаций для работы с массивами.
Но эффективно оптимизированный код на C++ (например, с использованием SIMD-инструкций, оптимизации кэш-памяти, параллелизма) почти всегда будет быстрее любой реализации на Python/numpy. Правда, такой код будет занимать сотни строк.
Производительность зависит от конкретных алгоритмов, поэтому так себе метод на C++ проиграет хорошему на numpy. Во многих задачах по работе с большими данными плюсы будут более быстрыми, но для типичного пользования numpy — отличная альтернатива.
486 открытий2К показов