Проверка числа на простоту – ускоряемся!
Запись от Jin X размещена 28.02.2019 в 01:29
Показов 10980
Комментарии 6
Метки c, c++, clang, cpp, gcc, primes, алгоритмы, оптимизация, простое число, простые числа, тест на простоту
|
Проверка числа на простоту – ускоряемся! Самый простой способ проверки числа N на простоту – проверить его делимость на все числа от 2 до корня из N.
Проверим делимость числа на 2, а затем на все нечётные, начиная с 3:
n & 1 вместо n % 2? Да, конечно, умный компилятор сам заменит % на &, но я хочу указать это явно. Если вас это напрягает, замените ![]() Этот код тоже далёк от совершенства, поскольку мы можем исключить числа, делящиеся не только на 2, но и на 3, сократив кол-во итераций ещё в 1.5 раза (а в сравнении с первым вариантом – в 3 раза). Чтобы это сделать, нам нужно увеличивать делитель то на 2, то на 4. Для этого будем проделывать с "дельтой" операцию xor 6. Проверим: 2 xor 6 = 4, 4 xor 6 = 2. Всё до гениальности просто! Цикл начнём с 5.Let's do it:
Исключим числа, делящиеся на 5, получив прирост ещё примерно на 25% (или в 3.75 раза в сравнении с первым вариантом). Но здесь нам придётся использовать массив значений delta, т.к. двумя числами (как в предыдущем случае – 2 и 4) мы уже не обойдёмся. Нам понадобится 8 чисел: { 4, 2, 4, 2, 4, 6, 2, 6 }, которые будут повторяться. И начинать цикл мы будет с 7. Барабанная дробь ![]()
Кроме того, я специально использовал проверку вида ...(n % 3 == 0 && n != 3)..., а не вынес if (n == 2 || n == 3 || n == 5) { return true; } в начало, т.к. такой вариант должен работать быстрее (правда, тесты хоть и показывают прирост, но мизерный).![]() Можно, это даст ещё приращение на 10-15%, но для исключения чисел, делящихся на 7 массив delta будет состоять уже из 48 чисел (цикл начинается с 11): Кликните здесь для просмотра всего текста
Для исключения чисел, делящихся ещё и на 11, массив delta будет состоять уже из 480 чисел (цикл начинается с 13).Кликните здесь для просмотра всего текста
Мой спич был бы не полным, если бы я не привёл результаты теста скорости. Итак, запускаем проверку чисел от 0 до 10'000'000 на простоту на моём домашнем компьютере (Intel Core i5-2500K / Windows 10 x64). Я использовал компиляторы GCC (g++) и Clang с ключом оптимизации -O2 для платформ x86 и x64. Результаты между различными компиляторами и платформами оказались настолько близкими (с расхождением порядка 1%), что я решил не разделять их, а объединить в одну таблицу. Здесь указано значение в секундах, доля и ускорение относительно первого варианта.
Используйте ссылки в таблице выше, посмотреть и протестировать код на платформе Try It Online. Исходники с тестовым кодом (более полным, чем на tio.run) прикреплены к статье в виде архива. На этом всё. Если вы знаете более быстрые варианты точного определения простоты числа, буду рад, если поделитесь в комментариях ![]() update 04.03.2019: добавлены функции is_prime7, is_prime11 (в спойлерах), тип изменён на unsigned int. Архив перезалит.
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Метки c, c++, clang, cpp, gcc, primes, алгоритмы, оптимизация, простое число, простые числа, тест на простоту
Размещено в Математика, алгоритмы
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 6
Комментарии
-
Уважаемый Jin X,
я решал эту задачу. И естественно нашел более быстрый и простой алгоритм. Суть его том, что в качестве делителей берутся только простые числа. Для этого объявляется массив нужного размера и заполняется простыми числами. Конкретно я брал массив длиной порядка 45000. Очевидно, что массив либо заполнялся с файла (простых чисел), либо был (инициировался) при загрузке программы.
Вариант 2
Вычислялись все простые числа до 2 000 000 000 и записывались в файл прямого доступа. Оставалось только проверить: находится ли данное число в файле?Запись от wer1 размещена 28.02.2019 в 08:02
-
нтч, брать значение из массива (или лучше битового набора – для 1 млрд чисел нужно ≈ 120 Мб памяти) – это очевидно самый быстрый способ. Для вычисления можно даже не заморачиваться с файлом, а использовать решето Эратосфена или решето Аткина, к примеру (правда, я их ещё не тестил их на скорость).
Но такой расход памяти не всегда оправдан. К примеру, если вам нужно лишь несколько значений из заранее неизвестного (очень широкого) диапазона. Либо если вы не знаете заранее, понадобятся ли вам эти значения – смысл держать выделенные мегабайты памяти?
Давайте в рамках этой статьи ограничимся разовым тестом числа на простоту без предварительных расчётов. Есть же всякие тесты Миллера и пр. Вопрос только в том, насколько они быстрые и не чересчур ли сложные?
А что касается решёт, то когда у меня дойдут до них руки, я протестирую их скорость и напишу об этом отдельную статью
p.s. По-хорошему, надо ещё разобраться со способами факторизации числа, нахождения списка делителей (кроме самых очевидных).Запись от Jin X размещена 28.02.2019 в 09:32
-
Запись от Croessmah размещена 02.03.2019 в 14:33
-
Быстрый алгоритмы поиска простых/всех делителей натурального числа (в т.ч. факторизация натурального числа)С++
Оптимизация через wheel factorization и расспралеливания расчета на 8 потоковЗапись от bedvit размещена 02.03.2019 в 17:22
-
Спасибо, сохранил несколько ссылок для просмотра. А алгоритм из шапки работает с той же скоростью (потому что, по сути, на том же принципе основан), что и мой последний (is_prime5)
Сообщение от Croessmah

Правда, на tio.run почему-то в 2 раза медленнее...
Значит, я сам того не зная, применил у себя этот wheel optimization
Сообщение от bedvit

Распараллеливание – это интересная идея, но полагаю, что числа должны быть достаточно большими + их должно быть несколько, иначе процесс запуска/остановки распараллеливания будет долгим.
Кстати, я уже сделал тест Миллера (детерминированный, а не вероятностный Миллера-Рабина). Работает довольно быстро (лично у меня на компе прирост начинается с отметки примерно в полмиллиона, на tio.run эта отметка ниже).
За решето ещё не брался
Запись от Jin X размещена 04.03.2019 в 18:07
-
Запись от Jin X размещена 04.03.2019 в 20:04




(цикл начинается с 13).


