Эффективное хранение простых чисел

Для библиотеки мне нужно сохранить первые числа простых чисел до предела L. Эта коллекция должна иметь время поиска O (1) (чтобы проверить, является ли число простым или нет), и это должно быть легко, учитывая число, чтобы найти следующее простое число (при условии, что оно меньше L).

Учитывая, что L фиксировано, сито Eratostene для генерации списка отлично. Прямо сейчас я использую упакованный булевский массив для хранения списка, который содержит только записи для нечетных чисел между 3 и L (включительно). Это занимает (L-2)/2 бит памяти. Я хотел бы иметь возможность статически увеличивать L, не используя больше памяти.

Есть ли структура данных, использующая меньше памяти со схожими свойствами? Или, по крайней мере, с постоянным временем поиска? (нечетные числа затем могут быть перечислены до тех пор, пока мы не получим простое)

(язык, на котором я написал это, Factor, но этот вопрос будет таким же на любом языке, который имеет встроенный или простой программируемые упакованные битовые массивы)

Ответ 1

Вы можете явно проверить большее количество простых чисел, чтобы удалить избыточность.

В данный момент вы делаете это только для двоих, проверяя на делимость на два явно, а затем сохраняя только для нечетных чисел, являются ли они основными.

Для 2 и 3 вы получаете остатки от 0 до 5, из которых только 1 и 5 не делятся на два или три и могут приводить к простому числу, поэтому вы сокращаетесь до 1/3.

Для 2, 3 и 5 вы получаете 8 номеров из 30, что удобно хранить в байте.

Это объясняется более подробно здесь.

Ответ 2

Альтернатива упакованным растровым изображениям и колесам - но одинаково эффективная в определенных контекстах - хранит различия между последовательными штрихами. Если вы оставите номер 2, как обычно, все различия будут четными. Сохраняя разницу /2, вы можете получить до 2 ^ 40ish областей (как раз перед 1999066711391) с использованием байтовых переменных.

Простые числа до 2 ^ 32 требуют всего 194 Мбайт, по сравнению с 256 Мбайт для только растрового изображения с коэффициентом. Итерация по дельта-хранимым числам намного быстрее, чем для колесного хранилища, которое включает в себя колесо modulo-2, известное как растровое изображение только для коэффициентов.

Для диапазонов от 1999066711391 и выше требуется больший размер ячейки или хранилище переменной длины. Последнее может быть чрезвычайно эффективным, даже если используются очень простые схемы (например, продолжать добавлять до тех пор, пока не будет добавлен байт < 255, как в LZ4 - сжатие стилей), из-за чрезвычайно низкой частоты зазоров дольше 510/2.

Для эффективности лучше всего разделить диапазон на разделы (страницы) и управлять ими в стиле B-Tree.

Энтропийное кодирование различий (Huffmann или арифметическое кодирование) сокращает требования к постоянному хранению чуть меньше половины, что близко к теоретическому оптимуму и лучше, чем списки или диски, сжатые с использованием лучших доступных упаковщиков.

Если данные хранятся несжатыми, то они по-прежнему гораздо более компактны, чем файлы двоичных или текстовых номеров, на порядок или больше. С индексом стиля B-Tree на месте легко просто отображать разделы в память по мере необходимости и прокручивать их с невероятной скоростью.

Ответ 3

В данный момент вы обрабатываете 2 как частный случай, а затем имеете массив, в котором каждое нечетное число сопоставляется с элементом в массиве (при этом некоторые нечетные числа являются первичными). Вы могли бы улучшить это, рассматривая 2 и 3 как особые случаи, признающие, что остальные простые числа имеют вид 6n + 1 или 6n-1 (то есть для всех простых чисел p, где p > 3, p mod 6 = 1 или 5). Это может быть дополнительно обобщено - см. Wikipedia. Для всех простых чисел p > 5, p mod 30 = 1, 7, 11, 13, 17, 19, 23 или 29. Вы можете продолжать использовать это и уменьшать объем памяти, необходимый за счет времени обработки (хотя это все равно будет O (1), только медленнее O (1)).

Ответ 4

Возможно, структура данных trie, которая содержит только простые числа, - это то, что вы ищете. Вместо использования символов в качестве индексов вы можете использовать целые цифры. Реализация этого Judy-Array s.

Кроме того, они не соответствуют вашему требованию O (1), они чрезвычайно эффективны для работы с подобными ключами (как и большинство частей) и довольно быстро выглядят с помощью O (m) (m = key- длина) максимум.

Если вы посмотрите на премьер в предварительно сгенерированном дереве, вы можете пройти дерево до тех пор, пока не найдете его, или вы уже находитесь на node, который находится рядом с предыдущим и следующим простым.

Ответ 5

Учитывая, что память настолько дешевая, я не думаю, что вы можете сделать гораздо лучше с точки зрения скорости, чем ваша существующая схема.

Если есть лучшее решение, я бы предположил, что он воспользуется Prime Number Theor, который показывает, что по мере того, как L получает больший, предел

π (L)/(L/ln (L)) приближается к 1.

Возможно, лучшее решение будет иметь адаптивное решение упаковки в структуре данных вроде список пропусков.

Ответ 6

Как насчет какой-либо хеш-таблицы?

Вам понадобится очень хорошая хеш-функция (что-то вроде n mod p, где p не кратно ни одному из нижних простых чисел q - выберите q достаточно высоко, чтобы свести к минимуму количество столкновений).

Ответ 7

Как насчет дерева интервалов? http://www.geeksforgeeks.org/interval-tree/

Это может быть не O (1), но это очень быстро. Например, O (log (p (n))), где p (n) - число простых чисел до числа n. Таким образом, вы будете иметь необходимую память, будет пропорционально количеству простых чисел, что значительно сократит стоимость памяти.

Например, предположим, что вы найдете штрих в say p1, а затем следующий в p2, Вставьте интервал (p1, p2) и т.д., И когда вы запустите поиск любого числа в этом диапазоне, он вернет этот интервал, и вы можете вернуть p2, который будет ответом в вашем случае.

Ответ 8

Если вы можете выяснить, какие из них Mersenne или другие легко представленные простые числа, вы можете сохранить несколько бит используя это представление с флагом для применимых чисел.

Кроме того, как насчет сохранения чисел в качестве отличия от предыдущего номера? Тогда размер не должен расти так же быстро (но поиск будет медленным). Объединяясь с вышеприведенным подходом, вы можете сохранить простые числа Мерсенна и отличие от последнего простого Мерсена.