Учитывая Prime Number N, вычислите следующий премьер?

Сотрудник просто сказал мне, что коллекция С# Dictionary изменяется на простые числа по тайным причинам, связанным с хешированием. И мой ближайший вопрос заключался в том, "как он узнает, что представляет собой следующее премьер-листво: рассказывают ли они гигантскую таблицу или вычисляют" на лету ", что страшная недетерминированная продолжительность выполнения вставки, вызывающая изменение размера"

Итак, мой вопрос, учитывая, что N, которое является простым числом, является наиболее эффективным способом вычисления следующего простого числа?

Ответ 1

пробелы между последовательными простыми числами, как известно, довольно малы, причем первый пробел более 100 встречается для простого числа 370261. Это означает что даже простая грубая сила будет достаточно быстрой в большинстве случаев, принимая в среднем O (ln (p) * sqrt (p)).

При p = 10000 операции O (921). Принимая во внимание, что мы будем выполнять эту однократную вставку O (ln (p)) (грубо говоря), это хорошо связано с ограничениями большинства проблем, выполняющих порядок миллисекунды на большинстве современных аппаратных средств.

Ответ 2

Примерно год назад я работал над этой областью для libС++ при реализации неупорядоченные (хэш-контейнеры) для С++ 11. Я думал, что поделюсь мой опыт здесь. Этот опыт поддерживает принятый ответ marcog для разумное определение "грубой силы".

Это означает, что даже простая грубая сила будет достаточно быстрой в большинстве обстоятельства, беря O (ln (p) * sqrt (p)) в среднем.

Я разработал несколько реализаций size_t next_prime(size_t n), где spec для этой функции:

Возвращает: наименьшее простое, которое больше или равно n.

Каждая реализация next_prime сопровождается вспомогательной функцией is_prime. is_prime следует рассматривать как частную реализацию; не предназначен для прямого вызова клиентом. Каждая из этих реализаций была, конечно, проверена на правильность, но также протестированы со следующим тестом производительности:

int main()
{
    typedef std::chrono::high_resolution_clock Clock;
    typedef std::chrono::duration<double, std::milli> ms;
    Clock::time_point t0 = Clock::now();

    std::size_t n = 100000000;
    std::size_t e = 100000;
    for (std::size_t i = 0; i < e; ++i)
        n = next_prime(n+1);

    Clock::time_point t1 = Clock::now();
    std::cout << e/ms(t1-t0).count() << " primes/millisecond\n";
    return n;
}

Я должен подчеркнуть, что это тест производительности и не отражает типичные использование, которое будет выглядеть больше:

// Overflow checking not shown for clarity purposes
n = next_prime(2*n + 1);

Все тесты производительности были скомпилированы с помощью:

clang++ -stdlib=libc++ -O3 main.cpp

Реализация 1

Существует семь реализаций. Цель отображения первого реализация должна продемонстрировать, что если вы не сможете остановить тестирование кандидата prime x для факторов в sqrt(x), то вы не смогли даже достичь которые могут быть классифицированы как грубая сила. Эта реализация жестоко медленный.

bool
is_prime(std::size_t x)
{
    if (x < 2)
        return false;
    for (std::size_t i = 2; i < x; ++i)
    {
        if (x % i == 0)
            return false;
    }
    return true;
}

std::size_t
next_prime(std::size_t x)
{
    for (; !is_prime(x); ++x)
        ;
    return x;
}

Для этой реализации мне пришлось установить только e на 100 вместо 100000, только для получить разумное время работы:

0.0015282 primes/millisecond

Реализация 2

Эта реализация является самой медленной из реализаций грубой силы и только отличие от реализации 1 заключается в том, что он прекращает тестирование на грубость когда коэффициент превосходит sqrt(x).

bool
is_prime(std::size_t x)
{
    if (x < 2)
        return false;
    for (std::size_t i = 2; true; ++i)
    {
        std::size_t q = x / i;
        if (q < i)
            return true;
        if (x % i == 0)
            return false;
    }
    return true;
}

std::size_t
next_prime(std::size_t x)
{
    for (; !is_prime(x); ++x)
        ;
    return x;
}

Обратите внимание, что sqrt(x) не вычисляется напрямую, а выводится q < i. Эта ускоряет работу в тысячах:

5.98576 primes/millisecond

и проверяет предсказание марка:

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

Реализация 3

Можно почти удвоить скорость (по крайней мере, на аппаратном обеспечении, которое я использую) избегая использования оператора %:

bool
is_prime(std::size_t x)
{
    if (x < 2)
        return false;
    for (std::size_t i = 2; true; ++i)
    {
        std::size_t q = x / i;
        if (q < i)
            return true;
        if (x == q * i)
            return false;
    }
    return true;
}

std::size_t
next_prime(std::size_t x)
{
    for (; !is_prime(x); ++x)
        ;
    return x;
}

11.0512 primes/millisecond

Реализация 4

До сих пор я даже не использовал общее знание, что 2 является единственным четным простом. Эта реализация включает в себя эти знания, почти удваивая скорость еще раз:

bool
is_prime(std::size_t x)
{
    for (std::size_t i = 3; true; i += 2)
    {
        std::size_t q = x / i;
        if (q < i)
            return true;
        if (x == q * i)
            return false;
    }
    return true;
}

std::size_t
next_prime(std::size_t x)
{
    if (x <= 2)
        return 2;
    if (!(x & 1))
        ++x;
    for (; !is_prime(x); x += 2)
        ;
    return x;
}

21.9846 primes/millisecond

Реализация 4 - это то, что большинство людей имеет в виду, когда думают "грубая сила".

Реализация 5

Используя следующую формулу, вы можете легко выбрать все числа, которые не делится ни на 2, ни на 3:

6 * k + {1, 5}

где k >= 1. Следующая реализация использует эту формулу, но реализована с симпатичным xor трюком:

bool
is_prime(std::size_t x)
{
    std::size_t o = 4;
    for (std::size_t i = 5; true; i += o)
    {
        std::size_t q = x / i;
        if (q < i)
            return true;
        if (x == q * i)
            return false;
        o ^= 6;
    }
    return true;
}

std::size_t
next_prime(std::size_t x)
{
    switch (x)
    {
    case 0:
    case 1:
    case 2:
        return 2;
    case 3:
        return 3;
    case 4:
    case 5:
        return 5;
    }
    std::size_t k = x / 6;
    std::size_t i = x - 6 * k;
    std::size_t o = i < 2 ? 1 : 5;
    x = 6 * k + o;
    for (i = (3 + o) / 2; !is_prime(x); x += i)
        i ^= 6;
    return x;
}

Это фактически означает, что алгоритм должен проверять только 1/3 целые числа для примитивности вместо 1/2 числа и тест производительности показывает ожидаемую скорость почти на 50%:

32.6167 primes/millisecond

Реализация 6

Эта реализация является логическим продолжением реализации 5: она использует следующую формулу для вычисления всех чисел, которые не делятся на 2, 3 и 5:

30 * k + {1, 7, 11, 13, 17, 19, 23, 29}

Он также разворачивает внутренний цикл внутри is_prime и создает список "small primes ", который полезен для обработки чисел менее 30.

static const std::size_t small_primes[] =
{
    2,
    3,
    5,
    7,
    11,
    13,
    17,
    19,
    23,
    29
};

static const std::size_t indices[] =
{
    1,
    7,
    11,
    13,
    17,
    19,
    23,
    29
};

bool
is_prime(std::size_t x)
{
    const size_t N = sizeof(small_primes) / sizeof(small_primes[0]);
    for (std::size_t i = 3; i < N; ++i)
    {
        const std::size_t p = small_primes[i];
        const std::size_t q = x / p;
        if (q < p)
            return true;
        if (x == q * p)
            return false;
    }
    for (std::size_t i = 31; true;)
    {
        std::size_t q = x / i;
        if (q < i)
            return true;
        if (x == q * i)
            return false;
        i += 6;

        q = x / i;
        if (q < i)
            return true;
        if (x == q * i)
            return false;
        i += 4;

        q = x / i;
        if (q < i)
            return true;
        if (x == q * i)
            return false;
        i += 2;

        q = x / i;
        if (q < i)
            return true;
        if (x == q * i)
            return false;
        i += 4;

        q = x / i;
        if (q < i)
            return true;
        if (x == q * i)
            return false;
        i += 2;

        q = x / i;
        if (q < i)
            return true;
        if (x == q * i)
            return false;
        i += 4;

        q = x / i;
        if (q < i)
            return true;
        if (x == q * i)
            return false;
        i += 6;

        q = x / i;
        if (q < i)
            return true;
        if (x == q * i)
            return false;
        i += 2;
    }
    return true;
}

std::size_t
next_prime(std::size_t n)
{
    const size_t L = 30;
    const size_t N = sizeof(small_primes) / sizeof(small_primes[0]);
    // If n is small enough, search in small_primes
    if (n <= small_primes[N-1])
        return *std::lower_bound(small_primes, small_primes + N, n);
    // Else n > largest small_primes
    // Start searching list of potential primes: L * k0 + indices[in]
    const size_t M = sizeof(indices) / sizeof(indices[0]);
    // Select first potential prime >= n
    //   Known a-priori n >= L
    size_t k0 = n / L;
    size_t in = std::lower_bound(indices, indices + M, n - k0 * L) - indices;
    n = L * k0 + indices[in];
    while (!is_prime(n))
    {
        if (++in == M)
        {
            ++k0;
            in = 0;
        }
        n = L * k0 + indices[in];
    }
    return n;
}

Это, возможно, выходит за рамки "грубой силы", и это хорошо для повышения скорость еще 27,5%:

41.6026 primes/millisecond

Реализация 7

Практически играть в эту игру для еще одной итерации, разрабатывая формула для чисел, не делящихся на 2, 3, 5 и 7:

210 * k + {1, 11, ...},

Исходный код здесь не показан, но очень похож на реализацию 6. Это реализация, которую я решил использовать для неупорядоченных контейнеров libС++, и этот исходный код является открытым исходным кодом (найден по ссылке).

Эта окончательная итерация хороша для еще одного повышения скорости на 14,6% для:

47.685 primes/millisecond

Использование этого алгоритма гарантирует, что клиенты libС++ хэш-таблиц могут выбрать любое основное их решение наиболее выгодно для их ситуации, а производительность для этого приложения вполне приемлемо.

Ответ 3

На всякий случай кому-то интересно:

Используя отражатель, я определил, что .Net использует статический класс, который содержит жесткий кодированный список из ~ 72 простых чисел, начиная с 7199369, который сканирует наименьшее простое, которое по меньшей мере вдвое превышает текущий размер, и для размеров, больших, чем это он вычисляет следующее простое с помощью пробного деления всех нечетных чисел вплоть до sqrt потенциального числа. Этот класс является неизменным и потокобезопасным (т.е. Большие простые числа не сохраняются для использования в будущем).

Ответ 4

Всего несколько экспериментов с расстоянием между пробами.


Это дополнение для визуализации других ответов.

Я получил простые числа от 100 000 (= 1 299 709) до 200 000-го (= 2750,159)

Некоторые данные:

Maximum interprime distance = 148

Mean interprime distance = 15  

График частоты интерференционных расстояний:

alt text

Interprime Distance vs Prime Number

alt text

Просто, чтобы увидеть его "случайным". Однако...

Ответ 5

Хорошим трюком является использование частичного сита. Например, каково следующее простое число, которое следует за номером N = 2534536543556?

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

mod(2534536543556,[3 5 7 11 13 17 19 23 29 31 37])
ans =
     2     1     3     6     4     1     3     4    22    16    25

Мы знаем, что следующее простое число после N должно быть нечетным числом, и мы можем сразу отказаться от всех нечетных кратных этого списка малых простых чисел. Эти модули позволяют просеять кратные эти маленькие простые числа. Если бы мы использовали небольшие числа до 200, мы можем использовать эту схему, чтобы немедленно отбросить большинство потенциальных простых чисел, больших N, за исключением небольшого списка.

Более явно, если мы ищем простое число за пределами 2534536543556, оно не может делиться на 2, поэтому нам нужно рассмотреть только нечетные числа за пределами этого значения. Вышеприведенные модули показывают, что 2534536543556 соответствует 2 mod 3, поэтому 2534536543556 + 1 соответствует 0 mod 3, как и должно быть 2534536543556 + 7, 2534536543556 + 13 и т.д. Эффективно мы можем вычесть многие цифры без какой-либо необходимости чтобы проверить их на прочность и без каких-либо пробных подразделений.

Аналогично, тот факт, что

mod(2534536543556,7) = 3

говорит нам, что 2534536543556 + 4 сравнимо с 0 mod 7. Конечно, это число четное, поэтому мы можем его игнорировать. Но 2534536543556 + 11 - нечетное число, которое делится на 7, как и 2534536543556 + 25 и т.д. Опять же, мы можем исключить эти числа как ясно композитные (потому что они делятся на 7) и поэтому не просто.

Используя только небольшой список простых чисел до 37, мы можем исключить большинство чисел, которые сразу же следуют за нашей начальной точкой 2534536543556, только за исключением нескольких:

{2534536543573 , 2534536543579 , 2534536543597}

Из этих чисел они простые?

2534536543573 = 1430239 * 1772107
2534536543579 = 99833 * 25387763

Я сделал попытку обеспечить простые факторизации первых двух чисел в списке. Смотрите, что они сложны, но основные факторы велики. Конечно, это имеет смысл, поскольку мы уже обеспечили, что число, которое осталось, может иметь небольшие первичные факторы. Третий в нашем кратковременном списке (2534536543597) на самом деле является самым первым простым числом за пределами N. Схема просеивания, о которой я описал, будет иметь тенденцию приводить к числу простых или состоящих из обычно больших простых факторов. Поэтому нам нужно было на самом деле применить явный тест для примитивности только к нескольким номерам, прежде чем найти следующее простое.

Аналогичная схема быстро дает следующий предел, превышающий N = 1000000000000000000000000000, как 1000000000000000000000000103.

Ответ 6

Нет функции f (n) для вычисления следующего простого числа. Вместо этого число должно быть проверено на простоту.

Также очень полезно при поиске n-го простого числа уже знать все простые числа от 1-го до (n-1) -го, потому что это единственные числа, которые необходимо тестировать как факторы.

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

Ответ 7

Как уже отмечалось, средство поиска следующего простого числа с учетом текущего шага еще не найдено. Поэтому большинство алгоритмов больше ориентированы на использование быстрого средства проверки primality, так как вам нужно проверить n/2 чисел между вашим известным прайм и следующий.

В зависимости от приложения вы также можете избежать просто жесткого кодирования справочной таблицы, как указано Paul Wheeler.

Ответ 8

Для чистой новизны всегда существует такой подход:

#!/usr/bin/perl
for $p ( 2 .. 200  ) {
      next if (1x$p) =~ /^(11+)\1+$/;
      for ($n=1x(1+$p); $n =~ /^(11+)\1+$/; $n.=1) { }
      printf "next prime after %d is %d\n", $p, length($n);
}

который, конечно, производит

next prime after 2 is 3
next prime after 3 is 5
next prime after 5 is 7
next prime after 7 is 11
next prime after 11 is 13
next prime after 13 is 17
next prime after 17 is 19
next prime after 19 is 23
next prime after 23 is 29
next prime after 29 is 31
next prime after 31 is 37
next prime after 37 is 41
next prime after 41 is 43
next prime after 43 is 47
next prime after 47 is 53
next prime after 53 is 59
next prime after 59 is 61
next prime after 61 is 67
next prime after 67 is 71
next prime after 71 is 73
next prime after 73 is 79
next prime after 79 is 83
next prime after 83 is 89
next prime after 89 is 97
next prime after 97 is 101
next prime after 101 is 103
next prime after 103 is 107
next prime after 107 is 109
next prime after 109 is 113
next prime after 113 is 127
next prime after 127 is 131
next prime after 131 is 137
next prime after 137 is 139
next prime after 139 is 149
next prime after 149 is 151
next prime after 151 is 157
next prime after 157 is 163
next prime after 163 is 167
next prime after 167 is 173
next prime after 173 is 179
next prime after 179 is 181
next prime after 181 is 191
next prime after 191 is 193
next prime after 193 is 197
next prime after 197 is 199
next prime after 199 is 211

Все развлечения и игры в стороне, хорошо известно, что оптимальный размер таблицы хэш строго доказуемо простое число формы 4N−1. Так что просто найти следующий премьер недостаточно. Вы также должны выполнить другую проверку.

Ответ 9

Насколько я помню, он использует простое число рядом с двойным текущим размером. Он не вычисляет, что простое число - там таблица с предварительно загруженными номерами до некоторой большой ценности (не совсем, примерно около 10 000 000). Когда это число достигнуто, он использует некоторый наивный алгоритм для получения следующего числа (например, curNum = curNum + 1) и проверяет его с помощью некоторых, если эти методы: http://en.wikipedia.org/wiki/Prime_number#Verifying_primality