Каков наилучший алгоритм проверки, является ли число простым? - PullRequest
144 голосов
/ 26 ноября 2009

Просто пример того, что я ищу: я мог бы представить каждое нечетное число с битом, например, для данного диапазона чисел (1, 10], начинается с 3:

1110

Следующий словарь можно сжать более правильно? Я мог бы определить кратные пять с некоторой работой, но числа, заканчивающиеся на 1, 3, 7 или 9, должны быть там в массиве битов. Надеюсь, это прояснит, чего я хочу.

Я ищу лучший алгоритм, чтобы проверить, простое ли число, то есть булева функция:

bool isprime(number);

Я хотел бы знать лучший алгоритм для реализации этой функциональности. Естественно, была бы структура данных, которую я мог бы запросить. I определяет наилучший алгоритм , который представляет собой алгоритм, который создает структуру данных с самым низким потреблением памяти для диапазона (1, N], где N - константа.

Ответы [ 27 ]

199 голосов
/ 26 ноября 2009

Самый быстрый алгоритм для общего простого тестирования - AKS . Статья в Википедии подробно описывает это и ссылается на оригинальную статью.

Если вы хотите найти большие числа, посмотрите на простые числа, которые имеют специальные формы, такие как простые числа Мерсенна .

Алгоритм, который я обычно реализую (легко понять и кодировать), выглядит следующим образом (в Python):

def isprime(n):
    """Returns True if n is prime."""
    if n == 2:
        return True
    if n == 3:
        return True
    if n % 2 == 0:
        return False
    if n % 3 == 0:
        return False

    i = 5
    w = 2

    while i * i <= n:
        if n % i == 0:
            return False

        i += w
        w = 6 - w

    return True

Это вариант классического O(sqrt(N)) алгоритма. Он использует тот факт, что простое число (кроме 2 и 3) имеет форму 6k - 1 или 6k + 1 и смотрит только на делители этой формы.

Иногда, если мне действительно нужна скорость и диапазон ограничен , я реализую тест псевдопростого числа на основе маленькой теоремы Ферма . Если мне действительно нужно больше скорости (то есть вообще избегать алгоритма O (sqrt (N))), я предварительно вычисляю ложные срабатывания (см. Carmichael numbers) и выполняю двоичный поиск. Это, безусловно, самый быстрый тест, который я когда-либо проводил, единственным недостатком является то, что диапазон ограничен.

77 голосов
/ 26 ноября 2009

Есть много способов сделать тест на простоту .

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

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

25 голосов
/ 26 ноября 2009

На мой взгляд, лучший способ - использовать то, что было раньше.

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

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

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

8 голосов
/ 17 мая 2017
bool isPrime(int n)
{
    // Corner cases
    if (n <= 1)  return false;
    if (n <= 3)  return true;

    // This is checked so that we can skip 
    // middle five numbers in below loop
    if (n%2 == 0 || n%3 == 0) return false;

    for (int i=5; i*i<=n; i=i+6)
        if (n%i == 0 || n%(i+2) == 0)
           return false;

    return true;
}
this is just c++ implementation of above  AKS algorithm

https://en.wikipedia.org/wiki/AKS_primality_test

6 голосов
/ 25 февраля 2018

В Python 3:

def is_prime(a):
    if a < 2:
        return False
    elif a!=2 and a % 2 == 0:
        return False
    else:
        return all (a % i for i in range(3, int(a**0.5)+1))

Пояснение: Простое число - это число, только делимое на себя и 1. Например: 2,3,5,7 ...

1) если a <2: </strong>, если «a» меньше 2, это не простое число.

2) elif a! = 2 и a% 2 == 0: если «a» делится на 2, то это определенно не простое число. Но если a = 2, мы не хотим оценивать это, поскольку это простое число. Отсюда условие а! = 2

3) вернуть все (a% i для i в диапазоне (3, int (a 0.5) +1)): ** Сначала посмотрите, что команда all () делает в python. Начиная с 3 мы делим «а» до его квадратного корня (а ** 0,5). Если «a» делится, вывод будет False. Почему квадратный корень? Допустим, а = 16. Квадратный корень из 16 = 4. Нам не нужно оценивать до 15. Нам нужно только проверить до 4, чтобы сказать, что это не простое число.

Дополнительно: Цикл для нахождения всех простых чисел в диапазоне.

for i in range(1,100):
    if is_prime(i):
        print("{} is a prime number".format(i))
6 голосов
/ 26 ноября 2009

Согласно википедии, Сито Эратосфена имеет сложность O(n * (log n) * (log log n)) и требует O(n) памяти - так что это довольно хорошее место для начала, если вы не тестируете особенно большие цифры.

4 голосов
/ 26 августа 2018

Можно использовать sympy .

import sympy

sympy.ntheory.primetest.isprime(33393939393929292929292911111111)

True

Из симпот док. Первым шагом является поиск тривиальных факторов, которые в случае их обнаружения позволяют быстро вернуться. Далее, если сито достаточно велико, используйте поиск сито на сите. Для небольших чисел выполняется ряд детерминированных тестов Миллера-Рабина с основаниями, о которых известно, что в их диапазоне нет контрпримеров. Наконец, если число больше 2 ^ 64, выполняется сильный тест BPSW. Хотя это вероятный первичный тест, и мы полагаем, что существуют контрпримеры, известных контрпримеров не существует

4 голосов
/ 23 ноября 2018

Я сравнил эффективность самых популярных предложений, чтобы определить, является ли число простым. Я использовал python 3.6 на ubuntu 17.10; Я проверил с номерами до 100.000 (вы можете проверить с большими числами, используя мой код ниже).

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

plot1

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

plot2

Вот функции, которые я использовал:

  1. этот ответ и этот ответ предложил конструкцию, использующую all():

    def is_prime_1(n):
        return n > 1 and all(n % i for i in range(2, int(math.sqrt(n)) + 1))
    
  2. Этот ответ использовал какой-то цикл while:

    def is_prime_2(n):
        if n <= 1:
            return False
        if n == 2:
            return True
        if n == 3:
            return True
        if n % 2 == 0:
            return False
        if n % 3 == 0:
            return False
    
        i = 5
        w = 2
        while i * i <= n:
            if n % i == 0:
                return False
            i += w
            w = 6 - w
    
        return True
    
  3. Этот ответ включал версию с for петлей:

    def is_prime_3(n):
        if n <= 1:
            return False
    
        if n % 2 == 0 and n > 2:
            return False
    
        for i in range(3, int(math.sqrt(n)) + 1, 2):
            if n % i == 0:
                return False
    
        return True
    
  4. И я смешал несколько идей из других ответов в новый:

    def is_prime_4(n):
        if n <= 1:          # negative numbers, 0 or 1
            return False
        if n <= 3:          # 2 and 3
            return True
        if n % 2 == 0 or n % 3 == 0:
            return False
    
        for i in range(5, int(math.sqrt(n)) + 1, 2):
            if n % i == 0:
                return False
    
        return True
    

Вот мой скрипт для сравнения вариантов:

import math
import pandas as pd
import seaborn as sns
import time
from matplotlib import pyplot as plt


def is_prime_1(n):
    ...
def is_prime_2(n):
    ...
def is_prime_3(n):
    ...
def is_prime_4(n):
    ...

default_func_list = (is_prime_1, is_prime_2, is_prime_3, is_prime_4)

def assert_equal_results(func_list=default_func_list, n):
    for i in range(-2, n):
        r_list = [f(i) for f in func_list]
        if not all(r == r_list[0] for r in r_list):
            print(i, r_list)
            raise ValueError
    print('all functions return the same results for integers up to {}'.format(n))

def compare_functions(func_list=default_func_list, n):
    result_list = []
    n_measurements = 3

    for f in func_list:
        for i in range(1, n + 1):
            ret_list = []
            t_sum = 0
            for _ in range(n_measurements):
                t_start = time.perf_counter()
                is_prime = f(i)
                t_end = time.perf_counter()

                ret_list.append(is_prime)
                t_sum += (t_end - t_start)

            is_prime = ret_list[0]
            assert all(ret == is_prime for ret in ret_list)
            result_list.append((f.__name__, i, is_prime, t_sum / n_measurements))

    df = pd.DataFrame(
        data=result_list,
        columns=['f', 'number', 'is_prime', 't_seconds'])
    df['t_micro_seconds'] = df['t_seconds'].map(lambda x: round(x * 10**6, 2))
    print('df.shape:', df.shape)

    print()
    print('', '-' * 41)
    print('| {:11s} | {:11s} | {:11s} |'.format(
        'is_prime', 'count', 'percent'))
    df_sub1 = df[df['f'] == 'is_prime_1']
    print('| {:11s} | {:11,d} | {:9.1f} % |'.format(
        'all', df_sub1.shape[0], 100))
    for (is_prime, count) in df_sub1['is_prime'].value_counts().iteritems():
        print('| {:11s} | {:11,d} | {:9.1f} % |'.format(
            str(is_prime), count, count * 100 / df_sub1.shape[0]))
    print('', '-' * 41)

    print()
    print('', '-' * 69)
    print('| {:11s} | {:11s} | {:11s} | {:11s} | {:11s} |'.format(
        'f', 'is_prime', 't min (us)', 't mean (us)', 't max (us)'))
    for f, df_sub1 in df.groupby(['f', ]):
        col = df_sub1['t_micro_seconds']
        print('|{0}|{0}|{0}|{0}|{0}|'.format('-' * 13))
        print('| {:11s} | {:11s} | {:11.2f} | {:11.2f} | {:11.2f} |'.format(
            f, 'all', col.min(), col.mean(), col.max()))
        for is_prime, df_sub2 in df_sub1.groupby(['is_prime', ]):
            col = df_sub2['t_micro_seconds']
            print('| {:11s} | {:11s} | {:11.2f} | {:11.2f} | {:11.2f} |'.format(
                f, str(is_prime), col.min(), col.mean(), col.max()))
    print('', '-' * 69)

    return df

Запуск функции compare_functions(n=10**5) (числа до 100.000) Я получаю этот вывод:

df.shape: (400000, 5)

 -----------------------------------------
| is_prime    | count       | percent     |
| all         |     100,000 |     100.0 % |
| False       |      90,408 |      90.4 % |
| True        |       9,592 |       9.6 % |
 -----------------------------------------

 ---------------------------------------------------------------------
| f           | is_prime    | t min (us)  | t mean (us) | t max (us)  |
|-------------|-------------|-------------|-------------|-------------|
| is_prime_1  | all         |        0.57 |        2.50 |      154.35 |
| is_prime_1  | False       |        0.57 |        1.52 |      154.35 |
| is_prime_1  | True        |        0.89 |       11.66 |       55.54 |
|-------------|-------------|-------------|-------------|-------------|
| is_prime_2  | all         |        0.24 |        1.14 |      304.82 |
| is_prime_2  | False       |        0.24 |        0.56 |      304.82 |
| is_prime_2  | True        |        0.25 |        6.67 |       48.49 |
|-------------|-------------|-------------|-------------|-------------|
| is_prime_3  | all         |        0.20 |        0.95 |       50.99 |
| is_prime_3  | False       |        0.20 |        0.60 |       40.62 |
| is_prime_3  | True        |        0.58 |        4.22 |       50.99 |
|-------------|-------------|-------------|-------------|-------------|
| is_prime_4  | all         |        0.20 |        0.89 |       20.09 |
| is_prime_4  | False       |        0.21 |        0.53 |       14.63 |
| is_prime_4  | True        |        0.20 |        4.27 |       20.09 |
 ---------------------------------------------------------------------

Затем, запустив функцию compare_functions(n=10**6) (числа до 1.000.000), я получаю этот вывод:

df.shape: (4000000, 5)

 -----------------------------------------
| is_prime    | count       | percent     |
| all         |   1,000,000 |     100.0 % |
| False       |     921,502 |      92.2 % |
| True        |      78,498 |       7.8 % |
 -----------------------------------------

 ---------------------------------------------------------------------
| f           | is_prime    | t min (us)  | t mean (us) | t max (us)  |
|-------------|-------------|-------------|-------------|-------------|
| is_prime_1  | all         |        0.51 |        5.39 |     1414.87 |
| is_prime_1  | False       |        0.51 |        2.19 |      413.42 |
| is_prime_1  | True        |        0.87 |       42.98 |     1414.87 |
|-------------|-------------|-------------|-------------|-------------|
| is_prime_2  | all         |        0.24 |        2.65 |      612.69 |
| is_prime_2  | False       |        0.24 |        0.89 |      322.81 |
| is_prime_2  | True        |        0.24 |       23.27 |      612.69 |
|-------------|-------------|-------------|-------------|-------------|
| is_prime_3  | all         |        0.20 |        1.93 |       67.40 |
| is_prime_3  | False       |        0.20 |        0.82 |       61.39 |
| is_prime_3  | True        |        0.59 |       14.97 |       67.40 |
|-------------|-------------|-------------|-------------|-------------|
| is_prime_4  | all         |        0.18 |        1.88 |      332.13 |
| is_prime_4  | False       |        0.20 |        0.74 |      311.94 |
| is_prime_4  | True        |        0.18 |       15.23 |      332.13 |
 ---------------------------------------------------------------------

Я использовал следующий скрипт для отображения результатов:

def plot_1(func_list=default_func_list, n):
    df_orig = compare_functions(func_list=func_list, n=n)
    df_filtered = df_orig[df_orig['t_micro_seconds'] <= 20]
    sns.lmplot(
        data=df_filtered, x='number', y='t_micro_seconds',
        col='f',
        # row='is_prime',
        markers='.',
        ci=None)

    plt.ticklabel_format(style='sci', axis='x', scilimits=(3, 3))
    plt.show()
3 голосов
/ 09 февраля 2019

Для больших чисел вы не можете просто наивно проверить, делится ли число кандидатов N на одно из чисел, меньших, чем sqrt (N). Доступны гораздо более масштабируемые тесты, такие как тест первичности Миллера-Рабина . Ниже у вас есть реализация в Python:

def is_prime(x):
    """Fast implementation fo Miller-Rabin primality test, guaranteed to be correct."""
    import math
    def get_sd(x):
        """Returns (s: int, d: int) for which x = d*2^s """
        if not x: return 0, 0
        s = 0
        while 1:
            if x % 2 == 0:
                x /= 2
                s += 1
            else:
                return s, x
    if x <= 2:
        return x == 2
    # x - 1 = d*2^s
    s, d = get_sd(x - 1)
    if not s:
        return False  # divisible by 2!
    log2x = int(math.log(x) / math.log(2)) + 1
    # As long as Riemann hypothesis holds true, it is impossible
    # that all the numbers below this threshold are strong liars.
    # Hence the number is guaranteed to be a prime if no contradiction is found.
    threshold = min(x, 2*log2x*log2x+1)
    for a in range(2, threshold):
        # From Fermat's little theorem if x is a prime then a^(x-1) % x == 1
        # Hence the below must hold true if x is indeed a prime:
        if pow(a, d, x) != 1:
            for r in range(0, s):
                if -pow(a, d*2**r, x) % x == 1:
                    break
            else:
                # Contradicts Fermat's little theorem, hence not a prime.
                return False
    # No contradiction found, hence x must be a prime.
    return True

Вы можете использовать его, чтобы найти огромные простые числа:

x = 10000000000000000000000000000000000000000000000000000000000000000000000000000
for e in range(1000):
    if is_prime(x + e):
        print('%d is a prime!' % (x + e))
        break

# 10000000000000000000000000000000000000000000000000000000000000000000000000133 is a prime!

Если вы тестируете случайные целые числа, возможно, вы хотите сначала проверить, делится ли число кандидатов на любое из простых чисел, меньших, скажем, 1000, прежде чем позвонить Миллеру-Рабину. Это поможет вам отфильтровать очевидные не простые числа, такие как 10444344345.

2 голосов
/ 27 июня 2017

Python 3:

def is_prime(a):
    return a > 1 and all(a % i for i in range(2, int(a**0.5) + 1))
...